diff --git a/Cargo.lock b/Cargo.lock index a6600c07..17d77d11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -52,57 +52,58 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", @@ -111,15 +112,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" dependencies = [ "addr2line", "cc", @@ -130,32 +131,44 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bitflags" -version = "1.3.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" [[package]] name = "cfg-if" @@ -165,23 +178,23 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets", + "windows-targets 0.52.5", ] [[package]] name = "clap" -version = "4.4.8" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", "clap_derive", @@ -189,9 +202,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.8" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", @@ -201,9 +214,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ "heck", "proc-macro2", @@ -213,50 +226,90 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] [[package]] name = "dns_rust" version = "0.1.0" dependencies = [ "async-trait", + "base64", "bytes", "chrono", "clap", "futures-util", "hex", + "hmac", "lru", - "rand", + "rand 0.8.5", + "rust-crypto", + "sha2", "thiserror", "tokio", "tokio-stream", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", @@ -265,15 +318,15 @@ dependencies = [ [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-macro", @@ -283,28 +336,44 @@ dependencies = [ "slab", ] +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] name = "gimli" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", @@ -312,15 +381,15 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -328,11 +397,20 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "iana-time-zone" -version = "0.1.58" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -351,26 +429,32 @@ dependencies = [ "cc", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "js-sys" -version = "0.3.65" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] [[package]] name = "libc" -version = "0.2.150" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -378,9 +462,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "lru" @@ -393,35 +477,35 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", - "wasi", - "windows-sys", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", ] [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -438,24 +522,24 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -463,22 +547,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.5", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -494,22 +578,45 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.76" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + [[package]] name = "rand" version = "0.8.5" @@ -518,7 +625,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -528,9 +635,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", ] +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.6.4" @@ -540,20 +662,48 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ "bitflags", ] +[[package]] +name = "rust-crypto" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" +dependencies = [ + "gcc", + "libc", + "rand 0.3.23", + "rustc-serialize", + "time", +] + [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-serialize" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "fe834bc780604f4674073badbad26d7219cadfb4a2275802db12cbae17498401" [[package]] name = "scopeguard" @@ -561,11 +711,22 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -581,31 +742,37 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "2.0.48" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -614,29 +781,40 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + [[package]] name = "tokio" -version = "1.35.1" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -648,14 +826,14 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", @@ -673,6 +851,12 @@ dependencies = [ "tokio", ] +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -681,9 +865,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "version_check" @@ -691,6 +875,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -699,9 +889,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -709,9 +899,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", @@ -724,9 +914,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -734,9 +924,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", @@ -747,17 +937,39 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.88" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "winapi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.51.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.5", ] [[package]] @@ -766,7 +978,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", ] [[package]] @@ -775,13 +996,29 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -790,56 +1027,104 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index ded60c37..05e94d48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,11 @@ tokio-stream = "0.1" thiserror = "1.0.20" futures-util = "0.3.28" async-trait = "0.1.77" +sha2 = "0.10.2" +hmac = "0.12.1" +rust-crypto = "0.2" +base64 = "0.22.1" + lru = "0.12.3" [lib] diff --git a/src/async_resolver.rs b/src/async_resolver.rs index 01a93f52..12e66fd7 100644 --- a/src/async_resolver.rs +++ b/src/async_resolver.rs @@ -19,10 +19,13 @@ use crate::message::rcode::Rcode; use crate::message::rdata::Rdata; use crate::message::resource_record::ResourceRecord; use crate::message::rrtype::Rrtype; +use crate::tsig; use crate::message::{self, DnsMessage}; use crate::resolver_cache::ResolverCache; use std::net::IpAddr; +use std::time::SystemTime; use std::sync::{Arc, Mutex}; +use std::vec; /// Asynchronous resolver for DNS queries. /// @@ -105,10 +108,13 @@ impl AsyncResolver { let response = self .inner_lookup(domain_name_struct, Rrtype::A, rclass.into()) .await; - + return self .check_error_from_msg(response) .and_then(|lookup_response| { + if lookup_response.to_dns_msg().get_header().get_tc() { + self.config.set_protocol(ConnectionProtocol::TCP); + } let rrs_iter = lookup_response.to_vec_of_rr().into_iter(); let ip_addresses: Result, _> = rrs_iter .map(|rr| AsyncResolver::from_rr_to_ip(rr)) @@ -168,6 +174,11 @@ impl AsyncResolver { Rclass::from(rclass), ) .await; + + + if self.config.get_tsig(){ + self.verify_tsig(response.clone()); + } return self.check_error_from_msg(response); } @@ -316,13 +327,13 @@ impl AsyncResolver { /// answer section, it is always preferred. fn store_data_cache(&self, response: DnsMessage) { let truncated = response.get_header().get_tc(); - let rcode = response.get_header().get_rcode(); { let mut cache = self.cache.lock().unwrap(); cache.timeout(); if !truncated { cache.add(response.clone()); } + } } @@ -417,6 +428,44 @@ impl AsyncResolver { _ => Err(ClientError::ResponseError(rcode.into()))?, } } + + /// Verifies tsig + pub fn verify_tsig(&self, + response: Result) + -> Result { + let lookup_response = match response { + Ok(val) => Ok(val), + Err(_) => Err(ClientError::TemporaryError("no DNS message found")), + }; + + let dns_response = lookup_response.unwrap().to_dns_msg(); + + let key_bytes = self.config.get_key(); + let shared_key_name = self.config.get_key_name(); + let time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); + let algorithm = String::from(self.config.get_algorithm()); + let alg_list = vec![(algorithm, true)]; + let mac = dns_response.get_mac(); + + let (_val, rcode) = tsig::process_tsig( + &dns_response, + &key_bytes, + shared_key_name.clone().unwrap(), + time, + alg_list, + mac, + ); + + match rcode { + Rcode::NOERROR => Ok(LookupResponse::new(dns_response)), + Rcode::FORMERR => Err(ClientError::FormatError("The name server was unable to interpret the query."))?, + Rcode::SERVFAIL => Err(ClientError::ServerFailure("The name server was unable to process this query due to a problem with the name server."))?, + Rcode::NXDOMAIN => Err(ClientError::NameError("The domain name referenced in the query does not exist."))?, + Rcode::NOTIMP => Err(ClientError::NotImplemented("The name server does not support the requested kind of query."))?, + Rcode::REFUSED => Err(ClientError::Refused("The name server refuses to perform the specified operation for policy reasons."))?, + _ => Err(ClientError::ResponseError(rcode.into()))?, + } + } } // Getters @@ -2009,7 +2058,6 @@ mod async_resolver_test { resolver.save_negative_answers(dns_response.clone()); - let rrtype_search = Rrtype::A; assert_eq!(dns_response.get_answer().len(), 0); assert_eq!(dns_response.get_additional().len(), 1); assert_eq!( diff --git a/src/async_resolver/config.rs b/src/async_resolver/config.rs index 19c32359..ca21f939 100644 --- a/src/async_resolver/config.rs +++ b/src/async_resolver/config.rs @@ -1,8 +1,8 @@ use crate::client::{udp_connection::ClientUDPConnection, tcp_connection::ClientTCPConnection,client_connection::ClientConnection }; use crate::client::client_connection::ConnectionProtocol; use crate::message::DnsMessage; +use crate::tsig::tsig_algorithm::TsigAlgorithm; use std::cmp::max; -use std::option; use std::{net::{IpAddr,SocketAddr,Ipv4Addr}, time::Duration}; use super::server_info::ServerInfo; @@ -73,6 +73,14 @@ pub struct ResolverConfig { ends0_flags: u16, /// edns0 options for the resolver. ends0_options: Vec, + /// This is whether tsig is enabled or not. + tsig: bool, + /// This is the tsig keyname for the resolver. + key_name: Option, + /// This is the tsig key for the resolver. + key: Vec, + /// algorithm for the tsig key + algorithm: TsigAlgorithm, } impl ResolverConfig { @@ -113,6 +121,10 @@ impl ResolverConfig { ends0_version: 0, ends0_flags: 0, ends0_options: Vec::new(), + tsig: false, + key_name: None, + key: Vec::new(), + algorithm: TsigAlgorithm::HmacSha256, }; resolver_config } @@ -154,6 +166,10 @@ impl ResolverConfig { ends0_version: 0, ends0_flags: 0, ends0_options: Vec::new(), + tsig: false, + key_name: None, + key: Vec::new(), + algorithm: TsigAlgorithm::HmacSha256, }; resolver_config } @@ -245,6 +261,38 @@ impl ResolverConfig { message.add_edns0(Some(self.get_max_payload()), self.get_ends0_version(), self.get_ends0_flags(), Some(self.get_ends0_options())); } } + + /// add tsig to the resolver + /// + /// # Examples + /// ``` + /// let mut resolver_config = ResolverConfig::default(); + /// resolver_config.add_tsig("keyname".to_string(), b"key".to_vec(), Some(TsigAlgorithm::HmacSha256)); + /// ``` + pub fn add_tsig(&mut self, key_name: String, key: Vec, algorithm: Option) { + self.tsig = true; + self.key_name = Some(key_name); + self.key = key; + if let Some(algorithm) = algorithm { + self.algorithm = algorithm; + } + } + + /// add tsig from the resolver to a dns message + /// + /// # Examples + /// ``` + /// let mut resolver_config = ResolverConfig::default(); + /// resolver_config.add_tsig("keyname".to_string(), b"key".to_vec(), Some(TsigAlgorithm::HmacSha256)); + /// let message = Message::new(); + /// resolver_config.add_tsig_to_message(&message, 300, vec![]); + /// ``` + pub fn add_tsig_to_message(&self, message: &mut DnsMessage, fudge: Option, mac_request: Option>) { + if self.tsig { + message.add_tsig(self.key.clone(), self.algorithm.clone(), + fudge.unwrap_or(300), self.key_name.clone(), mac_request.unwrap_or(Vec::new())); + } + } } ///Getters @@ -317,6 +365,22 @@ impl ResolverConfig { pub fn get_ends0_options(&self) -> Vec { self.ends0_options.clone() } + + pub fn get_tsig(&self) -> bool { + self.tsig + } + + pub fn get_key_name(&self) -> Option { + self.key_name.clone() + } + + pub fn get_key(&self) -> Vec { + self.key.clone() + } + + pub fn get_algorithm(&self) -> TsigAlgorithm { + self.algorithm.clone() + } } ///Setters @@ -389,6 +453,22 @@ impl ResolverConfig{ pub fn set_ends0_options(&mut self, ends0_options: Vec) { self.ends0_options = ends0_options; } + + pub fn set_tsig(&mut self, tsig: bool) { + self.tsig = tsig; + } + + pub fn set_key_name(&mut self, key_name: Option) { + self.key_name = key_name; + } + + pub fn set_key(&mut self, key: Vec) { + self.key = key; + } + + pub fn set_algorithm(&mut self, algorithm: TsigAlgorithm) { + self.algorithm = algorithm; + } } diff --git a/src/async_resolver/server_info.rs b/src/async_resolver/server_info.rs index b6ab26cb..d0b10e99 100644 --- a/src/async_resolver/server_info.rs +++ b/src/async_resolver/server_info.rs @@ -12,6 +12,8 @@ pub struct ServerInfo { ip_addr: IpAddr, //The port of the server. port: u16, + //Tsig is enabled. + tsig: bool, //The key of the server. key: String, // The algorithm of the server. @@ -29,6 +31,7 @@ impl ServerInfo { ServerInfo { ip_addr, port, + tsig: false, key, algorithm, udp_connection, @@ -43,6 +46,7 @@ impl ServerInfo { ServerInfo { ip_addr, port, + tsig: false, key, algorithm, udp_connection, @@ -59,6 +63,7 @@ impl ServerInfo { ServerInfo { ip_addr, port, + tsig: false, key, algorithm, udp_connection, @@ -66,6 +71,16 @@ impl ServerInfo { } } + /// Function to enable tsig. + pub fn enable_tsig(&mut self) { + self.tsig = true; + } + + /// Function to disable tsig. + pub fn disable_tsig(&mut self) { + self.tsig = false; + } + /// Implements get_ip_address /// Returns IpAddr. pub fn get_ip_addr(&self) -> IpAddr { @@ -88,6 +103,11 @@ impl ServerInfo { self.port = port; } + /// Get the tsig of the server. + pub fn get_tsig(&self) -> bool { + self.tsig + } + /// Get the key of the server. pub fn get_key(&self) -> &str { &self.key diff --git a/src/dns_cache.rs b/src/dns_cache.rs index dc2de025..330547b4 100644 --- a/src/dns_cache.rs +++ b/src/dns_cache.rs @@ -43,7 +43,7 @@ impl DnsCache { pub fn new(max_size: Option) -> Self { let cache = DnsCache { cache: LruCache::new(max_size.unwrap_or_else(|| NonZeroUsize::new(1667).unwrap())), - max_size: max_size.unwrap_or_else(|| NonZeroUsize::new(100).unwrap()), + max_size: max_size.unwrap_or_else(|| NonZeroUsize::new(1667).unwrap()), }; cache } @@ -120,7 +120,7 @@ impl DnsCache { } /// Given a domain_name, gets an element from cache - pub fn get(&mut self, domain_name: DomainName, rrtype: Rrtype, rclass: Rclass) -> Option> { + pub fn get_primary(&mut self, domain_name: DomainName, rrtype: Rrtype, rclass: Rclass) -> Option> { let mut cache = self.get_cache(); let rr_cache_vec = cache.get(&CacheKey::Primary(rrtype, rclass, domain_name)).cloned(); @@ -130,6 +130,23 @@ impl DnsCache { rr_cache_vec } + pub fn get_secondary(&mut self, domain_name: DomainName, rclass: Rclass)-> Option> { + let mut cache = self.get_cache(); + + let rr_cache_vec = cache.get(&&CacheKey::Secondary(rclass, domain_name)).cloned(); + + self.set_cache(cache); + + rr_cache_vec + } + + pub fn get(&mut self, domain_name: DomainName, rrtype: Option, rclass: Rclass) -> Option> { + if rrtype != None { + return self.get_primary(domain_name, rrtype.unwrap(), rclass) + } + self.get_secondary(domain_name, rclass) + } + /// Removes the resource records from a domain name and type which were the oldest used pub fn remove_oldest_used(&mut self) { let mut cache = self.get_cache(); @@ -143,7 +160,7 @@ impl DnsCache { pub fn get_response_time( &mut self, domain_name: DomainName, - rrtype: Rrtype, + rrtype: Option, rclass: Rclass, ip_address: IpAddr, ) -> u32 { @@ -347,7 +364,7 @@ mod dns_cache_test { cache.add(domain_name.clone(), resource_record.clone(), Some(Rrtype::A), Rclass::IN, None); - let rr_cache_vec = cache.get(domain_name.clone(), Rrtype::A, Rclass::IN).unwrap(); + let rr_cache_vec = cache.get(domain_name.clone(), Some(Rrtype::A), Rclass::IN).unwrap(); let first_rr_cache = rr_cache_vec.first().unwrap(); @@ -382,7 +399,7 @@ mod dns_cache_test { cache.add(domain_name.clone(), resource_record.clone(), Some(Rrtype::A), Rclass::IN, None); - let rr_cache_vec = cache.get(domain_name.clone(), Rrtype::A, Rclass::IN).unwrap(); + let rr_cache_vec = cache.get(domain_name.clone(), Some(Rrtype::A), Rclass::IN).unwrap(); assert_eq!(rr_cache_vec.len(), 2); } @@ -411,9 +428,9 @@ mod dns_cache_test { cache.add(domain_name.clone(), resource_record_2.clone(), Some(Rrtype::AAAA), Rclass::IN, None); - let rr_cache_vec = cache.get(domain_name.clone(), Rrtype::A, Rclass::IN).unwrap(); + let rr_cache_vec = cache.get(domain_name.clone(), Some(Rrtype::A), Rclass::IN).unwrap(); - let rr_cache_vec_2 = cache.get(domain_name.clone(), Rrtype::AAAA, Rclass::IN).unwrap(); + let rr_cache_vec_2 = cache.get(domain_name.clone(), Some(Rrtype::AAAA), Rclass::IN).unwrap(); assert_eq!(rr_cache_vec.len(), 1); assert_eq!(rr_cache_vec_2.len(), 1); @@ -443,7 +460,7 @@ mod dns_cache_test { cache.add(domain_name.clone(), resource_record.clone(), Some(Rrtype::A), Rclass::IN, None); - let rr_cache_vec = cache.get(domain_name.clone(), Rrtype::A, Rclass::IN).unwrap(); + let rr_cache_vec = cache.get(domain_name.clone(), Some(Rrtype::A), Rclass::IN).unwrap(); assert_eq!(rr_cache_vec.len(), 1); } @@ -464,7 +481,7 @@ mod dns_cache_test { cache.remove(domain_name.clone(), Some(Rrtype::A), Rclass::IN); - let rr_cache_vec = cache.get(domain_name.clone(), Rrtype::A, Rclass::IN); + let rr_cache_vec = cache.get(domain_name.clone(), Some(Rrtype::A), Rclass::IN); assert!(rr_cache_vec.is_none()); } @@ -483,7 +500,7 @@ mod dns_cache_test { cache.add(domain_name.clone(), resource_record.clone(), Some(Rrtype::A), Rclass::IN, None); - let rr_cache_vec = cache.get(domain_name.clone(), Rrtype::A, Rclass::IN).unwrap(); + let rr_cache_vec = cache.get(domain_name.clone(), Some(Rrtype::A), Rclass::IN).unwrap(); let first_rr_cache = rr_cache_vec.first().unwrap(); @@ -508,7 +525,7 @@ mod dns_cache_test { let mut cache = DnsCache::new(NonZeroUsize::new(10)); let domain_name = DomainName::new_from_str("example.com"); - let rr_cache_vec = cache.get(domain_name.clone(), Rrtype::A, Rclass::IN); + let rr_cache_vec = cache.get(domain_name.clone(), Some(Rrtype::A), Rclass::IN); assert!(rr_cache_vec.is_none()); } @@ -547,21 +564,21 @@ mod dns_cache_test { cache.add(domain_name_2.clone(), resource_record_2.clone(), Some(Rrtype::A), Rclass::IN, None); cache.add(domain_name_3.clone(), resource_record_3.clone(), Some(Rrtype::A), Rclass::IN, None); - let _rr_cache_vec = cache.get(domain_name.clone(), Rrtype::A, Rclass::IN); + let _rr_cache_vec = cache.get(domain_name.clone(), Some(Rrtype::A), Rclass::IN); - let _rr_cache_vec_2 = cache.get(domain_name_2.clone(), Rrtype::A, Rclass::IN); + let _rr_cache_vec_2 = cache.get(domain_name_2.clone(), Some(Rrtype::A), Rclass::IN); cache.remove_oldest_used(); - let rr_cache_vec = cache.get(domain_name_3.clone(), Rrtype::A, Rclass::IN); + let rr_cache_vec = cache.get(domain_name_3.clone(), Some(Rrtype::A), Rclass::IN); assert!(rr_cache_vec.is_none()); - let rr_cache_vec_2 = cache.get(domain_name_2.clone(), Rrtype::A, Rclass::IN); + let rr_cache_vec_2 = cache.get(domain_name_2.clone(), Some(Rrtype::A), Rclass::IN); assert!(rr_cache_vec_2.is_some()); - let rr_cache_vec_3 = cache.get(domain_name.clone(), Rrtype::A, Rclass::IN); + let rr_cache_vec_3 = cache.get(domain_name.clone(), Some(Rrtype::A), Rclass::IN); assert!(rr_cache_vec_3.is_some()); } @@ -590,7 +607,7 @@ mod dns_cache_test { cache.set_cache(lru_cache); - let response_time_obtained = cache.get_response_time(domain_name.clone(), Rrtype::A, Rclass::IN, ip_address); + let response_time_obtained = cache.get_response_time(domain_name.clone(), Some(Rrtype::A), Rclass::IN, ip_address); assert_eq!(response_time_obtained, response_time); } @@ -613,7 +630,7 @@ mod dns_cache_test { cache.update_response_time(domain_name.clone(), Rrtype::A, Rclass::IN, new_response_time, ip_address); - let response_time_obtained = cache.get_response_time(domain_name.clone(), Rrtype::A, Rclass::IN, ip_address); + let response_time_obtained = cache.get_response_time(domain_name.clone(), Some(Rrtype::A), Rclass::IN, ip_address); assert_eq!(response_time_obtained, new_response_time); } @@ -718,11 +735,11 @@ mod dns_cache_test { assert!(!cache.is_empty()); - let rr_cache_vec = cache.get(domain_name.clone(), Rrtype::A, Rclass::IN); + let rr_cache_vec = cache.get(domain_name.clone(), Some(Rrtype::A), Rclass::IN); assert!(rr_cache_vec.is_none()); - let rr_cache_vec_2 = cache.get(domain_name_2.clone(), Rrtype::A, Rclass::IN); + let rr_cache_vec_2 = cache.get(domain_name_2.clone(), Some(Rrtype::A), Rclass::IN); assert!(rr_cache_vec_2.is_some()); } diff --git a/src/dnssec.rs b/src/dnssec.rs new file mode 100644 index 00000000..9f6c7239 --- /dev/null +++ b/src/dnssec.rs @@ -0,0 +1,4 @@ +pub mod dnssec_message; +pub mod dnssec_message_processing; +pub mod dnssec_fetch; +pub mod rrset_signature; \ No newline at end of file diff --git a/src/dnssec/dnssec_fetch.rs b/src/dnssec/dnssec_fetch.rs new file mode 100644 index 00000000..ccd090aa --- /dev/null +++ b/src/dnssec/dnssec_fetch.rs @@ -0,0 +1,20 @@ +use crate::message::DnsMessage; +use crate::message::rdata::Rdata; +use crate::message::rdata::dnskey_rdata::DnskeyRdata; +use crate::message::resource_record::ResourceRecord; +use crate::dnssec::dnssec_message_processing::extract_dnssec_records; +use crate::dnssec::rrset_signature::{verify_rrsig, verify_ds}; + +use crate::client::client_error::ClientError; + +pub async fn fetch_dnskey_records(dns_response: &DnsMessage) -> Result, ClientError> { + let mut dnskey_records = Vec::new(); + + for record in dns_response.get_answer() { + if let Rdata::DNSKEY(dnskey) = &record.get_rdata() { + dnskey_records.push(dnskey.clone()); + } + } + + Ok(dnskey_records) +} diff --git a/src/dnssec/dnssec_message.rs b/src/dnssec/dnssec_message.rs new file mode 100644 index 00000000..3f1b4b0c --- /dev/null +++ b/src/dnssec/dnssec_message.rs @@ -0,0 +1,86 @@ +use std::str::FromStr; +use crate::domain_name::DomainName; +use crate::message::rclass::Rclass; +use crate::message::DnsMessage; +use crate::message::rdata::opt_rdata::OptRdata; +use crate::message::rdata::Rdata; +use crate::message::resource_record::{FromBytes, ResourceRecord, ToBytes}; +use crate::message::rcode; +use crate::message::rcode::Rcode; +use crate::message::rrtype::Rrtype; + +const EDNS_VERSION: u8 = 0; +const REQUESTED_UDP_LEN: u16 = 4096; +/* +The mechanism chosen for the explicit notification of the ability of +the client to accept (if not understand) DNSSEC security RRs is using +the most significant bit of the Z field on the EDNS0 OPT header in +the query. This bit is referred to as the "DNSSEC OK" (DO) bit. In +the context of the EDNS0 OPT meta-RR, the DO bit is the first bit of +the third and fourth bytes of the "extended RCODE and flags" portion +of the EDNS0 OPT meta-RR, structured as follows: + + +0 (MSB) +1 (LSB) + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + 0: | EXTENDED-RCODE | VERSION | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + 2: |DO| Z | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +*/ +fn create_opt_rr(capacity: u16 ,e_rcode :u8, version: u8, do_bit: bool) -> ResourceRecord { + let opt_rdata = OptRdata::new(); + let rdata = Rdata::OPT(opt_rdata); + let mut rr = ResourceRecord::new(rdata); + + let do_val: u16 = if do_bit {0x8000} else {0x0}; + let ttl: u32 = (e_rcode as u32) << 24 | (version as u32) << 16| (do_val as u32); + rr.set_ttl(ttl); + rr.set_rclass(Rclass::UNKNOWN(capacity)); + println!("EL ttl es: {:#05x?}", ttl); + rr +} + +fn read_opt_rr(opt_rr: ResourceRecord) -> String { + let requested_udp_len = Rclass::from(opt_rr.get_rclass()); + let data = opt_rr.get_ttl().to_be_bytes(); + let (e_rcode, version) = (data[0], data[1]); + let z = u16::from_be_bytes([data[2], data[3]]); + + let do_bit = ((z & 0x8000) > 0) as u8 ; + format!("OPT PSEUDO-RR\n\trequested_udp_len: {requested_udp_len}\n\terror code: {e_rcode}\n\tversion: EDNS{version}\n\tuse dnssec: {do_bit}") +} + +/* + A security-aware resolver MUST include an EDNS ([RFC2671]) OPT + pseudo-RR with the DO ([RFC3225]) bit set when sending queries. +*/ +fn create_dns_message_with_dnssec(mut msg: DnsMessage) -> DnsMessage { + // We create a opt rr with the do bit set to 1 + // with NOERR as rcode and EDNS0 + let rr = create_opt_rr(REQUESTED_UDP_LEN, + Rcode::from(Rcode::NOERROR).into(), + EDNS_VERSION, + true); + + let vec = vec![rr]; + msg.add_additionals(vec); + msg +} + +#[test] +fn see_dnssec_message() { + let query = DnsMessage::new_query_message( + DomainName::new_from_str("example.com"), + Rrtype::A, + Rclass::UNKNOWN(4096), + 1, + true, + 2000 + ); + let query= create_dns_message_with_dnssec(query); + assert_eq!(String::from_str + ("OPT PSEUDO-RR\n\trequested_udp_len: 4096\n\terror code: 0\n\tversion: EDNS0\n\tuse dnssec: 1") + .expect("Not a utf8 str"), + read_opt_rr(query.get_additional().pop().expect("No OPT Record!")) + ) +} \ No newline at end of file diff --git a/src/dnssec/dnssec_message_processing.rs b/src/dnssec/dnssec_message_processing.rs new file mode 100644 index 00000000..8bbfff40 --- /dev/null +++ b/src/dnssec/dnssec_message_processing.rs @@ -0,0 +1,21 @@ +use crate::message::DnsMessage; +use crate::message::rdata::Rdata; +use crate::message::resource_record::ResourceRecord; + +pub fn extract_dnssec_records(dns_response: &DnsMessage) -> (Vec, Vec) { + let answers = dns_response.get_answer(); + let additionals = dns_response.get_additional(); + + let mut dnskey_records = Vec::new(); + let mut rrsig_records = Vec::new(); + + for record in answers.iter().chain(additionals.iter()) { + match record.get_rdata() { + Rdata::DNSKEY(_) => dnskey_records.push(record.clone()), + Rdata::RRSIG(_) => rrsig_records.push(record.clone()), + _ => {} + } + } + + (dnskey_records, rrsig_records) +} diff --git a/src/dnssec/rrset_signature.rs b/src/dnssec/rrset_signature.rs new file mode 100644 index 00000000..d7d75447 --- /dev/null +++ b/src/dnssec/rrset_signature.rs @@ -0,0 +1,72 @@ +use sha2::{Sha256, Digest}; +use crypto::digest::Digest as RustDigest; +use crypto::sha1::Sha1; +use base64::encode; +use crate::message::rdata::Rdata; +use crate::message::rdata::dnskey_rdata::DnskeyRdata; +use crate::message::rdata::rrsig_rdata::RRSIGRdata; +use crate::message::rrtype::Rrtype; +use crate::message::resource_record::{ResourceRecord, ToBytes}; +use crate::client::client_error::ClientError; + +pub fn verify_rrsig(rrsig: &RRSIGRdata, dnskey: &DnskeyRdata, rrset: &[ResourceRecord]) -> Result { + let mut rrsig_data = Vec::new(); + rrsig_data.extend_from_slice(&u16::from(rrsig.get_type_covered()).to_be_bytes()); + rrsig_data.push(rrsig.get_algorithm()); + rrsig_data.push(rrsig.get_labels()); + rrsig_data.extend_from_slice(&rrsig.get_original_ttl().to_be_bytes()); + rrsig_data.extend_from_slice(&rrsig.get_signature_expiration().to_be_bytes()); + rrsig_data.extend_from_slice(&rrsig.get_signature_inception().to_be_bytes()); + rrsig_data.extend_from_slice(&rrsig.get_key_tag().to_be_bytes()); + rrsig_data.extend_from_slice(&rrsig.get_signer_name().to_bytes());//Try? + + let mut rrset_sorted = rrset.to_vec(); + rrset_sorted.sort_by(|a, b| a.get_name().cmp(&b.get_name())); + + for rr in rrset_sorted.iter() { + rrsig_data.extend_from_slice(&rr.get_name().to_bytes()); //Try? + rrsig_data.extend_from_slice(&rr.get_ttl().to_be_bytes()); + rrsig_data.extend_from_slice(&(rr.get_rdata().to_bytes().len() as u16).to_be_bytes()); + rrsig_data.extend_from_slice(&rr.get_rdata().to_bytes());//Try? + } + + let signature = rrsig.get_signature().clone(); + let hashed = Sha256::digest(&rrsig_data); + + match dnskey.algorithm { + 3 | 5 => { + // (DSA/RSA)/SHA1 + let mut sha1 = Sha1::new(); + sha1.input(&rrsig_data); + let digest = sha1.result_str(); + Ok(digest == encode(&signature)) + }, + 8 => { + // RSA/SHA256 + Ok(encode(&hashed) == encode(&signature)) + }, + _ => Err(ClientError::NotImplemented("Unknown DNSKEY algorithm")), + } +} + +pub fn verify_ds(ds_record: &ResourceRecord, dnskey: &DnskeyRdata) -> Result { + if let Rdata::DS(ds_rdata) = &ds_record.get_rdata() { + let dnskey_bytes = dnskey.to_bytes(); //Try? + let hashed_key = match ds_rdata.algorithm { + 1 => { + let mut hasher = Sha1::new(); + hasher.input(&dnskey_bytes); + hasher.result_str() + }, + 2 => { + let hashed = Sha256::digest(&dnskey_bytes); + encode(&hashed) + }, + _ => return Err(ClientError::NotImplemented("Unknown DS algorithm")), + }; + + Ok(ds_rdata.digest == hashed_key.as_bytes()) + } else { + Err(ClientError::FormatError("Provided record is not a DS record")) + } +} diff --git a/src/lib.rs b/src/lib.rs index 0a2f1a87..cff3be71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,5 @@ pub mod domain_name; pub mod message; pub mod async_resolver; pub mod truncated_dns_message; - - - - +pub mod tsig; +pub mod dnssec; \ No newline at end of file diff --git a/src/message.rs b/src/message.rs index d2c78262..1efcdd9d 100644 --- a/src/message.rs +++ b/src/message.rs @@ -15,12 +15,15 @@ use crate::message::question::Question; use crate::message::resource_record::ResourceRecord; use crate::message::rdata::Rdata; use crate::message::rdata::opt_rdata::OptRdata; +use crate::tsig; +use crate::tsig::tsig_algorithm::TsigAlgorithm; use crate::message::rdata::opt_rdata::option_code::OptionCode; use rand::thread_rng; use rand::Rng; use resource_record::ToBytes; use core::fmt; use std::vec::Vec; +use std::time::SystemTime; #[derive(Clone)] /// Structs that represents a DNS message. @@ -286,6 +289,51 @@ impl DnsMessage { self.update_header_counters(); } + /// Adds Tsig to the message. + /// + /// # Example + /// ``` + /// let dns_query_message = new_query_message(DomainName::new_from_str("example.com".to_string()), Rrtype::A, Rclass:IN, 0, false); + /// let key = vec![1, 2, 3, 4, 5, 6, 7, 8]; + /// let alg_name = TsigAlgorithm::HmacSha1; + /// let fudge = 300; + /// let key_name = "key".to_string(); + /// let mac_request = vec![]; + /// dns_query_message.add_tsig(key, alg_name, fudge, key_name, mac_request); + /// ``` + pub fn add_tsig(&mut self, key: Vec, alg_name: TsigAlgorithm, + fudge: u16, key_name: Option, mac_request: Vec) { + let message = self; + let time_signed = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); + tsig::sign_tsig(message, &key, alg_name, + fudge, time_signed, key_name.unwrap_or("".to_string()), mac_request); + } + + /// Gets the MAC from the TSIG RR. + /// + /// # Example + /// ``` + /// let dns_query_message = new_query_message(DomainName::new_from_str("example.com".to_string()), Rrtype::A, Rclass:IN, 0, false); + /// let key = vec![1, 2, 3, 4, 5, 6, 7, 8]; + /// let alg_name = TsigAlgorithm::HmacSha1; + /// let fudge = 300; + /// let key_name = "key".to_string(); + /// let mac_request = vec![]; + /// dns_query_message.add_tsig(key, alg_name, fudge, key_name, mac_request); + /// let mac = dns_query_message.get_mac(); + /// ``` + pub fn get_mac(&self) -> Vec { + let mut mac = Vec::new(); + let additional = self.get_additional(); + + for rr in additional { + if let Rdata::TSIG(tsig_rdata) = rr.get_rdata() { + mac = tsig_rdata.get_mac(); + } + } + + mac + } /// Creates a new axfr query message. /// @@ -615,6 +663,7 @@ impl DnsMessage { let mut msg_answers = self.get_answer(); msg_answers.append(&mut answers); + self.header.set_ancount(msg_answers.len() as u16); self.set_answer(msg_answers); } @@ -640,6 +689,7 @@ impl DnsMessage { let mut msg_authorities = self.get_authority(); msg_authorities.append(&mut authorities); + self.header.set_nscount(msg_authorities.len() as u16); self.set_answer(msg_authorities); } @@ -656,6 +706,7 @@ impl DnsMessage { let mut msg_additionals = self.get_additional(); msg_additionals.append(&mut additionals); + self.header.set_arcount(msg_additionals.len() as u16); self.set_additional(msg_additionals); } @@ -683,14 +734,17 @@ impl DnsMessage { impl fmt::Display for DnsMessage { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut result = String::new(); + let question = self.get_question(); let answers = self.get_answer().into_iter(); let authority = self.get_authority().into_iter(); let additional = self.get_additional().into_iter(); - result.push_str(&format!("Answer\n")); + result.push_str(&format!("Question section\n")); + result.push_str(&format!("{}\n", question)); + result.push_str(&format!("Answer section\n")); answers.for_each(|answer| result.push_str(&format!("{}\n", answer))); - result.push_str(&format!("Authority\n")); + result.push_str(&format!("Authority section\n")); authority.for_each(|authority| result.push_str(&format!("{}\n", authority))); - result.push_str(&format!("Additional\n")); + result.push_str(&format!("Additional section\n")); additional.for_each(|additional| result.push_str(&format!("{}\n", additional))); write!(f, "{}", result) } @@ -984,7 +1038,7 @@ mod message_test { */ let bytes: [u8; 50] = [ //test passes with this one - 0b00100100, 0b10010101, 0b10010010, 0b00000000, 0, 1, 0b00000000, 1, 0, 0, 0, 0, 4, 116, + 0b00100100, 0b10010101, 0b10010010, 0b00100000, 0, 1, 0b00000000, 1, 0, 0, 0, 0, 4, 116, 101, 115, 116, 3, 99, 111, 109, 0, 0, 16, 0, 1, 3, 100, 99, 99, 2, 99, 108, 0, 0, 16, 0, 1, 0, 0, 0b00010110, 0b00001010, 0, 6, 5, 104, 101, 108, 108, 111, ]; @@ -1002,7 +1056,10 @@ mod message_test { assert_eq!(header.get_qr(), true); assert_eq!(header.get_op_code(), 2); assert_eq!(header.get_tc(), true); + + assert_eq!(header.get_ad(), true); assert_eq!(header.get_rcode(), Rcode::NOERROR); + assert_eq!(header.get_ancount(), 1); // Question @@ -1041,7 +1098,10 @@ mod message_test { header.set_qr(true); header.set_op_code(2); header.set_tc(true); + + header.set_ad(true); header.set_rcode(Rcode::UNKNOWN(8)); + header.set_ancount(0b0000000000000001); header.set_qdcount(1); @@ -1081,7 +1141,7 @@ mod message_test { let msg_bytes = &dns_msg.to_bytes(); let real_bytes: [u8; 50] = [ - 0b00100100, 0b10010101, 0b10010010, 0b00001000, 0, 1, 0b00000000, 0b00000001, 0, 0, 0, + 0b00100100, 0b10010101, 0b10010010, 0b00101000, 0, 1, 0b00000000, 0b00000001, 0, 0, 0, 0, 4, 116, 101, 115, 116, 3, 99, 111, 109, 0, 0, 5, 0, 2, 3, 100, 99, 99, 2, 99, 108, 0, 0, 16, 0, 1, 0, 0, 0b00010110, 0b00001010, 0, 6, 5, 104, 101, 108, 108, 111, ]; diff --git a/src/message/header.rs b/src/message/header.rs index 4e2b60dc..0d9b6c9b 100644 --- a/src/message/header.rs +++ b/src/message/header.rs @@ -3,22 +3,23 @@ use crate::message::rcode::Rcode; #[derive(Default, Clone)] /// An struct that represents a Header secction from a DNS message. -/// -/// 1 1 1 1 1 1 -/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 -/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ -/// | ID | -/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ -/// |QR| Opcode |AA|TC|RD|RA| Z | RCODE | -/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ -/// | QDCOUNT | -/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ -/// | ANCOUNT | -/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ -/// | NSCOUNT | -/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ -/// | ARCOUNT | -/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +/// EDIT: now added bits AD CD for DNS security extensions. +/// +/// 1 1 1 1 1 1 +/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +/// | ID | +/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +/// |QR| Opcode |AA|TC|RD|RA| Z|AD|CD| RCODE | +/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +/// | QDCOUNT | +/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +/// | ANCOUNT | +/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +/// | NSCOUNT | +/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +/// | ARCOUNT | +/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ #[derive (PartialEq, Debug)] pub struct Header { /// Id @@ -40,10 +41,12 @@ pub struct Header { tc: bool, // TrunCation rd: bool, // Recursion Desired ra: bool, // Recursion Available + ad: bool, // Authentic Data + cd: bool, // Checking Disabled - /// Reserved + /// Reserved Edit: Now z is just a flag #[allow(dead_code)] - z: u8, + z: bool, /// Response Code /// @@ -137,7 +140,11 @@ impl Header { let tc = (bytes[2] & 0b00000010) >> 1; let rd = bytes[2] & 0b00000001; let ra = bytes[3] >> 7; + + let ad = (bytes[3] & 0b00100000) >> 5; + let cd = (bytes[3] & 0b00010000) >> 4; let rcode = Rcode::from(bytes[3] & 0b00001111); + let qdcount = ((bytes[4] as u16) << 8) | bytes[5] as u16; let ancount = ((bytes[6] as u16) << 8) | bytes[7] as u16; let nscount = ((bytes[8] as u16) << 8) | bytes[9] as u16; @@ -151,6 +158,8 @@ impl Header { header.set_tc(tc != 0); header.set_rd(rd != 0); header.set_ra(ra != 0); + header.set_ad(ad != 0); + header.set_cd(cd != 0); header.set_rcode(rcode); header.set_qdcount(qdcount); header.set_ancount(ancount); @@ -209,7 +218,7 @@ impl Header { return 0b00000100; } - return 0 as u8; + return 0u8; } /// Returns a byte that represents the field in the DNS message. @@ -222,7 +231,7 @@ impl Header { return 0b00000010; } - return 0 as u8; + return 0u8; } /// Returns a byte that represents the field in the DNS message. @@ -235,7 +244,7 @@ impl Header { return 0b00000001; } - return 0 as u8; + return 0u8; } /// Returns a byte that represents the field in the DNS message. @@ -248,7 +257,33 @@ impl Header { return 0b10000000; } - return 0 as u8; + return 0u8; + } + + /// Returns a byte that represents the field in the DNS message. + /// + /// See the DNS message structure in struct documentation for more info. + fn ad_to_byte(&self) -> u8 { + let ad = self.get_ad(); + + if ad { + return 0b00100000; + } + + return 0u8; + } + + /// Returns a byte that represents the field in the DNS message. + /// + /// See the DNS message structure in struct documentation for more info. + fn cd_to_byte(&self) -> u8 { + let cd = self.get_cd(); + + if cd { + return 0b00010000; + } + + return 0u8; } /// Gets the first byte from the qdcount attribute. @@ -331,9 +366,13 @@ impl Header { /// Gets a byte that represents the second byte of flags section. fn get_second_flags_byte(&self) -> u8 { let ra_byte = self.ra_to_byte(); + + let ad_byte = self.ad_to_byte(); + let cd_byte = self.cd_to_byte(); let rcode_byte = u8::from(self.get_rcode()); - let second_byte = ra_byte | rcode_byte; + + let second_byte = ra_byte | ad_byte | cd_byte | rcode_byte; second_byte } @@ -382,7 +421,7 @@ impl Header { header_bytes } - /// Checks if the header is well formed. + /// Checks if the header is well-formed. pub fn format_check(&self)-> Result{ // OP CODE: A four bit field between 0-15 @@ -390,8 +429,8 @@ impl Header { return Err("Format Error: OP CODE"); } - // Z: A 3 bit field that MUST be zero - if self.z != 0 { + // Z: A z flag field MUST be zero/false + if self.z != false { return Err("Format Error: Z"); } @@ -441,6 +480,16 @@ impl Header { self.ra = ra; } + /// Sets the ad attribute with a value. + pub fn set_ad(&mut self, ad: bool) { + self.ad = ad; + } + + /// Sets the cd attribute with a value. + pub fn set_cd(&mut self, cd: bool) { + self.cd = cd; + } + /// Sets the rcode attribute with a value. pub fn set_rcode(&mut self, rcode: Rcode) { self.rcode = rcode; @@ -504,6 +553,16 @@ impl Header { self.ra } + /// Gets the ad attribute value. + pub fn get_ad(&self) -> bool { + self.ad + } + + /// Gets the cd attribute value. + pub fn get_cd(&self) -> bool { + self.cd + } + /// Gets the `rcode` attribute value. pub fn get_rcode(&self) -> Rcode { self.rcode @@ -546,7 +605,11 @@ mod header_test { assert_eq!(header.tc, false); assert_eq!(header.rd, false); assert_eq!(header.ra, false); + + assert_eq!(header.ad, false); + assert_eq!(header.cd, false); assert_eq!(header.rcode, Rcode::NOERROR); + assert_eq!(header.qdcount, 0); assert_eq!(header.ancount, 0); assert_eq!(header.nscount, 0); @@ -637,6 +700,30 @@ mod header_test { assert_eq!(ra, true); } + #[test] + fn set_and_get_ad() { + let mut header = Header::new(); + + let mut ad = header.get_ad(); + assert_eq!(ad, false); + + header.set_ad(true); + ad = header.get_ad(); + assert_eq!(ad, true); + } + + #[test] + fn set_and_get_cd() { + let mut header = Header::new(); + + let mut cd = header.get_cd(); + assert_eq!(cd, false); + + header.set_cd(true); + cd = header.get_cd(); + assert_eq!(cd, true); + } + #[test] fn set_and_get_rcode() { let mut header = Header::new(); @@ -708,13 +795,17 @@ mod header_test { header.set_qr(true); header.set_op_code(2); header.set_tc(true); + + header.set_ad(true); + header.set_cd(true); header.set_rcode(Rcode::REFUSED); + header.set_ancount(0b0000101010100101); bytes[0] = 0b00100100; bytes[1] = 0b10010101; bytes[2] = 0b10010010; - bytes[3] = 0b00000101; + bytes[3] = 0b00110101; bytes[6] = 0b00001010; bytes[7] = 0b10100101; @@ -728,7 +819,7 @@ mod header_test { bytes[0] = 0b00100100; bytes[1] = 0b10010101; bytes[2] = 0b10010010; - bytes[3] = 0b00000101; + bytes[3] = 0b00110101; bytes[6] = 0b00001010; bytes[7] = 0b10100101; @@ -738,7 +829,11 @@ mod header_test { header.set_qr(true); header.set_op_code(2); header.set_tc(true); + + header.set_ad(true); + header.set_cd(true); header.set_rcode(Rcode::REFUSED); + header.set_ancount(0b0000101010100101); let header_from_bytes = Header::from_bytes(&bytes); @@ -790,8 +885,10 @@ mod header_test { ]; let mut header = Header::from_bytes(&bytes_header); - header.z = 3; + + header.z = true; header.set_rcode(Rcode::UNKNOWN(16)); + header.set_op_code(22); let result_check = header.format_check(); diff --git a/src/message/question.rs b/src/message/question.rs index af445d7a..bc9f4cd5 100644 --- a/src/message/question.rs +++ b/src/message/question.rs @@ -4,6 +4,8 @@ use crate::message::rclass::Rclass; use super::rrtype::Rrtype; +use std::fmt; + #[derive(Default, Clone)] /// An struct that represents the question section from a dns message /// ```text @@ -29,6 +31,20 @@ pub struct Question { rclass: Rclass, } + +impl fmt::Display for Question { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + let name = self.get_qname().get_name(); + let rtype = self.get_rrtype(); + let rclass = self.get_rclass(); + + formatter.write_fmt(format_args!( + "{} {} {} ", + name, rclass, rtype + )) + } +} + // Methods impl Question { /// Creates a new Question with default values diff --git a/src/message/rcode.rs b/src/message/rcode.rs index 91d3d036..a4f5d1d3 100644 --- a/src/message/rcode.rs +++ b/src/message/rcode.rs @@ -11,6 +11,9 @@ pub enum Rcode { NXDOMAIN, NOTIMP, REFUSED, + BADSIG, + BADKEY, + BADTIME, UNKNOWN(u8), } @@ -23,6 +26,9 @@ impl From for Rcode { 3 => Rcode::NXDOMAIN, 4 => Rcode::NOTIMP, 5 => Rcode::REFUSED, + 16 => Rcode::BADSIG, + 17 => Rcode::BADKEY, + 18 => Rcode::BADTIME, _ => Rcode::UNKNOWN(int), } } @@ -37,6 +43,9 @@ impl From for u8 { Rcode::NXDOMAIN => 3, Rcode::NOTIMP => 4, Rcode::REFUSED => 5, + Rcode::BADSIG => 16, + Rcode::BADKEY => 17, + Rcode::BADTIME => 18, Rcode::UNKNOWN(u8) => u8, } } @@ -69,6 +78,9 @@ impl fmt::Display for Rcode { Rcode::NXDOMAIN => "NXDOMAIN", Rcode::NOTIMP => "NOTIMP", Rcode::REFUSED => "REFUSED", + Rcode::BADSIG => "BADSIG", + Rcode::BADKEY => "BADKEY", + Rcode::BADTIME => "BADTIME", Rcode::UNKNOWN(_) => "UNKNOWN", }) } diff --git a/src/message/rdata.rs b/src/message/rdata.rs index c186c080..f057bd18 100644 --- a/src/message/rdata.rs +++ b/src/message/rdata.rs @@ -589,11 +589,33 @@ mod resolver_query_tests { #[test] fn to_bytes_tsigrdata(){ let expected_bytes = vec![ - 0x8, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x6D, 0x64, - 0x35, 0x7, 0x73, 0x69, 0x67, 0x2D, 0x61, 0x6C, 0x67, - 0x3, 0x72, 0x65, 0x67, 0x3, 0x69, 0x6E, 0x74, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x7, 0x5B, 0xCD, 0x15, 0x4, 0xD2, 0x0, 0x4, 0xA1, 0xB2, 0xC3, 0xD4, - 0x4, 0xD2, 0x0, 0x0, 0x0, 0x0 + //This is the string "hmac-md5.sig-alg.reg.int" in octal, terminated in 00 + 0x8, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x6D, 0x64, + 0x35, 0x7, 0x73, 0x69, 0x67, 0x2D, 0x61, 0x6C, 0x67, + 0x3, 0x72, 0x65, 0x67, 0x3, 0x69, 0x6E, 0x74, 0x0, + + //this is the time signed 123456789 == 0x75bcd15 + 0x0, 0x0, 0x7, 0x5B, 0xCD, 0x15, + + // this the fudge 1234 + 0x4, 0xD2, + + // this is the macsize = 4 + 0x0, 0x4, + + // this is the mac = [0xA1, 0xB2, 0xC3, 0xD4] + 0xA1, 0xB2, 0xC3, 0xD4, + + // this is the original id = 1234 + 0x4, 0xD2, + + // this is the error = 0 + 0x0, 0x0, + + // this is the other len = 0 + 0x0, 0x0 + + // No other data, so its empty! ]; let mut tsig_rdata = TSigRdata::new(); @@ -932,11 +954,34 @@ mod resolver_query_tests { #[test] fn from_bytes_tsig_rdata(){ let data_bytes = vec![ - 0x8, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x6D, 0x64, - 0x35, 0x7, 0x73, 0x69, 0x67, 0x2D, 0x61, 0x6C, 0x67, - 0x3, 0x72, 0x65, 0x67, 0x3, 0x69, 0x6E, 0x74, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x7, 0x5B, 0xCD, 0x15, 0x4, 0xD2, 0x0, 0x4, 0xA1, 0xB2, 0xC3, 0xD4, - 0x4, 0xD2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFA, 0x0, 0x1 + //This is the string "hmac-md5.sig-alg.reg.int" in octal, terminated in 00 + 0x8, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x6D, 0x64, + 0x35, 0x7, 0x73, 0x69, 0x67, 0x2D, 0x61, 0x6C, 0x67, + 0x3, 0x72, 0x65, 0x67, 0x3, 0x69, 0x6E, 0x74, 0x0, + + //this is the time signed 123456789 == 0x75bcd15 + 0x0, 0x0, 0x7, 0x5B, 0xCD, 0x15, + + // this the fudge 1234 + 0x4, 0xD2, + + // this is the macsize = 4 + 0x0, 0x4, + + // this is the mac = [0xA1, 0xB2, 0xC3, 0xD4] + 0xA1, 0xB2, 0xC3, 0xD4, + + // this is the original id = 1234 + 0x4, 0xD2, + + // this is the error = 0 + 0x0, 0x0, + + // this is the other len = 0 + 0x0, 0x0, + //Extra bytes for from_bytes function + 0x0, 0xFA, 0x0, 0x1 + // No other data, so its empty! ]; let rdata = Rdata::from_bytes(&data_bytes, &data_bytes).unwrap(); let mut domain_name = DomainName::new(); diff --git a/src/message/rdata/opt_rdata.rs b/src/message/rdata/opt_rdata.rs index ce394dec..1c2b0fae 100644 --- a/src/message/rdata/opt_rdata.rs +++ b/src/message/rdata/opt_rdata.rs @@ -53,6 +53,7 @@ impl FromBytes> for OptRdata { return Err("Format Error"); } + let option_code = OptionCode::from(u16::from_be_bytes([bytes[i], bytes[i + 1]])); let option_length = u16::from_be_bytes([bytes[i + 2], bytes[i + 3]]); diff --git a/src/message/rdata/tsig_rdata.rs b/src/message/rdata/tsig_rdata.rs index 179d759d..6519cc05 100644 --- a/src/message/rdata/tsig_rdata.rs +++ b/src/message/rdata/tsig_rdata.rs @@ -67,10 +67,6 @@ impl ToBytes for TSigRdata{ let time_signed = self.get_time_signed(); - bytes.push((time_signed >> 56) as u8); - - bytes.push((time_signed >> 48) as u8); - bytes.push((time_signed >> 40) as u8); bytes.push((time_signed >> 32) as u8); @@ -78,6 +74,10 @@ impl ToBytes for TSigRdata{ bytes.push((time_signed >> 24) as u8); bytes.push((time_signed >> 16) as u8); + + bytes.push((time_signed >> 8) as u8); + + bytes.push((time_signed >> 0) as u8); let fudge = self.get_fudge(); @@ -255,14 +255,14 @@ impl TSigRdata { /// Set the time signed attribute from an array of bytes. fn set_time_signed_from_bytes(&mut self, bytes: &[u8]){ - let time_signed = (bytes[0] as u64) << 56 - | (bytes[1] as u64) << 48 - | (bytes[2] as u64) << 40 - | (bytes[3] as u64) << 32 - | (bytes[4] as u64) << 24 - | (bytes[5] as u64) << 16 - | (0 as u64) << 8 - | 0 as u64; + + let time_signed = (bytes[0] as u64) << 40 + | (bytes[1] as u64) << 32 + | (bytes[2] as u64) << 24 + | (bytes[3] as u64) << 16 + | (bytes[4] as u64) << 8 + | (bytes[5] as u64) << 0; + self.set_time_signed(time_signed); } @@ -561,7 +561,6 @@ mod tsig_rdata_test { } #[test] - #[ignore = "Fix test"] fn to_bytes_test(){ let mut tsig_rdata = TSigRdata::new(); @@ -580,11 +579,33 @@ mod tsig_rdata_test { let bytes_to_test = tsig_rdata.to_bytes(); let bytes = vec![ - 0x8, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x6D, 0x64, - 0x35, 0x7, 0x73, 0x69, 0x67, 0x2D, 0x61, 0x6C, 0x67, - 0x3, 0x72, 0x65, 0x67, 0x3, 0x69, 0x6E, 0x74, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x7, 0x5B, 0xCD, 0x15, 0x4, 0xD2, 0x0, 0x4, 0xA1, 0xB2, 0xC3, 0xD4, - 0x4, 0xD2, 0x0, 0x0, 0x0, 0x0 + //This is the string "hmac-md5.sig-alg.reg.int" in octal, terminated in 00 + 0x8, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x6D, 0x64, + 0x35, 0x7, 0x73, 0x69, 0x67, 0x2D, 0x61, 0x6C, 0x67, + 0x3, 0x72, 0x65, 0x67, 0x3, 0x69, 0x6E, 0x74, 0x0, + + //this is the time signed 123456789 == 0x75bcd15 + 0x0, 0x0, 0x7, 0x5B, 0xCD, 0x15, + + // this the fudge 1234 + 0x4, 0xD2, + + // this is the macsize = 4 + 0x0, 0x4, + + // this is the mac = [0xA1, 0xB2, 0xC3, 0xD4] + 0xA1, 0xB2, 0xC3, 0xD4, + + // this is the original id = 1234 + 0x4, 0xD2, + + // this is the error = 0 + 0x0, 0x0, + + // this is the other len = 0 + 0x0, 0x0 + + // No other data, so its empty! ]; for i in 0..bytes.len() { @@ -593,14 +614,35 @@ mod tsig_rdata_test { } #[test] - #[ignore = "Fix test"] fn from_bytes_test(){ let bytes = vec![ - 0x8, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x6D, 0x64, - 0x35, 0x7, 0x73, 0x69, 0x67, 0x2D, 0x61, 0x6C, 0x67, - 0x3, 0x72, 0x65, 0x67, 0x3, 0x69, 0x6E, 0x74, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x7, 0x5B, 0xCD, 0x15, 0x4, 0xD2, 0x0, 0x4, 0xA1, 0xB2, 0xC3, 0xD4, - 0x4, 0xD2, 0x0, 0x0, 0x0, 0x0 + //This is the string "hmac-md5.sig-alg.reg.int" in octal, terminated in 00 + 0x8, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x6D, 0x64, + 0x35, 0x7, 0x73, 0x69, 0x67, 0x2D, 0x61, 0x6C, 0x67, + 0x3, 0x72, 0x65, 0x67, 0x3, 0x69, 0x6E, 0x74, 0x0, + + //this is the time signed 123456789 == 0x75bcd15 + 0x0, 0x0, 0x7, 0x5B, 0xCD, 0x15, + + // this the fudge 1234 + 0x4, 0xD2, + + // this is the macsize = 4 + 0x0, 0x4, + + // this is the mac = [0xA1, 0xB2, 0xC3, 0xD4] + 0xA1, 0xB2, 0xC3, 0xD4, + + // this is the original id = 1234 + 0x4, 0xD2, + + // this is the error = 0 + 0x0, 0x0, + + // this is the other len = 0 + 0x0, 0x0 + + // No other data, so its empty! ]; let tsig_rdata_result = TSigRdata::from_bytes(&bytes, &bytes); diff --git a/src/message/resource_record.rs b/src/message/resource_record.rs index 78017e58..eda68225 100644 --- a/src/message/resource_record.rs +++ b/src/message/resource_record.rs @@ -86,7 +86,7 @@ impl ResourceRecord { rtype: Rrtype::A, rclass: Rclass::IN, ttl: 0 as u32, - rdlength: 0 as u16, + rdlength: val.to_bytes().len() as u16, rdata: Rdata::A(val), }, Rdata::NS(val) => ResourceRecord { @@ -94,7 +94,7 @@ impl ResourceRecord { rtype: Rrtype::NS, rclass: Rclass::IN, ttl: 0 as u32, - rdlength: 0 as u16, + rdlength: val.to_bytes().len() as u16, rdata: Rdata::NS(val), }, Rdata::CNAME(val) => ResourceRecord { @@ -102,7 +102,7 @@ impl ResourceRecord { rtype: Rrtype::CNAME, rclass: Rclass::IN, ttl: 0 as u32, - rdlength: 0 as u16, + rdlength: val.to_bytes().len() as u16, rdata: Rdata::CNAME(val), }, Rdata::SOA(val) => ResourceRecord { @@ -110,7 +110,7 @@ impl ResourceRecord { rtype: Rrtype::SOA, rclass: Rclass::IN, ttl: 0 as u32, - rdlength: 0 as u16, + rdlength: val.to_bytes().len() as u16, rdata: Rdata::SOA(val), }, Rdata::PTR(val) => ResourceRecord { @@ -118,7 +118,7 @@ impl ResourceRecord { rtype: Rrtype::PTR, rclass: Rclass::IN, ttl: 0 as u32, - rdlength: 0 as u16, + rdlength: val.to_bytes().len() as u16, rdata: Rdata::PTR(val), }, Rdata::HINFO(val) => ResourceRecord { @@ -126,7 +126,7 @@ impl ResourceRecord { rtype: Rrtype::HINFO, rclass: Rclass::IN, ttl: 0 as u32, - rdlength: 0 as u16, + rdlength: val.to_bytes().len() as u16, rdata: Rdata::HINFO(val), }, Rdata::MX(val) => ResourceRecord { @@ -134,7 +134,7 @@ impl ResourceRecord { rtype: Rrtype::MX, rclass: Rclass::IN, ttl: 0 as u32, - rdlength: 0 as u16, + rdlength: val.to_bytes().len() as u16, rdata: Rdata::MX(val), }, Rdata::TXT(val) => ResourceRecord { @@ -142,7 +142,7 @@ impl ResourceRecord { rtype: Rrtype::TXT, rclass: Rclass::IN, ttl: 0 as u32, - rdlength: 0 as u16, + rdlength: val.to_bytes().len() as u16, rdata: Rdata::TXT(val), }, Rdata::AAAA(val) => ResourceRecord { @@ -150,7 +150,7 @@ impl ResourceRecord { rtype: Rrtype::AAAA, rclass: Rclass::IN, ttl: 0 as u32, - rdlength: 0 as u16, + rdlength: val.to_bytes().len() as u16, rdata: Rdata::AAAA(val), }, Rdata::OPT(val) => ResourceRecord { @@ -158,7 +158,7 @@ impl ResourceRecord { rtype: Rrtype::OPT, rclass: Rclass::IN, ttl: 0 as u32, - rdlength: 0 as u16, + rdlength: val.to_bytes().len() as u16, rdata: Rdata::OPT(val), }, Rdata::DS(val) => ResourceRecord { @@ -166,7 +166,7 @@ impl ResourceRecord { rtype: Rrtype::DS, rclass: Rclass::IN, ttl: 0 as u32, - rdlength: 0 as u16, + rdlength: val.to_bytes().len() as u16, rdata: Rdata::DS(val), }, Rdata::RRSIG(val) => ResourceRecord { @@ -174,7 +174,7 @@ impl ResourceRecord { rtype: Rrtype::RRSIG, rclass: Rclass::IN, ttl: 0 as u32, - rdlength: 0 as u16, + rdlength: val.to_bytes().len() as u16, rdata: Rdata::RRSIG(val), }, Rdata::NSEC(val) => ResourceRecord { @@ -182,7 +182,7 @@ impl ResourceRecord { rtype: Rrtype::NSEC, rclass: Rclass::IN, ttl: 0 as u32, - rdlength: 0 as u16, + rdlength: val.to_bytes().len() as u16, rdata: Rdata::NSEC(val), }, Rdata::DNSKEY(val) => ResourceRecord { @@ -190,7 +190,7 @@ impl ResourceRecord { rtype: Rrtype::DNSKEY, rclass: Rclass::IN, ttl: 0 as u32, - rdlength: 0 as u16, + rdlength: val.to_bytes().len() as u16, rdata: Rdata::DNSKEY(val), }, Rdata::NSEC3(val) => ResourceRecord { @@ -198,7 +198,7 @@ impl ResourceRecord { rtype: Rrtype::NSEC3, rclass: Rclass::IN, ttl: 0 as u32, - rdlength: 0 as u16, + rdlength: val.to_bytes().len() as u16, rdata: Rdata::NSEC3(val), }, Rdata::NSEC3PARAM(val) => ResourceRecord { @@ -206,15 +206,15 @@ impl ResourceRecord { rtype: Rrtype::NSEC3PARAM, rclass: Rclass::IN, ttl: 0 as u32, - rdlength: 0 as u16, + rdlength: val.to_bytes().len() as u16, rdata: Rdata::NSEC3PARAM(val), }, Rdata::TSIG(val) => ResourceRecord { name: DomainName::new(), rtype: Rrtype::TSIG, - rclass: Rclass::IN, + rclass: Rclass::ANY, ttl: 0 as u32, - rdlength: 0 as u16, + rdlength: val.to_bytes().len() as u16, rdata: Rdata::TSIG(val), }, _ => ResourceRecord { diff --git a/src/message/rrset.rs b/src/message/rrset.rs new file mode 100644 index 00000000..00e5d0a9 --- /dev/null +++ b/src/message/rrset.rs @@ -0,0 +1,185 @@ +use crate::message::Rtype; +use crate::message::Rclass; +use crate::message::resource_record::ResourceRecord; +use std::collections::HashSet; + +/// Represents a set of resource records (RRset). +#[derive(Debug)] +pub struct RRset { + /// The name of the domain associated with this RRset. + name: String, + /// The type of resource record in this RRset. + rtype: Rtype, + /// The class of resource record in this RRset. + rclass: Rclass, + /// The time to live (TTL) value for records in this RRset. + ttl: u32, + /// The set of resource records belonging to this RRset. + records: HashSet, +} + +impl RRset { + /// Creates a new RRset. + pub fn new(name: String, rtype: Rtype, rclass: Rclass, ttl: u32) -> RRset { + RRset { + name, + rtype, + rclass, + ttl, + records: HashSet::new(), + } + } + + /// Adds a resource record to this RRset. + pub fn add_record(&mut self, record: ResourceRecord) { + self.records.insert(record); + } + + /// Gets the name of the domain associated with this RRset. + pub fn get_name(&self) -> &String { + &self.name + } + + /// Gets the type of resource record in this RRset. + pub fn get_type(&self) -> Rtype { + self.rtype + } + + /// Gets the class of resource record in this RRset. + pub fn get_class(&self) -> Rclass { + self.rclass + } + + /// Gets the time to live (TTL) value for records in this RRset. + pub fn get_ttl(&self) -> u32 { + self.ttl + } + + /// Gets the set of resource records belonging to this RRset. + pub fn get_records(&self) -> &HashSet { + &self.records + } + + /// Gets the labels of the domain associated with this RRset. + pub fn get_labels(&self) -> usize { + self.name.split('.').count() + } + + /// Serializes the RRset to a byte array for signing. + pub fn to_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + for record in &self.records { + bytes.extend(record.to_bytes()); // Assuming ResourceRecord has a to_bytes method + } + bytes + } +} + + +#[cfg(test)] +mod tests { + use super::*; + use crate::message::Rtype; + use crate::message::Rclass; + use crate::message::resource_record::{ResourceRecord, Rdata, ARdata, NsRdata, CnameRdata}; + use std::net::IpAddr; + use std::collections::HashSet; + + #[test] + fn test_create_rrset() { + let name = "example.com".to_string(); + let rtype = Rtype::A; + let rclass = Rclass::IN; + let ttl = 3600; + + let rrset = RRset::new(name.clone(), rtype, rclass, ttl); + + assert_eq!(rrset.get_name(), &name); + assert_eq!(rrset.get_type(), Rtype::A); + assert_eq!(rrset.get_class(), Rclass::IN); + assert_eq!(rrset.get_ttl(), 3600); + assert_eq!(rrset.get_labels(), 2); + assert!(rrset.get_records().is_empty()); + } + + #[test] + fn test_add_record() { + let name = "example.com".to_string(); + let rtype = Rtype::A; + let rclass = Rclass::IN; + let ttl = 3600; + + let mut rrset = RRset::new(name.clone(), rtype, rclass, ttl); + + let mut a_rdata = Rdata::A(ARdata::new()); + match a_rdata { + Rdata::A(ref mut val) => val.set_address(IpAddr::from([127, 0, 0, 1])), + _ => unreachable!(), + } + + let record = ResourceRecord::new(a_rdata); + rrset.add_record(record); + + assert_eq!(rrset.get_records().len(), 1); + } + + #[test] + fn test_get_name() { + let name = "example.com".to_string(); + let rtype = Rtype::A; + let rclass = Rclass::IN; + let ttl = 3600; + + let rrset = RRset::new(name.clone(), rtype, rclass, ttl); + + assert_eq!(rrset.get_name(), &name); + } + + #[test] + fn test_get_type() { + let name = "example.com".to_string(); + let rtype = Rtype::NS; + let rclass = Rclass::IN; + let ttl = 3600; + + let rrset = RRset::new(name.clone(), rtype, rclass, ttl); + + assert_eq!(rrset.get_type(), Rtype::NS); + } + + #[test] + fn test_get_class() { + let name = "example.com".to_string(); + let rtype = Rtype::MX; + let rclass = Rclass::CH; + let ttl = 3600; + + let rrset = RRset::new(name.clone(), rtype, rclass, ttl); + + assert_eq!(rrset.get_class(), Rclass::CH); + } + + #[test] + fn test_get_ttl() { + let name = "example.com".to_string(); + let rtype = Rtype::A; + let rclass = Rclass::IN; + let ttl = 7200; + + let rrset = RRset::new(name.clone(), rtype, rclass, ttl); + + assert_eq!(rrset.get_ttl(), 7200); + } + + #[test] + fn test_get_labels() { + let name = "sub.example.com".to_string(); + let rtype = Rtype::A; + let rclass = Rclass::IN; + let ttl = 3600; + + let rrset = RRset::new(name.clone(), rtype, rclass, ttl); + + assert_eq!(rrset.get_labels(), 3); + } +} diff --git a/src/resolver_cache.rs b/src/resolver_cache.rs index fce779d1..8bbd8fc4 100644 --- a/src/resolver_cache.rs +++ b/src/resolver_cache.rs @@ -179,7 +179,7 @@ impl ResolverCache { qtype: Rrtype, qclass: Rclass, ) -> Option> { - let rr_stored_data = self.cache_answer.get(domain_name, qtype, qclass); + let rr_stored_data = self.cache_answer.get(domain_name, Some(qtype), qclass); if let Some(rr_stored_data) = rr_stored_data { let mut rr_vec = Vec::new(); @@ -199,7 +199,7 @@ impl ResolverCache { qtype: Rrtype, qclass: Rclass, ) -> Option> { - let rr_stored_data = self.cache_authority.get(domain_name, qtype, qclass); + let rr_stored_data = self.cache_authority.get(domain_name, Some(qtype), qclass); if let Some(rr_stored_data) = rr_stored_data { let mut rr_vec = Vec::new(); @@ -219,7 +219,7 @@ impl ResolverCache { qtype: Rrtype, qclass: Rclass, ) -> Option> { - let rr_stored_data = self.cache_additional.get(domain_name, qtype, qclass); + let rr_stored_data = self.cache_additional.get(domain_name, Some(qtype), qclass); if let Some(rr_stored_data) = rr_stored_data { let mut rr_vec = Vec::new(); @@ -238,7 +238,7 @@ impl ResolverCache { qtype: Rrtype, qclass: Rclass, ) -> Option { - let rr_stored_data = self.cache_answer.get(domain_name, qtype, qclass); + let rr_stored_data = self.cache_answer.get(domain_name, Some(qtype), qclass); if let Some(rr_stored_data) = rr_stored_data { Some(rr_stored_data[0].get_rcode()) @@ -515,7 +515,7 @@ mod resolver_cache_test { let rr = resolver_cache .cache_answer - .get(domain_name.clone(), Rrtype::A, Rclass::IN) + .get(domain_name.clone(), Some(Rrtype::A), Rclass::IN) .unwrap(); assert_eq!(rr[0].get_resource_record(), resource_record); @@ -547,7 +547,7 @@ mod resolver_cache_test { let rr = resolver_cache .cache_authority - .get(domain_name.clone(), Rrtype::A, Rclass::IN) + .get(domain_name.clone(), Some(Rrtype::A), Rclass::IN) .unwrap(); assert_eq!(rr[0].get_resource_record(), resource_record); @@ -579,7 +579,7 @@ mod resolver_cache_test { let rr = resolver_cache .cache_additional - .get(domain_name.clone(), Rrtype::A, Rclass::IN) + .get(domain_name.clone(), Some(Rrtype::A), Rclass::IN) .unwrap(); assert_eq!(rr[0].get_resource_record(), resource_record); @@ -647,15 +647,15 @@ mod resolver_cache_test { let rr_answer = resolver_cache .cache_answer - .get(domain_name.clone(), Rrtype::A, Rclass::IN) + .get(domain_name.clone(), Some(Rrtype::A), Rclass::IN) .unwrap(); let rr_authority = resolver_cache .cache_authority - .get(domain_name.clone(), Rrtype::A, Rclass::IN) + .get(domain_name.clone(), Some(Rrtype::A), Rclass::IN) .unwrap(); let rr_additional = resolver_cache .cache_additional - .get(domain_name.clone(), Rrtype::A, Rclass::IN) + .get(domain_name.clone(), Some(Rrtype::A), Rclass::IN) .unwrap(); assert_eq!(rr_answer[0].get_resource_record(), resource_record_1); @@ -1320,15 +1320,15 @@ mod resolver_cache_test { let rr_answer = resolver_cache .cache_answer - .get(domain_name.clone(), Rrtype::A, Rclass::IN); + .get(domain_name.clone(), Some(Rrtype::A), Rclass::IN); let rr_authority = resolver_cache .cache_authority - .get(domain_name.clone(), Rrtype::A, Rclass::IN); + .get(domain_name.clone(), Some(Rrtype::A), Rclass::IN); let rr_additional = resolver_cache .cache_additional - .get(domain_name.clone(), Rrtype::A, Rclass::IN); + .get(domain_name.clone(), Some(Rrtype::A), Rclass::IN); assert_eq!(rr_answer, None); assert_eq!(rr_authority, None); diff --git a/src/tsig.rs b/src/tsig.rs new file mode 100644 index 00000000..93b9007a --- /dev/null +++ b/src/tsig.rs @@ -0,0 +1,628 @@ +pub mod tsig_algorithm; + +use crypto::mac::MacResult; +use crate::domain_name::DomainName; +use std::time::SystemTime; +use crate::message::rclass::Rclass; +use crate::message::resource_record::{ResourceRecord, ToBytes}; + +use crate::message::{rdata::tsig_rdata::TSigRdata, DnsMessage,}; +use crate::message::rdata::Rdata; +use crypto::hmac::Hmac as crypto_hmac; +use crypto::mac::Mac as crypto_mac; +use crypto::{sha1::Sha1,sha2::Sha256}; +use tsig_algorithm::TsigAlgorithm; +use crate::message::rcode::Rcode; + + +//TODO: Encontrar alguna manera de pasar una referencia Digest u Hmac de un algoritmo no especificado +// función auxiliar para evitar la redundancia de código en sign_tsig +fn set_tsig_rd(name: String, original_id: u16, result: MacResult, + fudge: u16, time_signed: u64, mac_size: u16) -> TSigRdata{ + let mut tsig_rd: TSigRdata = TSigRdata::new(); + let mac = result.code(); + + //Convertir los bytes brutos a una cadena hexadecimal + let a_name = name.to_lowercase(); + let a_name = DomainName::new_from_string(a_name); + //añadir los valores correspondientes al tsig_rd + tsig_rd.set_algorithm_name(a_name); + tsig_rd.set_mac_size(mac_size); + tsig_rd.set_mac(mac.to_vec()); + tsig_rd.set_fudge(fudge); + tsig_rd.set_original_id(original_id); + tsig_rd.set_time_signed(time_signed); + + return tsig_rd; +} +//TODO: crear una función para simplificar la extracción de bits paa simplificar código +// This function extracts the digest +#[doc = r"This function recives a DNS message and generate the digest da. Requested by RFC 8945 4.3.3 "] +pub fn get_digest_request(mac: Vec ,dns_msg: Vec, tsig_rr: ResourceRecord) -> Vec { + let mut res: Vec = vec![]; + + if mac.len() != 0 { + let mac_len = mac.len() as u16; + let bytes_mac_len = mac_len.to_be_bytes(); + res.push(bytes_mac_len[0]); + res.push(bytes_mac_len[1]); + res.extend(mac.clone()); + } + res.extend(dns_msg.clone()); + let tsig_rdata = tsig_rr.get_rdata(); + res.extend(tsig_rr.get_name().to_bytes()); + //The below shifts are meant to correctly retreive theby + //processing TSIG RR + let rclass_bytes: u16 = Rclass::from(tsig_rr.get_rclass()).into(); + let rclass_ubyte = (rclass_bytes >> 8) as u8; + let rclass_lbyte = rclass_bytes as u8; + res.push(rclass_ubyte); + res.push(rclass_lbyte); + + let rclass_ttl: u32 = tsig_rr.get_ttl(); + let r_ttl1 = (rclass_ttl >> 24) as u8; + let r_ttl2 = (rclass_ttl >> 16) as u8; + let r_ttl3 = (rclass_ttl >> 8) as u8; + let r_ttl4 = rclass_ttl as u8; + res.push(r_ttl1); + res.push(r_ttl2); + res.push(r_ttl3); + res.push(r_ttl4); + + //processing TSIG RDATA + let tsig_rd = match tsig_rdata { + Rdata::TSIG(tsig_rd) => tsig_rd, + _ => panic!() + }; + let a_name = tsig_rd.get_algorithm_name().to_bytes(); + // Remember that time_signed is u48 + let tsig_rd_time_signed: u64 = tsig_rd.get_time_signed(); + let tsig_rd_fudge: u16 = tsig_rd.get_fudge(); + let tsig_rd_error: u16= tsig_rd.get_error(); + let tsig_rd_other_len: u16 = tsig_rd.get_other_len(); + let tsig_rd_other_data = tsig_rd.get_other_data(); + + res.extend(a_name); + + let time_s1 = (tsig_rd_time_signed >> 40) as u8; + let time_s2 = (tsig_rd_time_signed >> 32) as u8; + let time_s3 = (tsig_rd_time_signed >> 24) as u8; + let time_s4 = (tsig_rd_time_signed >> 16) as u8; + let time_s5 = (tsig_rd_time_signed >> 8) as u8; + let time_s6 = (tsig_rd_time_signed) as u8; + res.push(time_s1); + res.push(time_s2); + res.push(time_s3); + res.push(time_s4); + res.push(time_s5); + res.push(time_s6); + + let fudge1 = (tsig_rd_fudge >> 8) as u8; + let fudge2 = (tsig_rd_fudge) as u8; + res.push(fudge1); + res.push(fudge2); + + let error1 = (tsig_rd_error >> 8) as u8; + let error2 = (tsig_rd_error) as u8; + res.push(error1); + res.push(error2); + + let otherl1 = (tsig_rd_other_len >> 8) as u8; + let otherl2 = (tsig_rd_other_len) as u8; + res.push(otherl1); + res.push(otherl2); + + res.extend(tsig_rd_other_data); + + return res; +} + +fn digest(bytes: Vec, tsig_algorithm: TsigAlgorithm, key: Vec) -> Vec{ + match tsig_algorithm { + TsigAlgorithm::HmacSha1 => { + + //new_query_message.push(); + let mut hasher = crypto_hmac::new(Sha1::new(), &key); + hasher.input(&bytes[..]); + hasher.result().code().to_vec() + }, + TsigAlgorithm::HmacSha256 => { + let mut hasher = crypto_hmac::new(Sha256::new(), &key); + hasher.input(&bytes[..]); + hasher.result().code().to_vec() + } + TsigAlgorithm::UNKNOWN(a) => { + panic!("Unknown algorithm {}", a); + } + } +} + +//RFC 8945, section 5.1 +#[doc = r"This function creates the signature of a DnsMessage with a key in bytes and the algName that will be used to encrypt the key."] +pub fn sign_tsig(query_msg: &mut DnsMessage, key: &[u8], alg_name: TsigAlgorithm, + fudge: u16, time_signed: u64, key_name: String, mac_request: Vec) { + let tsig_rd: TSigRdata; + let new_query_message = query_msg.clone(); + let original_id = query_msg.get_query_id(); + let alg_name_str = String::from(alg_name.clone()); + let tsig_rr= set_tsig_vars(alg_name_str.as_str(), key_name.as_str(), + time_signed, fudge); + let digest_comp = get_digest_request(mac_request, new_query_message.to_bytes(), + tsig_rr); + match alg_name { + + TsigAlgorithm::HmacSha1 => { + + //new_query_message.push(); + let mut hasher = crypto_hmac::new(Sha1::new(), key); + hasher.input(&digest_comp[..]); + let result = hasher.result(); + tsig_rd = set_tsig_rd( + "hmac-sha1".to_lowercase(), + original_id, + result, + fudge, + time_signed, + 20); + + }, + TsigAlgorithm::HmacSha256 => { + let mut hasher = crypto_hmac::new(Sha256::new(), key); + hasher.input(&digest_comp[..]); + let result = hasher.result(); + tsig_rd = set_tsig_rd( + "hmac-sha256".to_lowercase(), + original_id, + result, + fudge, + time_signed, + 32); + + }, + TsigAlgorithm::UNKNOWN(a) => { + panic!("Unknown algorithm {}", a); + } + } + let rr_len = tsig_rd.to_bytes().len() as u16; + let mut new_rr: ResourceRecord = ResourceRecord::new(Rdata::TSIG(tsig_rd)); + new_rr.set_name(DomainName::new_from_string(key_name)); + new_rr.set_rdlength(rr_len); + let mut vec: Vec = vec![]; + vec.push(new_rr); + query_msg.add_additionals(vec); +} + +//Revisa si el nombre de la llave es correcto +fn check_key(key_in_rr:String, key_name:String)-> bool { + key_in_rr.eq(&key_name) +} + +//Verifica que el algoritmo esté disponible, y además esté implementado +fn check_alg_name(alg_name:&String, alg_list: Vec<(String,bool)>) -> bool{ + let mut answer: bool = false; + for (name,available) in alg_list { + if name.eq(alg_name){ + if available { + answer = true; + } + } + } + return answer +} + +//Verifica que los mac sean iguales +fn check_mac(new_mac: Vec, mac: Vec) -> bool{ + if mac.len()!=new_mac.len(){ + return false + } + for i in 0..mac.len(){ + if new_mac[i]!=mac[i]{ + return false + } + } + true +} + +//Verifica el error de la sección 5.2.3 +fn check_time_values(mytime: u64,fudge: u16, time: u64) -> bool { + let part1 = (time - (fudge as u64)) < mytime; + let part2 = mytime < (time+(fudge as u64)); + part1 && part2 +} + +//RFC 8945 5.2 y 5.4 +//verificar que existen los resource records que corresponden a tsig +//vector con resource records que son TSIG. Luego se Verifica si hay algún tsig rr +fn check_exists_tsig_rr(add_rec: &Vec) -> bool { + let filtered_tsig:Vec<_> = add_rec.iter() + .filter(|tsig| + if let Rdata::TSIG(_) = tsig.get_rdata() {true} + else {false}).collect(); + + filtered_tsig.len()==0 +} + + +//Debe haber un único tsig +//Tsig RR debe ser el último en la sección adicional, y debe ser único2 +fn check_last_one_is_tsig(add_rec: &Vec) -> bool { + let filtered_tsig:Vec<_> = add_rec.iter() + .filter(|tsig| + if let Rdata::TSIG(_) = tsig.get_rdata() {true} + else {false}).collect(); + + let islast = if let Rdata::TSIG(_) = add_rec[add_rec.len()-1].get_rdata() {false} else {true}; + + filtered_tsig.len()>1 || islast +} + +#[doc = r"This function process a tsig message, checking for errors in the DNS message"] +pub fn process_tsig(msg: &DnsMessage, key:&[u8], key_name: String, time: u64, + available_algorithm: Vec<(String, bool)>, mac_to_process: Vec) -> (bool, Rcode) { + let mut retmsg = msg.clone(); + let mut addit = retmsg.get_additional(); + //RFC 8945 5.2 y 5.4 + //verificar que existen los resource records que corresponden a tsig + //vector con resource records que son TSIG. Luego se Verifica si hay algún tsig rr + if check_exists_tsig_rr(&addit) { + println!("RCODE 1: FORMERR"); + return (false, Rcode::FORMERR); + } + + //Debe haber un único tsig + //Tsig RR debe ser el último en la sección adicional, y debe ser único + if check_last_one_is_tsig(&addit) { + println!("RCODE 1: FORMERR"); + return (false, Rcode::FORMERR); + } + + //sacar el último elemento del vector resource record, y disminuir elvalor de ARCOUNT + let rr_copy = addit.pop().expect("No tsig rr"); + let tsig_rr_copy: TSigRdata; + match rr_copy.get_rdata() { + Rdata::TSIG(data) =>{ + tsig_rr_copy = data; + } + _ => { + println!("error"); + unimplemented!("TODO: error code if last rr is not tsig; FORMERR") + } + } + + //RFC 8945 5.2.1 + let key_in_rr = rr_copy.get_name().get_name(); + let name_alg = tsig_rr_copy.get_algorithm_name().get_name(); + + let flag = check_alg_name(&name_alg, available_algorithm); + if !flag { + println!("RCODE 9: NOAUTH\n TSIG ERROR 17: BADKEY"); + return (false, Rcode::BADKEY); + } + + let cond1 = check_key(key_in_rr.clone(), key_name.clone()); + if !cond1 { + println!("RCODE 9: NOAUTH\n TSIG ERROR 17: BADKEY"); + println!("key in rr: {:?} key given {:?}", key_in_rr, key_name); + return (false, Rcode::BADKEY); + } + + //RFC 8945 5.2.2 + //retmsg.set_additional(addit); + let fudge = tsig_rr_copy.get_fudge(); + let time_signed = tsig_rr_copy.get_time_signed(); + let mac_received = tsig_rr_copy.get_mac(); + let mut new_alg_name: TsigAlgorithm = TsigAlgorithm::HmacSha1; + match name_alg.as_str() { + "hmac-sha1" => new_alg_name = TsigAlgorithm::HmacSha1, + "hmac-sha256" => new_alg_name = TsigAlgorithm::HmacSha256, + &_ => println!("Not supported algorithm") + } + + //let nuevo_len_arcount = addit.len() as u16; + //let mut new_header = retmsg.get_header(); + //new_header.set_arcount(nuevo_len_arcount); + //retmsg.set_header(new_header); + retmsg.set_additional(addit); + retmsg.update_header_counters(); + + // This gets the bytes to use the function and generate the digest + let bytes_to_hash = get_digest_request(mac_to_process, retmsg.to_bytes(), rr_copy); + + let new_mac = digest(bytes_to_hash, new_alg_name, key.to_vec()); + + let cond2 = check_mac(new_mac, mac_received); + + if !cond2 { + println!("RCODE 9: NOAUTH\n TSIG ERROR 16: BADSIG"); + return (false, Rcode::BADSIG) + } + //let mytime = SystemTime::now().duration_since(UNIX_EPOCH).expect("no debería fallar el tiempo"); + let cond3 = check_time_values(time, fudge, time_signed); + if !cond3 { + println!("RCODE 9: NOAUTH\n TSIG ERROR 18: BADTIME"); + return (false, Rcode::BADTIME) + } + (true, Rcode::NOERROR) + +} + +pub fn immediate_process_tsig(msg: &DnsMessage, key:&[u8], key_name: String, + available_algorithm: Vec<(String, bool)>, mac_to_process: Vec) -> (bool, Rcode) { + + let time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); + process_tsig(msg, key, key_name, time, available_algorithm, mac_to_process) +} + +//Auxiliar function to create the TSIG variables and resource recrods +#[doc= r"This function helps to set create a partial TSIG resource record on a DNS query"] +fn set_tsig_vars(alg_name: &str, name: &str, time_signed: u64, fudge: u16) -> ResourceRecord{ + //TSIG Variables + // TSIG RDATA + let mut tsig_rd: TSigRdata = TSigRdata::new(); + tsig_rd.set_algorithm_name(DomainName::new_from_str(alg_name)); + tsig_rd.set_time_signed(time_signed); + tsig_rd.set_fudge(fudge); + tsig_rd.set_error(0); + tsig_rd.set_other_len(0); + // TSIG RR + let mut tsig_rr = ResourceRecord::new(Rdata::TSIG(tsig_rd)); + tsig_rr.set_name(DomainName::new_from_str(name)); + //tsig_rr.set_rclass(Rclass::ANY); + tsig_rr.set_ttl(0); + + return tsig_rr +} + +//Sección de tests unitarios + +#[cfg(test)] +mod tsig_test { + use super::*; + use crate::message::rdata::a_rdata::ARdata; + use crate::message::rrtype::Rrtype; + + #[test] + fn check_process_tsig_exists() { + //Server process + let response = DnsMessage::new_response_message(String::from("test.com"), "NS", "IN", 1, true, 1); + //Client process + let key_name:String = "".to_string(); + let mut lista :Vec<(String, bool)> = vec![]; + let server_key = b"1234567890"; + lista.push((String::from("hmac-sha256"),true)); + let (answer, error) = process_tsig(& response, server_key, key_name, 21010, lista, vec![]); + assert!(!answer); + assert_eq!(error,Rcode::FORMERR); + } + + #[test] + fn check_process_tsig_exists2() { + //Server process + let mut response = DnsMessage::new_response_message(String::from("test.com"), "NS", "IN", 1, true, 1); + let server_key = b"1234567890"; + let alg_name = TsigAlgorithm::HmacSha256; + let alg_name2 = TsigAlgorithm::HmacSha256; + let fudge = 300; + let time_signed = 21000; + let key_name = "".to_string(); + + sign_tsig(&mut response, server_key, alg_name, fudge, time_signed, key_name.clone(), vec![]); + let mut response_capture = response.clone(); + sign_tsig(&mut response_capture, server_key, alg_name2, fudge, time_signed, key_name.clone(), vec![]); + //Client process + let key_name:String = "".to_string(); + let mut lista :Vec<(String, bool)> = vec![]; + lista.push((String::from("hmac-sha256"),true)); + let (control_answer, _) = process_tsig(& response, server_key, key_name.clone(),21010, lista.clone(), vec![]); + assert!(control_answer); + let (answer, error) = process_tsig(& response_capture, server_key, key_name, 21010, lista, vec![]); + assert!(!answer); + assert_eq!(error, Rcode::FORMERR); + } + + // verificar que no se haya añadido otro resource record en el additionals luego de añadir un tsig_rr + #[test] + fn check_process_tsig_exists3(){ + //Server process + let mut response = DnsMessage::new_response_message(String::from("test.com"), "NS", "IN", 1, true, 1); + let server_key = b"1234567890"; + let alg_name = TsigAlgorithm::HmacSha256; + let fudge = 300; + let time_signed = 21000; + let key_name = ""; + //se crea un rr TSIG que se añadirá en adittionals + sign_tsig(&mut response, server_key, alg_name, fudge, time_signed, key_name.to_string(), vec![]); + + //se agrega otro resource record en el additional... + let mut new_additional = Vec::::new(); + let a_rdata5 = Rdata::A(ARdata::new()); + let rr5 = ResourceRecord::new(a_rdata5); + new_additional.push(rr5); + response.add_additionals(new_additional); + let response_capture = response.clone(); + + //Client process + let key_name:String = "".to_string(); + let mut lista :Vec<(String, bool)> = vec![]; + lista.push((String::from("hmac-sha256"),true)); + let (answer, error) = process_tsig(& response_capture, server_key, key_name, 21010, lista, vec![]); + assert!(!answer); + assert_eq!(error, Rcode::FORMERR); + } + #[test] + fn check_process_tsig_alg_name() { + //Server process + let mut response = DnsMessage::new_response_message(String::from("test.com"), "NS", "IN", 1, true, 1); + let server_key = b"1234567890"; + let alg_name = TsigAlgorithm::HmacSha256; + let fudge = 300; + let time_signed = 21000; + let key_name = "".to_string(); + sign_tsig(&mut response, server_key, alg_name, fudge, time_signed, key_name, vec![]); + let response_capture = response.clone(); + //Client process + let key_name:String = "".to_string(); + let mut lista :Vec<(String, bool)> = vec![]; + //suponemos que hmacsha256 no está disponible + lista.push((String::from("hmac-sha1"),true)); + let (answer, error) = process_tsig(& response_capture, server_key, key_name, 21010, lista, vec![]); + assert!(!answer); + assert_eq!(error,Rcode::BADKEY); + } + #[test] + fn check_process_tsig_alg_name2() { + //Server process + let mut response = DnsMessage::new_response_message(String::from("test.com"), "NS", "IN", 1, true, 1); + let server_key = b"1234567890"; + let alg_name = TsigAlgorithm::HmacSha256; + let fudge = 300; + let time_signed = 21000; + let key_name = "".to_string(); + sign_tsig(&mut response, server_key, alg_name, fudge, time_signed, key_name, vec![]); + let response_capture = response.clone(); + //Client process + let key_name:String = "".to_string(); + let mut lista :Vec<(String, bool)> = vec![]; + //suponemos que reconocemos hmac-sha256, pero no está implementado + lista.push((String::from("hmac-sha256"),false)); + let (answer, error) = process_tsig(& response_capture, server_key, key_name, 21010, lista, vec![]); + assert!(!answer); + assert_eq!(error,Rcode::BADKEY); + } + #[test] + fn check_process_tsig_key(){ + //Server process + let mut response = DnsMessage::new_response_message(String::from("test.com"), "NS", "IN", 1, true, 1); + let server_key = b"1234567890"; + let alg_name = TsigAlgorithm::HmacSha256; + let fudge = 300; + let time_signed = 21000; + let key_name = "".to_string(); + sign_tsig(&mut response, server_key, alg_name, fudge, time_signed, key_name, vec![]); + let response_capture = response.clone(); + //Client process + let key_name:String = "different".to_string(); + let mut lista :Vec<(String, bool)> = vec![]; + //suponemos que reconocemos hmac-sha256, pero no está implementado + lista.push((String::from("hmac-sha256"),false)); + let (answer, error) = process_tsig(& response_capture, server_key, key_name, 21010, lista, vec![]); + assert!(!answer); + assert_eq!(error,Rcode::BADKEY); + } + //TODO: completar este test, hay cosas que faltan por implementar + #[test] + fn check_process_tsig_badsign(){ + // Se establece un DnsMessage de prueba. Lo firmaremos, alteraremos la firma generada y esperamos recibir un error BADSIGN + let mut msg1 = DnsMessage::new_response_message(String::from("test.com"), "NS", "IN", 1, true, 1); + let key = b"1234567890"; + let alg_name = TsigAlgorithm::HmacSha1; + let fudge = 1000; + let time_signed = 210000000; + let key_name = "".to_string(); + // se firma el mensaje con algoritmo SHA-1 + sign_tsig(& mut msg1, key, alg_name, fudge, time_signed, key_name, vec![]); + let mut lista :Vec<(String, bool)> = vec![]; + lista.push((String::from("hmac-sha1"),true)); + lista.push((String::from("hmac-sha256"),true)); + // se verifica que el mensaje está firmado, pero se usa otra key + let key_name = "".to_string(); + let key2 = b"12345678909"; + let (_, error) = process_tsig(&mut msg1, key2, key_name, time_signed,lista, vec![]); + assert_eq!(error,Rcode::BADSIG); + } + #[test] + fn check_proces_tsig_badtime(){ + //Server process + let mut response = DnsMessage::new_response_message(String::from("test.com"), "NS", "IN", 1, true, 1); + let server_key = b"1234567890"; + let alg_name = TsigAlgorithm::HmacSha256; + let fudge = 300; + let time_signed = 21000; + let key_name = "".to_string(); + sign_tsig(&mut response, server_key, alg_name, fudge, time_signed, key_name, vec![]); + let response_capture = response.clone(); + //Client process + let key_name:String = "".to_string(); + let mut lista :Vec<(String, bool)> = vec![]; + //suponemos que reconocemos hmac-sha256, pero no está implementado + lista.push((String::from("hmac-sha256"),true)); + let (answer, error) = process_tsig(& response_capture, server_key, key_name, + 22010, lista, vec![]); + assert!(!answer); + assert_eq!(error,Rcode::BADTIME); + } + #[test] + fn check_process_tsig() { + //sender process + let mut response = DnsMessage::new_response_message(String::from("test.com"), "NS", "IN", 1, true, 1); + let server_key = b"1234567890"; + let alg_name = TsigAlgorithm::HmacSha256; + let fudge = 300; + let time_signed = 21000; + let key_name = "".to_string(); + sign_tsig(&mut response, server_key, alg_name, fudge, time_signed, key_name, vec![]); + let response_capture = response.clone(); + //recv process + let key_name:String = "".to_string(); + let mut lista :Vec<(String, bool)> = vec![]; + lista.push((String::from("hmac-sha256"),true)); + let (answer, error) = process_tsig(& response_capture, server_key, key_name, + 21010, lista, vec![]); + assert!(answer); + assert_eq!(error,Rcode::NOERROR); + } + //Unitary test to verify that the signer function is working properly + #[test] + fn check_signed_tsig() { + let key = b"1234567890"; + let alg_name = TsigAlgorithm::HmacSha1; + let fudge = 0; + let time_signed = 0; + let id = 6502; + let name: String = "".to_string(); + let domain = DomainName::new_from_str("uchile.cl"); + //DNS message + let mut q = DnsMessage::new_query_message( + domain.clone(), + Rrtype::A, + Rclass::ANY, + 0, + false, + id + ); + //partial TSIG Resource record verify the signing process + let tsig_rr = set_tsig_vars(String::from(alg_name.clone()).as_str(), &name, time_signed, fudge); + let q_for_mac = q.clone(); + //creation of the signature to compare + sign_tsig(&mut q, key, alg_name, fudge, time_signed, name, vec![]); + let firma_a_comparar = q.get_mac(); + // creation of the signature digest + let dig_for_mac = get_digest_request(vec![],q_for_mac.to_bytes(), tsig_rr); + let mut hasher = crypto_hmac::new(Sha1::new(), key); + hasher.input(&dig_for_mac[..]); + + let result = hasher.result(); + let mac_to_cmp = result.code(); + + let rr = q.get_additional().pop().expect("Should be a tsig"); + match rr.get_rdata() { + Rdata::TSIG(data) => { + assert_eq!(data.get_algorithm_name(), DomainName::new_from_str("hmac-sha1")); + assert_eq!(data.get_time_signed(), time_signed); + assert_eq!(data.get_fudge() , fudge); + assert_eq!(data.get_mac_size(), 20); + assert_eq!(data.get_original_id(), id); + assert_eq!(data.get_error(), 0); + assert_eq!(data.get_other_len(), 0); + assert!(data.get_other_data().is_empty()); + }, + _ =>{ + assert!(false); + } + } + println!("Comparando el mac"); + for i in 0..mac_to_cmp.len() { + assert_eq!(mac_to_cmp[i], firma_a_comparar[i]); + } + } +} \ No newline at end of file diff --git a/src/tsig/tsig_algorithm.rs b/src/tsig/tsig_algorithm.rs new file mode 100644 index 00000000..8701bd03 --- /dev/null +++ b/src/tsig/tsig_algorithm.rs @@ -0,0 +1,27 @@ + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TsigAlgorithm { + HmacSha1, + HmacSha256, + UNKNOWN(String), +} + +impl From for String { + fn from(alg: TsigAlgorithm) -> String { + match alg { + TsigAlgorithm::HmacSha1 => "hmac-sha1".to_string(), + TsigAlgorithm::HmacSha256 => "hmac-sha256".to_string(), + TsigAlgorithm::UNKNOWN(s) => s, + } + } +} + +impl From for TsigAlgorithm { + fn from(name: String) -> TsigAlgorithm { + match name { + name if name == "hmac-sha1" => TsigAlgorithm::HmacSha1, + name if name == "hmac-sha256" => TsigAlgorithm::HmacSha256, + _ => TsigAlgorithm::UNKNOWN(name), + } + } +} \ No newline at end of file diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 1541b83c..3c6f72c3 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -1,8 +1,10 @@ + use std::{net::IpAddr, str::FromStr}; use dns_rust::{async_resolver::{config::ResolverConfig, AsyncResolver}, client::client_error::ClientError, domain_name::DomainName, message::{rclass::Rclass, rdata::Rdata, resource_record::{ResourceRecord, ToBytes}, rrtype::Rrtype, DnsMessage}}; + // TODO: Change params type to intoDomainName async fn query_response(domain_name: &str, protocol: &str, qtype: &str) -> Result, ClientError> { @@ -13,6 +15,7 @@ async fn query_response(domain_name: &str, protocol: &str, qtype: &str) -> Resul domain_name, protocol, qtype, + "IN").await; response.map(|lookup_response| lookup_response.to_vec_of_rr()) @@ -109,5 +112,3 @@ async fn no_resource_available() { assert!(response.is_err()); } - - diff --git a/tests/tsig_integration_tests.rs b/tests/tsig_integration_tests.rs new file mode 100644 index 00000000..6d656f58 --- /dev/null +++ b/tests/tsig_integration_tests.rs @@ -0,0 +1,181 @@ +use std::{collections::HashMap, net:: UdpSocket, thread, time::Duration}; +use dns_rust::{domain_name::DomainName, message::{rdata::{tsig_rdata::TSigRdata, Rdata}, rrtype::Rrtype, DnsMessage},tsig::{process_tsig, sign_tsig}}; +use dns_rust::tsig::tsig_algorithm::TsigAlgorithm; +use dns_rust::message::rcode::Rcode; +use dns_rust::message::rclass::Rclass; + + +///RFC 8945 TSIG tests +/*This tests verifies section 5.3: + When a server has generated a response to a signed request, it signs + the response using the same algorithm and key. The server MUST NOT + generate a signed response to a request if either the key is invalid + (e.g., key name or algorithm name are unknown) or the MAC fails + validation; see Section 5.3.2 for details of responding in these + cases. + + It also MUST NOT generate a signed response to an unsigned request, + except in the case of a response to a client's unsigned TKEY request + if the secret key is established on the server side after the server + processed the client's request. Signing responses to unsigned TKEY + requests MUST be explicitly specified in the description of an + individual secret key establishment algorithm [RFC3645]. + + The digest components used to generate a TSIG on a response are: + + Request MAC + DNS Message (response) + TSIG Variables (response) + + DISCLAIMER: Como no hay un "NameServer" implementado, se probó la firma y verififcaciónde TSIG utilizando + threads y scokets. La idea central es tener un thread recibiendo datos de localhost, el cual tiene en alguna + parte guardados pares (key, name) que serían utilizados para la autenticación (en este caso, se guardaron los + pares en un hash_map. Como se guarden no es relevante para tsig, depende de la implementación del servidor. + Lo único importante es que podamos buscar la llave asociada a un cierto nombre, de acuerdo a la solicitud que + recibamos). + En este test se verifica a nivel macro el correcto flujo de TSIG: primero se envia a un host en localhost + una query firmada, el cual verifica la integridad de la query y responde con un respuesta firmada que será verificada. +*/ +#[tokio::test] +async fn tsig_signature() { + // the key to test tsig flow. The server should had the same key + let key = b"1234567890"; + // global test variables + let alg_name = TsigAlgorithm::HmacSha1; + let fudge = 100; + let time_signed = 12345678; + let id = 6502; + let name = "nictest.cl"; + let mut dns_query_message = + DnsMessage::new_query_message( + DomainName::new_from_string(name.to_string()), + Rrtype::A, + Rclass::IN, + 0, + false, + id); + //lista de algoritmos disponibles. En este caso, ambos host tendrán la misma + let mut a_algs :Vec<(String, bool)> = vec![]; + a_algs.push((String::from("hmac-sha1"),true)); + a_algs.push((String::from("hmac-sha256"),true)); + + + //Código para el servidor. Recibe un mensaje firmado, lo verifica y envía una repuesta autenticada, según lo descrito en el + //RFC 8945. Este servidor tiene su propia lista de algoritmos disponibles y llaves asociadas a nombres de dominio + fn host(){ + println!("I am a host"); + //la lista de algoritmos del host + let mut list :Vec<(String, bool)> = vec![]; + list.push((String::from("hmac-sha1"),true)); + list.push((String::from("hmac-sha256"),true)); + + // se crean las llaves del servidor + let key1 = b"1234567890"; + let key2 = b"1034567692"; + // se mapean las llaves anteriores a un nombre. Acá deberemos buscar el nombre de lo que se reciba para utilizar la llave correcta + let mut keys = HashMap::new(); + keys.insert(DomainName::new_from_string("nictest.cl".to_string()), key1); + keys.insert(DomainName::new_from_string("example.cl".to_string()), key2); + + //se recibiran datos de otro thread a través de un socket UDP + let udp_socket = UdpSocket::bind("127.0.0.1:8002").expect("Failed to bind to address"); + let mut buf = [0; 512]; + + match udp_socket.recv_from(&mut buf) { + + Ok((size, source)) => { + println!("Received {} bytes from {}", size, source); + let mut data = DnsMessage::from_bytes(&buf[0..size]).unwrap(); + println!("The data is {:?}", data); + let mut addit = data.get_additional(); + let rr = addit.pop().expect("No tsigrr"); + let mut tsig_rd = TSigRdata::new(); + // let mut can_sign = false; + + match rr.get_rdata() { + Rdata::TSIG(data) =>{ + tsig_rd = data; + } + _ => { + //can_sign = true; + println!("error: no TSIG rdata found!"); + } + } + //se extraen las variables TSIG necesarias. + let alg_name = tsig_rd.get_algorithm_name().get_name(); + let time =tsig_rd.get_time_signed(); + let fudge = tsig_rd.get_fudge(); + let mac = tsig_rd.get_mac(); + let name = rr.get_name(); + let key_name = name.clone().get_name(); + // se extrae la llave necesaria + let key_found = keys[&name]; + + //el servidor verifica la estructura del tsig recibido. Sumamos un pequeño delay al time para simular retraso + let (_,error) = process_tsig(&data, key_found, key_name.clone(), time + 50, list, vec![]); + //se setea el aditional sin el ultimo resource record, para que sign_tsig lo regenere + data.set_additional(addit); + data.update_header_counters(); + // se firma el mensaje recibido con el digest de la respuesta. Notar que el vector final ahora no está vacío + sign_tsig(&mut data, key_found,TsigAlgorithm::from(alg_name),fudge,time, key_name, mac); + let response = &DnsMessage::to_bytes(&data); + //se verifica que la request haya pasado proces_tsig + assert_eq!(error,Rcode::NOERROR); + + // se envia la respuesta si lo anterior resultó ser correcto + udp_socket + .send_to(&response, source) + .expect("Failed to send response"); + + } + Err(e) => { + eprintln!("Error receiving data: {}", e); + + } + } + + } + + //Lanzamiento de threads + println!("Starting server"); + let server_handle = thread::spawn(|| { + host(); + + }); + thread::sleep(Duration::from_secs(2)); + + // se instancia un socket cliente que enviará y mensajes + let client_sock = UdpSocket::bind("127.0.0.1:8001").expect("Nothing"); + // El cliente firma el mensaje para enviar al servidor. Se guarda el mac de la firma + sign_tsig(&mut dns_query_message, key, alg_name, fudge, time_signed, name.to_string(), vec![]); + let mac = dns_query_message.get_mac(); + let buf = dns_query_message.to_bytes(); + client_sock.send_to(&buf,"127.0.0.1:8002").unwrap(); + println!("Mensaje enviado"); + server_handle.join().unwrap(); + let mut buf = [0; 512]; + + // Ahora el cliente verifica la respuesta recibida del servidor + match client_sock.recv_from(&mut buf) { + + Ok((size, source)) => { + println!("Received {} bytes from {}", size, source); + let data = DnsMessage::from_bytes(&buf[0..size]).unwrap(); + println!("The data is {:?}", data); + + + // El cliente procesa la respuesta + let (answer, error ) = process_tsig(&data, key, name.to_string(), time_signed, a_algs, mac); + // se verifica que el mensaje haya pasado process_tsig + assert!(answer); + assert_eq!(error,Rcode::NOERROR); + } + Err(e) => { + eprintln!("Error receiving data: {}", e); + + } + } + + +} + \ No newline at end of file diff --git a/tests/use_case/example_with_bind9/Cargo.toml b/tests/use_case/example_with_bind9/Cargo.toml new file mode 100644 index 00000000..6242bf72 --- /dev/null +++ b/tests/use_case/example_with_bind9/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "ej_rust" +version = "0.1.0" +edition = "2021" + +[dependencies] +dns_rust = { path="../dns-rust"} +base64 = "0.22.1" diff --git a/tests/use_case/example_with_bind9/README.md b/tests/use_case/example_with_bind9/README.md new file mode 100644 index 00000000..d6c62284 --- /dev/null +++ b/tests/use_case/example_with_bind9/README.md @@ -0,0 +1,27 @@ +# Demo TSIG +Esta es la demo para tsig, es un proxy que retransmite los mensajes a el server, corre con dos pc's en la ip 192.168.100.(2|3)/24, idealmente usen un router y configuren las ip's de manera estatica. +- cliente es el 100.2, con los puertos 8887 y 8890 +- server es el 100.3 + +La llave que comparten esta en el archivo llave.key + +## recv_dig +Aqui la demo funciona como un proxy, recive un mensaje del dig, lo procesa y despues recive el mensaje de bind9, lo procesa y lo retransmitee (util para testear si funciona el parseo y la verficacion de tsig) + +comando para correr tsig con dig, ejecutar el main y en otra terminal ejecutar + +```bash +dig -p8887 @127.0.0.1 ns1.nictest -k wena.key +``` + +Y luego correr el ejemplo + +## recv_without_dig + +Esto es la demo que se vio en la presentacion. Este genera un mensaje preguntando por ns1.nictest (dominio configurado en bind9) y luego usa las funciones sign y process para verificar el tsig, lo envia, y verifica la respuesta del servidor + +Aqui es solo correr el ejemplo + +```bash +cargo run +``` diff --git a/tests/use_case/example_with_bind9/llave.key b/tests/use_case/example_with_bind9/llave.key new file mode 100644 index 00000000..054672bd --- /dev/null +++ b/tests/use_case/example_with_bind9/llave.key @@ -0,0 +1,4 @@ +key "WEIRD.NICTEST" { + algorithm hmac-sha1; + secret "7niAlAtSA70XRNgvlAB5m80ywDA="; +}; diff --git a/tests/use_case/example_with_bind9/src/main.rs b/tests/use_case/example_with_bind9/src/main.rs new file mode 100644 index 00000000..442bebd9 --- /dev/null +++ b/tests/use_case/example_with_bind9/src/main.rs @@ -0,0 +1,153 @@ +use std::net::UdpSocket; +use std::time::SystemTime; +use std::vec; +use dns_rust::domain_name::DomainName; +use dns_rust::message::rdata::Rdata; +use dns_rust::message::DnsMessage; +use dns_rust::message::{rrtype::Rrtype, rclass::Rclass}; +use dns_rust; +use base64; +use base64::Engine as _; +use dns_rust::tsig::{process_tsig, sign_tsig, TsigAlgorithm}; +use std::io::{stdin, stdout, Write}; +use std::{thread, time}; + + + +pub fn input(prompt: &str) -> String { + print!("{}", prompt); + let mut input = String::new(); + + stdout().flush().expect("Failed to flush stdout!"); + stdin().read_line(&mut input).expect("Failed to read line"); + + input.pop(); + + return input; +} +const KEY: &[u8; 28] = b"7niAlAtSA70XRNgvlAB5m80ywDA="; +//const KEY: &[u8; 28] = b"8niAlAtSA70XRNgvlAB5m80ywDA="; + +fn generate_tsig_a_query(domain :DomainName, id: u16, key_name: String, key: &[u8]) -> (DnsMessage, Vec) { + let mut dnsmsg = DnsMessage::new_query_message(domain, Rrtype::A, Rclass::IN, 0, true, id); + let mut header = dnsmsg.get_header(); + header.set_ad(true); + dnsmsg.set_header(header); + let alg_name = TsigAlgorithm::HmacSha1; + let time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); + let digest = sign_tsig(&mut dnsmsg, key, alg_name, 300, time, key_name, vec![]); + return (dnsmsg, digest); +} + + +fn recv_without_dig(){ + let three_secs = time::Duration::from_secs(4); + + let key_bytes = base64::prelude::BASE64_STANDARD.decode(KEY).unwrap(); + let mut lista_alg = vec![]; + lista_alg.push((String::from("hmac-sha1"),true)); + let domain_to_query = DomainName::new_from_str("ns1.nictest"); + let shared_key_name = "weird.nictest".to_string(); + let socket_udp = UdpSocket::bind("192.168.100.2:8890").expect("Failed to bind to address"); + println!("----------------------------------------------------------------"); + input("Generemos un mensaje con TSIG, presione enter para continuar\n"); + let (dns_msg, mac) = generate_tsig_a_query(domain_to_query, 6502, shared_key_name.clone(), &key_bytes); + + let time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); + let (val, err) = process_tsig(&dns_msg, &key_bytes, shared_key_name.clone(), time, lista_alg.clone(), vec![]); + println!("{}", &dns_msg); + if !val { + println!("Error en la validacion del mensaje"); + println!("{:?}", err); + panic!("Error en la validacion del mensaje"); + } + input("Presione enter para validar la consulta del cliente con tsig"); + println!("Validacion de la peticion OK! tsig_err {:?}", err); + println!("----------------------------------------------------------------"); + input("Presione enter para enviar el mensaje al servidor"); + println!("Enviando el mensaje al servidor..."); + thread::sleep(three_secs); + let test_bytes = dns_msg.to_bytes(); + socket_udp.send_to(&test_bytes, "192.168.100.3:53").unwrap(); + + let mut buf = [0; 2000]; + let (s, _) = socket_udp.recv_from(& mut buf).unwrap(); + println!("Recibiendo respuesta del servidor\n"); + let bytes = &buf[0..s].to_vec(); + let response = DnsMessage::from_bytes(&bytes).expect("Parseo mal!"); + println!("{}\n", &response); + input("Presione enter para validar la respuesta del servidor"); + let time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); + let (val, err) = process_tsig(&response, &key_bytes, shared_key_name.clone(), time, lista_alg, mac); + + + if !val { + println!("Error en la validacion del mensaje"); + println!("tsig_error_code: {:?}", err); + } + println!("----------------------------------------------------------------"); +} + +fn recv_dig() { + let key_bytes = base64::prelude::BASE64_STANDARD.decode(KEY).unwrap(); + let mut lista_alg = vec![]; + lista_alg.push((String::from("hmac-sha1"),true)); + + let socket_udp = UdpSocket::bind("127.0.0.1:8887").expect("Failed to bind to address"); + let socket_udp2 = UdpSocket::bind("192.168.100.2:8890").expect("Failed to bind to address"); + let mut buf = [0;1000]; + let (s, addr_in) = socket_udp.recv_from(&mut buf).unwrap(); + //println!("Llego un mensaje de largo {s}"); + let bytes = &buf[0..s].to_vec(); + let dnsmsg = DnsMessage::from_bytes(bytes).expect("Parseo mal!"); + + let time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); + let mac = vec![]; + let (a, b)= process_tsig(&dnsmsg, &key_bytes, "weird.nictest".to_string(), time, lista_alg.clone(), mac); + + println!("Verificando la query del cliente!"); + println!("bool: {:?} tsig_err: {:#?}", a, b); + println!("{:#?}",&dnsmsg); + println!("-----------------------------------------------------"); + + // println!("{:#?}", dnsmsg.get_header()); + let rrs = dnsmsg.get_additional().pop().unwrap(); + let tsig = match rrs.get_rdata() { + Rdata::TSIG(xd) => { + xd + }, + _ => panic!("xd") + }; + + + let mac = tsig.get_mac(); + let test_bytes = dnsmsg.to_bytes(); + + socket_udp2.send_to(&test_bytes, "192.168.100.3:53").unwrap(); + + let mut buf2 = [0; 2000]; + let (s2, _) = socket_udp2.recv_from(& mut buf2).unwrap(); + let bytes2 = &buf2[0..s2].to_vec(); + let dnsmsg2 = DnsMessage::from_bytes(&bytes2[0..s2]).expect("Parseo mal!"); + + // let mut response_dns_tsig_file = File::create("response_tsig_cliente.dns").unwrap(); + // response_dns_tsig_file.write_all(bytes2).expect("Error al escribir el archivo"); + + let parsed_bytes = dnsmsg2.to_bytes(); + + socket_udp.send_to(&parsed_bytes, addr_in).unwrap(); + //process_tsig(&dnsmsg2, key, key_name, time, available_algorithm, mac_to_process) + + + //panic!(); + //let bytes = general_purpose::STANDARD.decode(key).unwrap(); + let time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); + let (a, b)= process_tsig(&dnsmsg2, &key_bytes, "weird.nictest".to_string(), time, lista_alg, mac); + println!("Verificando la respuesta del servidor"); + println!("bool: {:?} tsig_err: {:#?}", a, b); +} + +fn main() { + //recv_dig(); + recv_without_dig() +} \ No newline at end of file