hickory-client-0.24.4/.cargo_vcs_info.json0000644000000001530000000000100140460ustar { "git": { "sha1": "b00cc1052cf769250063005e9fa268519522778f" }, "path_in_vcs": "crates/client" }hickory-client-0.24.4/Cargo.lock0000644000001275770000000000100120450ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "addr2line" version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] name = "adler2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "async-trait" version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", "windows-targets 0.52.6", ] [[package]] name = "base64" version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bumpalo" version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cc" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" dependencies = [ "shlex", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "core-foundation" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "data-encoding" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "displaydoc" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "endian-type" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "enum-as-inner" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" dependencies = [ "heck", "proc-macro2", "quote", "syn", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", "windows-sys 0.59.0", ] [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ "foreign-types-shared", ] [[package]] name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "futures" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-sink" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "getrandom" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "h2" version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", "futures-core", "futures-sink", "futures-util", "http", "indexmap", "slab", "tokio", "tokio-util", "tracing", ] [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hickory-client" version = "0.24.4" dependencies = [ "cfg-if", "data-encoding", "futures", "futures-channel", "futures-util", "hickory-proto", "once_cell", "openssl", "radix_trie", "rand", "rustls", "serde", "thiserror", "tokio", "tracing", "tracing-subscriber", ] [[package]] name = "hickory-proto" version = "0.24.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248" dependencies = [ "async-trait", "backtrace", "bytes", "cfg-if", "data-encoding", "enum-as-inner", "futures-channel", "futures-io", "futures-util", "h2", "http", "idna", "ipnet", "native-tls", "once_cell", "openssl", "quinn", "rand", "ring 0.17.8", "rustls", "rustls-native-certs", "rustls-pemfile", "serde", "socket2", "thiserror", "tinyvec", "tokio", "tokio-native-tls", "tokio-rustls", "tracing", "url", "webpki-roots", ] [[package]] name = "http" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", "itoa", ] [[package]] name = "icu_collections" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ "displaydoc", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_locid" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "displaydoc", "litemap", "tinystr", "writeable", "zerovec", ] [[package]] name = "icu_locid_transform" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" dependencies = [ "displaydoc", "icu_locid", "icu_locid_transform_data", "icu_provider", "tinystr", "zerovec", ] [[package]] name = "icu_locid_transform_data" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" [[package]] name = "icu_normalizer" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" dependencies = [ "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", "utf16_iter", "utf8_iter", "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" [[package]] name = "icu_properties" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" dependencies = [ "displaydoc", "icu_collections", "icu_locid_transform", "icu_properties_data", "icu_provider", "tinystr", "zerovec", ] [[package]] name = "icu_properties_data" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" [[package]] name = "icu_provider" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" dependencies = [ "displaydoc", "icu_locid", "icu_provider_macros", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_provider_macros" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "idna" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ "idna_adapter", "smallvec", "utf8_iter", ] [[package]] name = "idna_adapter" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ "icu_normalizer", "icu_properties", ] [[package]] name = "indexmap" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "ipnet" version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "itoa" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "linux-raw-sys" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "litemap" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "matchers" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ "regex-automata 0.1.10", ] [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ "adler2", ] [[package]] name = "mio" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", "wasi", "windows-sys 0.52.0", ] [[package]] name = "native-tls" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ "libc", "log", "openssl", "openssl-probe", "openssl-sys", "schannel", "security-framework", "security-framework-sys", "tempfile", ] [[package]] name = "nibble_vec" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" dependencies = [ "smallvec", ] [[package]] name = "nu-ansi-term" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ "overload", "winapi", ] [[package]] name = "object" version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "openssl" version = "0.10.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" dependencies = [ "bitflags", "cfg-if", "foreign-types", "libc", "once_cell", "openssl-macros", "openssl-sys", ] [[package]] name = "openssl-macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" version = "0.9.105" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" dependencies = [ "cc", "libc", "pkg-config", "vcpkg", ] [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "ppv-lite86" version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ "zerocopy", ] [[package]] name = "proc-macro2" version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "quinn" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75" dependencies = [ "bytes", "pin-project-lite", "quinn-proto", "quinn-udp", "rustc-hash", "rustls", "thiserror", "tokio", "tracing", ] [[package]] name = "quinn-proto" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "141bf7dfde2fbc246bfd3fe12f2455aa24b0fbd9af535d8c86c7bd1381ff2b1a" dependencies = [ "bytes", "rand", "ring 0.16.20", "rustc-hash", "rustls", "slab", "thiserror", "tinyvec", "tracing", ] [[package]] name = "quinn-udp" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" dependencies = [ "bytes", "libc", "socket2", "tracing", "windows-sys 0.48.0", ] [[package]] name = "quote" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] name = "radix_trie" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" dependencies = [ "endian-type", "nibble_vec", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", "regex-automata 0.4.9", "regex-syntax 0.8.5", ] [[package]] name = "regex-automata" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ "regex-syntax 0.6.29", ] [[package]] name = "regex-automata" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", "regex-syntax 0.8.5", ] [[package]] name = "regex-syntax" version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "ring" version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ "cc", "libc", "once_cell", "spin 0.5.2", "untrusted 0.7.1", "web-sys", "winapi", ] [[package]] name = "ring" version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", "getrandom", "libc", "spin 0.9.8", "untrusted 0.9.0", "windows-sys 0.52.0", ] [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys 0.59.0", ] [[package]] name = "rustls" version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring 0.17.8", "rustls-webpki", "sct", ] [[package]] name = "rustls-native-certs" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", "rustls-pemfile", "schannel", "security-framework", ] [[package]] name = "rustls-pemfile" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ "base64", ] [[package]] name = "rustls-webpki" version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ "ring 0.17.8", "untrusted 0.9.0", ] [[package]] name = "schannel" version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "sct" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ "ring 0.17.8", "untrusted 0.9.0", ] [[package]] name = "security-framework" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags", "core-foundation", "core-foundation-sys", "libc", "security-framework-sys", ] [[package]] name = "security-framework-sys" version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "serde" version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "sharded-slab" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "syn" version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "synstructure" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tempfile" version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", "once_cell", "rustix", "windows-sys 0.59.0", ] [[package]] name = "thiserror" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "thread_local" version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", ] [[package]] name = "tinystr" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", "zerovec", ] [[package]] name = "tinyvec" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", "libc", "mio", "pin-project-lite", "socket2", "tokio-macros", "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tokio-native-tls" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", ] [[package]] name = "tokio-rustls" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ "rustls", "tokio", ] [[package]] name = "tokio-util" version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", ] [[package]] name = "tracing" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tracing-core" version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", ] [[package]] name = "tracing-log" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ "log", "once_cell", "tracing-core", ] [[package]] name = "tracing-subscriber" version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", "once_cell", "regex", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", ] [[package]] name = "unicode-ident" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "untrusted" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", ] [[package]] name = "utf16_iter" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" [[package]] name = "utf8_iter" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "web-sys" version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "webpki-roots" version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" 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-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "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.6", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "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.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[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.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[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.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[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.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[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.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[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.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[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.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "write16" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] name = "writeable" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "yoke" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", "yoke-derive", "zerofrom", ] [[package]] name = "yoke-derive" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "zerofrom" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "zerovec" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" dependencies = [ "yoke", "zerofrom", "zerovec-derive", ] [[package]] name = "zerovec-derive" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", "syn", ] hickory-client-0.24.4/Cargo.toml0000644000000101540000000000100120460ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.71.1" name = "hickory-client" version = "0.24.4" authors = ["The contributors to Hickory DNS"] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = """ Hickory DNS is a safe and secure DNS library. This is the Client library with DNSSEC support. DNSSEC with NSEC validation for negative records, is complete. The client supports dynamic DNS with SIG0 authenticated requests, implementing easy to use high level funtions. Hickory DNS is based on the Tokio and Futures libraries, which means it should be easily integrated into other software that also use those libraries. """ homepage = "https://hickory-dns.org/" documentation = "https://docs.rs/hickory-client" readme = "README.md" keywords = [ "DNS", "BIND", "dig", "named", "dnssec", ] categories = ["network-programming"] license = "MIT OR Apache-2.0" repository = "https://github.com/hickory-dns/hickory-dns" [package.metadata.docs.rs] all-features = true default-target = "x86_64-unknown-linux-gnu" rustdoc-args = [ "--cfg", "docsrs", ] targets = [ "x86_64-apple-darwin", "x86_64-pc-windows-msvc", ] [lib] name = "hickory_client" path = "src/lib.rs" [dependencies.cfg-if] version = "1" [dependencies.data-encoding] version = "2.2.0" [dependencies.futures-channel] version = "0.3.5" features = ["std"] default-features = false [dependencies.futures-util] version = "0.3.5" features = ["std"] default-features = false [dependencies.hickory-proto] version = "0.24.0" features = [ "text-parsing", "tokio-runtime", ] default-features = false [dependencies.once_cell] version = "1.18.0" [dependencies.radix_trie] version = "0.2.0" [dependencies.rand] version = "0.8" [dependencies.rustls] version = "0.21.6" optional = true [dependencies.serde] version = "1.0" features = ["derive"] optional = true [dependencies.thiserror] version = "1.0.20" [dependencies.tokio] version = "1.21" features = [ "rt", "net", ] [dependencies.tracing] version = "0.1.30" [dev-dependencies.futures] version = "0.3.5" features = [ "std", "executor", ] default-features = false [dev-dependencies.openssl] version = "0.10.55" features = [ "v102", "v110", ] optional = false [dev-dependencies.tokio] version = "1.21" features = [ "rt", "macros", ] [dev-dependencies.tracing-subscriber] version = "0.3" features = [ "std", "fmt", "env-filter", ] [features] backtrace = ["hickory-proto/backtrace"] dns-over-https = ["hickory-proto/dns-over-https"] dns-over-https-openssl = [ "dns-over-https", "dns-over-openssl", ] dns-over-https-rustls = [ "dns-over-https", "dns-over-rustls", "rustls", "hickory-proto/dns-over-https-rustls", ] dns-over-native-tls = [ "dns-over-tls", "hickory-proto/dns-over-native-tls", ] dns-over-openssl = [ "dns-over-tls", "dnssec-openssl", ] dns-over-quic = [ "dns-over-rustls", "hickory-proto/dns-over-quic", ] dns-over-rustls = [ "dns-over-tls", "dnssec-ring", "hickory-proto/dns-over-rustls", ] dns-over-tls = [] dnssec = ["hickory-proto/dnssec"] dnssec-openssl = [ "dnssec", "hickory-proto/dnssec-openssl", ] dnssec-ring = [ "dnssec", "hickory-proto/dnssec-ring", ] mdns = ["hickory-proto/mdns"] native-certs = ["hickory-proto/native-certs"] serde-config = [ "serde", "hickory-proto/serde-config", ] webpki-roots = ["hickory-proto/webpki-roots"] [badges.codecov] branch = "main" repository = "hickory-dns/hickory-dns" service = "github" [badges.maintenance] status = "actively-developed" [lints.rust.unexpected_cfgs] level = "warn" priority = 0 check-cfg = ["cfg(nightly)"] hickory-client-0.24.4/Cargo.toml.orig000064400000000000000000000070411046102023000155300ustar 00000000000000[package] name = "hickory-client" # A short blurb about the package. This is not rendered in any format when # uploaded to crates.io (aka this is not markdown) description = """ Hickory DNS is a safe and secure DNS library. This is the Client library with DNSSEC support. DNSSEC with NSEC validation for negative records, is complete. The client supports dynamic DNS with SIG0 authenticated requests, implementing easy to use high level funtions. Hickory DNS is based on the Tokio and Futures libraries, which means it should be easily integrated into other software that also use those libraries. """ # These URLs point to more information about the repository documentation = "https://docs.rs/hickory-client" # This points to a file in the repository (relative to this Cargo.toml). The # contents of this file are stored and indexed in the registry. readme = "README.md" version.workspace = true authors.workspace = true edition.workspace = true rust-version.workspace = true homepage.workspace = true repository.workspace = true keywords.workspace = true categories.workspace = true license.workspace = true [badges] #github-actions = { repository = "bluejekyll/hickory", branch = "main", workflow = "test" } codecov = { repository = "hickory-dns/hickory-dns", branch = "main", service = "github" } maintenance = { status = "actively-developed" } [features] backtrace = ["hickory-proto/backtrace"] dns-over-https-openssl = ["dns-over-https", "dns-over-openssl"] dns-over-https-rustls = [ "dns-over-https", "dns-over-rustls", "rustls", "hickory-proto/dns-over-https-rustls", ] dns-over-https = ["hickory-proto/dns-over-https"] dns-over-quic = ["dns-over-rustls", "hickory-proto/dns-over-quic"] dns-over-native-tls = ["dns-over-tls", "hickory-proto/dns-over-native-tls"] dns-over-openssl = ["dns-over-tls", "dnssec-openssl"] dns-over-rustls = [ "dns-over-tls", "dnssec-ring", "hickory-proto/dns-over-rustls", ] dns-over-tls = [] webpki-roots = ["hickory-proto/webpki-roots"] native-certs = ["hickory-proto/native-certs"] dnssec-openssl = ["dnssec", "hickory-proto/dnssec-openssl"] dnssec-ring = ["dnssec", "hickory-proto/dnssec-ring"] dnssec = ["hickory-proto/dnssec"] serde-config = ["serde", "hickory-proto/serde-config"] # enables experimental the mDNS (multicast) feature mdns = ["hickory-proto/mdns"] [lib] name = "hickory_client" path = "src/lib.rs" [dependencies] cfg-if.workspace = true data-encoding.workspace = true futures-channel = { workspace = true, default-features = false, features = [ "std", ] } futures-util = { workspace = true, default-features = false, features = [ "std", ] } once_cell.workspace = true radix_trie.workspace = true rand.workspace = true rustls = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"], optional = true } thiserror.workspace = true tracing.workspace = true tokio = { workspace = true, features = ["rt", "net"] } hickory-proto = { workspace = true, features = [ "text-parsing", "tokio-runtime", ] } [dev-dependencies] futures = { workspace = true, default-features = false, features = [ "std", "executor", ] } openssl = { workspace = true, features = ["v102", "v110"], optional = false } tokio = { workspace = true, features = ["rt", "macros"] } tracing-subscriber = { workspace = true, features = [ "std", "fmt", "env-filter", ] } [package.metadata.docs.rs] all-features = true default-target = "x86_64-unknown-linux-gnu" targets = ["x86_64-apple-darwin", "x86_64-pc-windows-msvc"] rustdoc-args = ["--cfg", "docsrs"] [lints] workspace = true hickory-client-0.24.4/LICENSE-APACHE000064400000000000000000000261401046102023000145660ustar 00000000000000 Apache License Version 2.0, January 2004 https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. hickory-client-0.24.4/LICENSE-MIT000064400000000000000000000021151046102023000142720ustar 00000000000000Copyright (c) 2015 The hickory-dns Developers Copyright (c) 2017 Google LLC. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. hickory-client-0.24.4/README.md000064400000000000000000000141111046102023000141140ustar 00000000000000# Overview Hickory DNS is a library which implements the DNS protocol and client side functions. This library contains basic implementations for DNS record serialization, and communication. It is capable of performing `query`, `update`, and `notify` operations. `update` has been proven to be compatible with `BIND9` and `SIG0` signed records for updates. It is built on top of the [tokio](https://tokio.rs) async-io project, this allows it to be integrated into other systems using the tokio and futures libraries. The Hickory DNS [project](https://github.com/hickory-dns/hickory-dns) contains other libraries for DNS: a [resolver library](https://crates.io/crates/hickory-resolver) for lookups, a [server library](https://crates.io/crates/hickory-dns) for hosting zones, and variations on the TLS implementation over [rustls](https://crates.io/crates/hickory-dns-rustls) and [native-tls](https://crates.io/crates/hickory-dns-native-tls). **NOTICE** This project was rebranded from Trust-DNS to Hickory DNS and has been moved to the https://github.com/hickory-dns/hickory-dns organization and repo, this crate/binary has been moved to [hickory-client](https://crates.io/crates/hickory-client), from `0.24` and onward, for prior versions see [trust-dns-client](https://crates.io/crates/trust-dns-client). ## Featuress The `client` is capable of DNSSEC validation as well as offering higher order functions for performing DNS operations: - [SyncDnssecClient](https://docs.rs/hickory-client/latest/hickory_client/client/struct.SyncDnssecClient.html) - DNSSEC validation - [create](https://docs.rs/hickory-client/latest/hickory_client/client/trait.Client.html#method.create) - atomic create of a record, with authenticated request - [append](https://docs.rs/hickory-client/latest/hickory_client/client/trait.Client.html#method.append) - verify existence of a record and append to it - [compare_and_swap](https://docs.rs/hickory-client/latest/hickory_client/client/trait.Client.html#method.compare_and_swap) - atomic (depends on server) compare and swap - [delete_by_rdata](https://docs.rs/hickory-client/latest/hickory_client/client/trait.Client.html#method.delete_by_rdata) - delete a specific record - [delete_rrset](https://docs.rs/hickory-client/latest/hickory_client/client/trait.Client.html#method.delete_rrset) - delete an entire record set - [delete_all](https://docs.rs/hickory-client/latest/hickory_client/client/trait.Client.html#method.delete_all) - delete all records sets with a given name - [notify](https://docs.rs/hickory-client/latest/hickory_client/client/trait.Client.html#method.notify) - notify server that it should reload a zone ## Example ```rust use std::net::Ipv4Addr; use std::str::FromStr; use hickory_client::client::{Client, SyncClient}; use hickory_client::udp::UdpClientConnection; use hickory_client::op::DnsResponse; use hickory_client::rr::{DNSClass, Name, RData, Record, RecordType}; let address = "8.8.8.8:53".parse().unwrap(); let conn = UdpClientConnection::new(address).unwrap(); let client = SyncClient::new(conn); // Specify the name, note the final '.' which specifies it's an FQDN let name = Name::from_str("www.example.com.").unwrap(); // NOTE: see 'Setup a connection' example above // Send the query and get a message response, see RecordType for all supported options let response: DnsResponse = client.query(&name, DNSClass::IN, RecordType::A).unwrap(); // Messages are the packets sent between client and server in DNS, DnsResonse's can be // dereferenced to a Message. There are many fields to a Message, It's beyond the scope // of these examples to explain them. See hickory_dns::op::message::Message for more details. // generally we will be interested in the Message::answers let answers: &[Record] = response.answers(); // Records are generic objects which can contain any data. // In order to access it we need to first check what type of record it is // In this case we are interested in A, IPv4 address if let Some(RData::A(ref ip)) = answers[0].data() { assert_eq!(*ip, Ipv4Addr::new(93, 184, 215, 14)) } else { assert!(false, "unexpected result") } ``` ## DNS-over-TLS and DNS-over-HTTPS DoT and DoH are supported. This is accomplished through the use of one of `native-tls`, `openssl`, or `rustls` (only `rustls` is currently supported for DoH). To use with the `Client`, the `TlsClientConnection` or `HttpsClientConnection` should be used. Similarly, to use with the tokio `AsyncClient` the `TlsClientStream` or `HttpsClientStream` should be used. ClientAuth, mTLS, is currently not supported, there are some issues still being worked on. TLS is useful for Server authentication and connection privacy. To enable DoT one of the features `dns-over-native-tls`, `dns-over-openssl`, or `dns-over-rustls` must be enabled, `dns-over-https-rustls` is used for DoH. ## DNSSEC status Currently the root key is hardcoded into the system. This gives validation of DNSKEY and DS records back to the root. NSEC is implemented, but not NSEC3. Because caching is not yet enabled, it has been noticed that some DNS servers appear to rate limit the connections, validating RRSIG records back to the root can require a significant number of additional queries for those records. Zones will be automatically resigned on any record updates via dynamic DNS. To enable DNSSEC, one of the features `dnssec-openssl` or `dnssec-ring` must be enabled. ## Minimum Rust Version The current minimum rustc version for this project is `1.67` ## Versioning Hickory DNS does it's best job to follow semver. Hickory DNS will be promoted to 1.0 upon stabilization of the publicly exposed APIs. This does not mean that Hickory DNS will necessarily break on upgrades between 0.x updates. Whenever possible, old APIs will be deprecated with notes on what replaced those deprecations. Hickory DNS will make a best effort to never break software which depends on it due to API changes, though this can not be guaranteed. Deprecated interfaces will be maintained for at minimum one major release after that in which they were deprecated (where possible), with the exception of the upgrade to 1.0 where all deprecated interfaces will be planned to be removed. hickory-client-0.24.4/src/client/async_client.rs000064400000000000000000001217711046102023000177360ustar 00000000000000// Copyright 2015-2023 Benjamin Fry // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. use std::{ future::Future, pin::Pin, sync::Arc, task::{Context, Poll}, time::Duration, }; use futures_util::{ ready, stream::{Stream, StreamExt}, }; use rand; use tracing::debug; use crate::{ client::Signer, error::*, op::{Message, MessageType, OpCode, Query}, proto::{ error::{ProtoError, ProtoErrorKind}, op::{update_message, Edns}, xfer::{ BufDnsStreamHandle, DnsClientStream, DnsExchange, DnsExchangeBackground, DnsExchangeSend, DnsHandle, DnsMultiplexer, DnsRequest, DnsRequestOptions, DnsRequestSender, DnsResponse, }, TokioTime, }, rr::{rdata::SOA, DNSClass, Name, RData, Record, RecordSet, RecordType}, }; /// A DNS Client implemented over futures-rs. /// /// This Client is generic and capable of wrapping UDP, TCP, and other underlying DNS protocol /// implementations. pub type ClientFuture = AsyncClient; /// A DNS Client implemented over futures-rs. /// /// This Client is generic and capable of wrapping UDP, TCP, and other underlying DNS protocol /// implementations. #[derive(Clone)] pub struct AsyncClient { exchange: DnsExchange, use_edns: bool, } impl AsyncClient { /// Spawns a new AsyncClient Stream. This uses a default timeout of 5 seconds for all requests. /// /// # Arguments /// /// * `stream` - A stream of bytes that can be used to send/receive DNS messages /// (see TcpClientStream or UdpClientStream) /// * `stream_handle` - The handle for the `stream` on which bytes can be sent/received. /// * `signer` - An optional signer for requests, needed for Updates with Sig0, otherwise not needed #[allow(clippy::new_ret_no_self)] pub async fn new( stream: F, stream_handle: BufDnsStreamHandle, signer: Option>, ) -> Result< ( Self, DnsExchangeBackground, TokioTime>, ), ProtoError, > where F: Future> + Send + Unpin + 'static, S: DnsClientStream + 'static + Unpin, { Self::with_timeout(stream, stream_handle, Duration::from_secs(5), signer).await } /// Spawns a new AsyncClient Stream. /// /// # Arguments /// /// * `stream` - A stream of bytes that can be used to send/receive DNS messages /// (see TcpClientStream or UdpClientStream) /// * `timeout_duration` - All requests may fail due to lack of response, this is the time to /// wait for a response before canceling the request. /// * `stream_handle` - The handle for the `stream` on which bytes can be sent/received. /// * `signer` - An optional signer for requests, needed for Updates with Sig0, otherwise not needed pub async fn with_timeout( stream: F, stream_handle: BufDnsStreamHandle, timeout_duration: Duration, signer: Option>, ) -> Result< ( Self, DnsExchangeBackground, TokioTime>, ), ProtoError, > where F: Future> + 'static + Send + Unpin, S: DnsClientStream + 'static + Unpin, { let mp = DnsMultiplexer::with_timeout(stream, stream_handle, timeout_duration, signer); Self::connect(mp).await } /// Returns a future, which itself wraps a future which is awaiting connection. /// /// The connect_future should be lazy. /// /// # Returns /// /// This returns a tuple of Self a handle to send dns messages and an optional background. /// The background task must be run on an executor before handle is used, if it is Some. /// If it is None, then another thread has already run the background. pub async fn connect( connect_future: F, ) -> Result<(Self, DnsExchangeBackground), ProtoError> where S: DnsRequestSender, F: Future> + 'static + Send + Unpin, { let result = DnsExchange::connect(connect_future).await; let use_edns = true; result.map(|(exchange, bg)| (Self { exchange, use_edns }, bg)) } /// (Re-)enable usage of EDNS for outgoing messages pub fn enable_edns(&mut self) { self.use_edns = true; } /// Disable usage of EDNS for outgoing messages pub fn disable_edns(&mut self) { self.use_edns = false; } } impl DnsHandle for AsyncClient { type Response = DnsExchangeSend; type Error = ProtoError; fn send + Unpin + Send + 'static>(&self, request: R) -> Self::Response { self.exchange.send(request) } fn is_using_edns(&self) -> bool { self.use_edns } } impl ClientHandle for T where T: DnsHandle {} /// A trait for implementing high level functions of DNS. pub trait ClientHandle: 'static + Clone + DnsHandle + Send { /// A *classic* DNS query /// /// *Note* As of now, this will not recurse on PTR or CNAME record responses, that is up to /// the caller. /// /// # Arguments /// /// * `name` - the label to lookup /// * `query_class` - most likely this should always be DNSClass::IN /// * `query_type` - record type to lookup fn query( &mut self, name: Name, query_class: DNSClass, query_type: RecordType, ) -> ClientResponse<::Response> { let mut query = Query::query(name, query_type); query.set_query_class(query_class); let mut options = DnsRequestOptions::default(); options.use_edns = self.is_using_edns(); ClientResponse(self.lookup(query, options)) } /// Sends a NOTIFY message to the remote system /// /// [RFC 1996](https://tools.ietf.org/html/rfc1996), DNS NOTIFY, August 1996 /// /// /// ```text /// 1. Rationale and Scope /// /// 1.1. Slow propagation of new and changed data in a DNS zone can be /// due to a zone's relatively long refresh times. Longer refresh times /// are beneficial in that they reduce load on the Primary Zone Servers, but /// that benefit comes at the cost of long intervals of incoherence among /// authority servers whenever the zone is updated. /// /// 1.2. The DNS NOTIFY transaction allows Primary Zone Servers to inform Secondary /// Zone Servers when the zone has changed -- an interrupt as opposed to poll /// model -- which it is hoped will reduce propagation delay while not /// unduly increasing the masters' load. This specification only allows /// slaves to be notified of SOA RR changes, but the architecture of /// NOTIFY is intended to be extensible to other RR types. /// /// 1.3. This document intentionally gives more definition to the roles /// of "Primary", "Secondary" and "Stealth" servers, their enumeration in NS /// RRs, and the SOA MNAME field. In that sense, this document can be /// considered an addendum to [RFC1035]. /// /// ``` /// /// The below section describes how the Notify message should be constructed. The function /// implementation accepts a Record, but the actual data of the record should be ignored by the /// server, i.e. the server should make a request subsequent to receiving this Notification for /// the authority record, but could be used to decide to request an update or not: /// /// ```text /// 3.7. A NOTIFY request has QDCOUNT>0, ANCOUNT>=0, AUCOUNT>=0, /// ADCOUNT>=0. If ANCOUNT>0, then the answer section represents an /// unsecure hint at the new RRset for this . A /// Secondary receiving such a hint is free to treat equivalence of this /// answer section with its local data as a "no further work needs to be /// done" indication. If ANCOUNT=0, or ANCOUNT>0 and the answer section /// differs from the Secondary's local data, then the Secondary should query its /// known Primaries to retrieve the new data. /// ``` /// /// Client's should be ready to handle, or be aware of, a server response of NOTIMP: /// /// ```text /// 3.12. If a NOTIFY request is received by a Secondary who does not /// implement the NOTIFY opcode, it will respond with a NOTIMP /// (unimplemented feature error) message. A Primary Zone Server who receives /// such a NOTIMP should consider the NOTIFY transaction complete for /// that Secondary. /// ``` /// /// # Arguments /// /// * `name` - the label which is being notified /// * `query_class` - most likely this should always be DNSClass::IN /// * `query_type` - record type which has been updated /// * `rrset` - the new version of the record(s) being notified fn notify( &mut self, name: Name, query_class: DNSClass, query_type: RecordType, rrset: Option, ) -> ClientResponse<::Response> where R: Into, { debug!("notifying: {} {:?}", name, query_type); // build the message let mut message: Message = Message::new(); let id: u16 = rand::random(); message .set_id(id) // 3.3. NOTIFY is similar to QUERY in that it has a request message with // the header QR flag "clear" and a response message with QR "set". The // response message contains no useful information, but its reception by // the Primary is an indication that the Secondary has received the NOTIFY // and that the Primary Zone Server can remove the Secondary from any retry queue for // this NOTIFY event. .set_message_type(MessageType::Query) .set_op_code(OpCode::Notify); // Extended dns if self.is_using_edns() { message .extensions_mut() .get_or_insert_with(Edns::new) .set_max_payload(update_message::MAX_PAYLOAD_LEN) .set_version(0); } // add the query let mut query: Query = Query::new(); query .set_name(name) .set_query_class(query_class) .set_query_type(query_type); message.add_query(query); // add the notify message, see https://tools.ietf.org/html/rfc1996, section 3.7 if let Some(rrset) = rrset { message.add_answers(rrset.into()); } ClientResponse(self.send(message)) } /// Sends a record to create on the server, this will fail if the record exists (atomicity /// depends on the server) /// /// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997 /// /// ```text /// 2.4.3 - RRset Does Not Exist /// /// No RRs with a specified NAME and TYPE (in the zone and class denoted /// by the Zone Section) can exist. /// /// For this prerequisite, a requestor adds to the section a single RR /// whose NAME and TYPE are equal to that of the RRset whose nonexistence /// is required. The RDLENGTH of this record is zero (0), and RDATA /// field is therefore empty. CLASS must be specified as NONE in order /// to distinguish this condition from a valid RR whose RDLENGTH is /// naturally zero (0) (for example, the NULL RR). TTL must be specified /// as zero (0). /// /// 2.5.1 - Add To An RRset /// /// RRs are added to the Update Section whose NAME, TYPE, TTL, RDLENGTH /// and RDATA are those being added, and CLASS is the same as the zone /// class. Any duplicate RRs will be silently ignored by the Primary Zone /// Server. /// ``` /// /// # Arguments /// /// * `rrset` - the record(s) to create /// * `zone_origin` - the zone name to update, i.e. SOA name /// /// The update must go to a zone authority (i.e. the server used in the ClientConnection) fn create( &mut self, rrset: R, zone_origin: Name, ) -> ClientResponse<::Response> where R: Into, { let rrset = rrset.into(); let message = update_message::create(rrset, zone_origin, self.is_using_edns()); ClientResponse(self.send(message)) } /// Appends a record to an existing rrset, optionally require the rrset to exist (atomicity /// depends on the server) /// /// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997 /// /// ```text /// 2.4.1 - RRset Exists (Value Independent) /// /// At least one RR with a specified NAME and TYPE (in the zone and class /// specified in the Zone Section) must exist. /// /// For this prerequisite, a requestor adds to the section a single RR /// whose NAME and TYPE are equal to that of the zone RRset whose /// existence is required. RDLENGTH is zero and RDATA is therefore /// empty. CLASS must be specified as ANY to differentiate this /// condition from that of an actual RR whose RDLENGTH is naturally zero /// (0) (e.g., NULL). TTL is specified as zero (0). /// /// 2.5.1 - Add To An RRset /// /// RRs are added to the Update Section whose NAME, TYPE, TTL, RDLENGTH /// and RDATA are those being added, and CLASS is the same as the zone /// class. Any duplicate RRs will be silently ignored by the Primary Zone /// Server. /// ``` /// /// # Arguments /// /// * `rrset` - the record(s) to append to an RRSet /// * `zone_origin` - the zone name to update, i.e. SOA name /// * `must_exist` - if true, the request will fail if the record does not exist /// /// The update must go to a zone authority (i.e. the server used in the ClientConnection). If /// the rrset does not exist and must_exist is false, then the RRSet will be created. fn append( &mut self, rrset: R, zone_origin: Name, must_exist: bool, ) -> ClientResponse<::Response> where R: Into, { let rrset = rrset.into(); let message = update_message::append(rrset, zone_origin, must_exist, self.is_using_edns()); ClientResponse(self.send(message)) } /// Compares and if it matches, swaps it for the new value (atomicity depends on the server) /// /// ```text /// 2.4.2 - RRset Exists (Value Dependent) /// /// A set of RRs with a specified NAME and TYPE exists and has the same /// members with the same RDATAs as the RRset specified here in this /// section. While RRset ordering is undefined and therefore not /// significant to this comparison, the sets be identical in their /// extent. /// /// For this prerequisite, a requestor adds to the section an entire /// RRset whose preexistence is required. NAME and TYPE are that of the /// RRset being denoted. CLASS is that of the zone. TTL must be /// specified as zero (0) and is ignored when comparing RRsets for /// identity. /// /// 2.5.4 - Delete An RR From An RRset /// /// RRs to be deleted are added to the Update Section. The NAME, TYPE, /// RDLENGTH and RDATA must match the RR being deleted. TTL must be /// specified as zero (0) and will otherwise be ignored by the Primary /// Zone Server. CLASS must be specified as NONE to distinguish this from an /// RR addition. If no such RRs exist, then this Update RR will be /// silently ignored by the Primary Zone Server. /// /// 2.5.1 - Add To An RRset /// /// RRs are added to the Update Section whose NAME, TYPE, TTL, RDLENGTH /// and RDATA are those being added, and CLASS is the same as the zone /// class. Any duplicate RRs will be silently ignored by the Primary /// Zone Server. /// ``` /// /// # Arguments /// /// * `current` - the current rrset which must exist for the swap to complete /// * `new` - the new rrset with which to replace the current rrset /// * `zone_origin` - the zone name to update, i.e. SOA name /// /// The update must go to a zone authority (i.e. the server used in the ClientConnection). fn compare_and_swap( &mut self, current: C, new: N, zone_origin: Name, ) -> ClientResponse<::Response> where C: Into, N: Into, { let current = current.into(); let new = new.into(); let message = update_message::compare_and_swap(current, new, zone_origin, self.is_using_edns()); ClientResponse(self.send(message)) } /// Deletes a record (by rdata) from an rrset, optionally require the rrset to exist. /// /// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997 /// /// ```text /// 2.4.1 - RRset Exists (Value Independent) /// /// At least one RR with a specified NAME and TYPE (in the zone and class /// specified in the Zone Section) must exist. /// /// For this prerequisite, a requestor adds to the section a single RR /// whose NAME and TYPE are equal to that of the zone RRset whose /// existence is required. RDLENGTH is zero and RDATA is therefore /// empty. CLASS must be specified as ANY to differentiate this /// condition from that of an actual RR whose RDLENGTH is naturally zero /// (0) (e.g., NULL). TTL is specified as zero (0). /// /// 2.5.4 - Delete An RR From An RRset /// /// RRs to be deleted are added to the Update Section. The NAME, TYPE, /// RDLENGTH and RDATA must match the RR being deleted. TTL must be /// specified as zero (0) and will otherwise be ignored by the Primary /// Zone Server. CLASS must be specified as NONE to distinguish this from an /// RR addition. If no such RRs exist, then this Update RR will be /// silently ignored by the Primary Zone Server. /// ``` /// /// # Arguments /// /// * `rrset` - the record(s) to delete from a RRSet, the name, type and rdata must match the /// record to delete /// * `zone_origin` - the zone name to update, i.e. SOA name /// * `signer` - the signer, with private key, to use to sign the request /// /// The update must go to a zone authority (i.e. the server used in the ClientConnection). If /// the rrset does not exist and must_exist is false, then the RRSet will be deleted. fn delete_by_rdata( &mut self, rrset: R, zone_origin: Name, ) -> ClientResponse<::Response> where R: Into, { let rrset = rrset.into(); let message = update_message::delete_by_rdata(rrset, zone_origin, self.is_using_edns()); ClientResponse(self.send(message)) } /// Deletes an entire rrset, optionally require the rrset to exist. /// /// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997 /// /// ```text /// 2.4.1 - RRset Exists (Value Independent) /// /// At least one RR with a specified NAME and TYPE (in the zone and class /// specified in the Zone Section) must exist. /// /// For this prerequisite, a requestor adds to the section a single RR /// whose NAME and TYPE are equal to that of the zone RRset whose /// existence is required. RDLENGTH is zero and RDATA is therefore /// empty. CLASS must be specified as ANY to differentiate this /// condition from that of an actual RR whose RDLENGTH is naturally zero /// (0) (e.g., NULL). TTL is specified as zero (0). /// /// 2.5.2 - Delete An RRset /// /// One RR is added to the Update Section whose NAME and TYPE are those /// of the RRset to be deleted. TTL must be specified as zero (0) and is /// otherwise not used by the Primary Zone Server. CLASS must be specified as /// ANY. RDLENGTH must be zero (0) and RDATA must therefore be empty. /// If no such RRset exists, then this Update RR will be silently ignored /// by the Primary Zone Server. /// ``` /// /// # Arguments /// /// * `record` - The name, class and record_type will be used to match and delete the RecordSet /// * `zone_origin` - the zone name to update, i.e. SOA name /// /// The update must go to a zone authority (i.e. the server used in the ClientConnection). If /// the rrset does not exist and must_exist is false, then the RRSet will be deleted. fn delete_rrset( &mut self, record: Record, zone_origin: Name, ) -> ClientResponse<::Response> { assert!(zone_origin.zone_of(record.name())); let message = update_message::delete_rrset(record, zone_origin, self.is_using_edns()); ClientResponse(self.send(message)) } /// Deletes all records at the specified name /// /// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997 /// /// ```text /// 2.5.3 - Delete All RRsets From A Name /// /// One RR is added to the Update Section whose NAME is that of the name /// to be cleansed of RRsets. TYPE must be specified as ANY. TTL must /// be specified as zero (0) and is otherwise not used by the Primary /// Zone Server. CLASS must be specified as ANY. RDLENGTH must be zero (0) /// and RDATA must therefore be empty. If no such RRsets exist, then /// this Update RR will be silently ignored by the Primary Zone Server. /// ``` /// /// # Arguments /// /// * `name_of_records` - the name of all the record sets to delete /// * `zone_origin` - the zone name to update, i.e. SOA name /// * `dns_class` - the class of the SOA /// /// The update must go to a zone authority (i.e. the server used in the ClientConnection). This /// operation attempts to delete all resource record sets the specified name regardless of /// the record type. fn delete_all( &mut self, name_of_records: Name, zone_origin: Name, dns_class: DNSClass, ) -> ClientResponse<::Response> { assert!(zone_origin.zone_of(&name_of_records)); let message = update_message::delete_all( name_of_records, zone_origin, dns_class, self.is_using_edns(), ); ClientResponse(self.send(message)) } /// Download all records from a zone, or all records modified since given SOA was observed. /// The request will either be a AXFR Query (ask for full zone transfer) if a SOA was not /// provided, or a IXFR Query (incremental zone transfer) if a SOA was provided. /// /// # Arguments /// * `zone_origin` - the zone name to update, i.e. SOA name /// * `last_soa` - the last SOA known, if any. If provided, name must match `zone_origin` fn zone_transfer( &mut self, zone_origin: Name, last_soa: Option, ) -> ClientStreamXfr<::Response> { let ixfr = last_soa.is_some(); let message = update_message::zone_transfer(zone_origin, last_soa); ClientStreamXfr::new(self.send(message), ixfr) } } /// A stream result of a Client Request #[must_use = "stream do nothing unless polled"] pub struct ClientStreamingResponse(pub(crate) R) where R: Stream> + Send + Unpin + 'static; impl Stream for ClientStreamingResponse where R: Stream> + Send + Unpin + 'static, { type Item = Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Poll::Ready(ready!(self.0.poll_next_unpin(cx)).map(|r| r.map_err(ClientError::from))) } } /// A future result of a Client Request #[must_use = "futures do nothing unless polled"] pub struct ClientResponse(pub(crate) R) where R: Stream> + Send + Unpin + 'static; impl Future for ClientResponse where R: Stream> + Send + Unpin + 'static, { type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Poll::Ready( match ready!(self.0.poll_next_unpin(cx)) { Some(r) => r, None => Err(ProtoError::from(ProtoErrorKind::Timeout)), } .map_err(ClientError::from), ) } } /// A stream result of a zone transfer Client Request /// Accept messages until the end of a zone transfer. For AXFR, it search for a starting and an /// ending SOA. For IXFR, it do so taking into account there will be other SOA inbetween #[must_use = "stream do nothing unless polled"] pub struct ClientStreamXfr where R: Stream> + Send + Unpin + 'static, { state: ClientStreamXfrState, } impl ClientStreamXfr where R: Stream> + Send + Unpin + 'static, { fn new(inner: R, maybe_incr: bool) -> Self { Self { state: ClientStreamXfrState::Start { inner, maybe_incr }, } } } /// State machine for ClientStreamXfr, implementing almost all logic #[derive(Debug)] enum ClientStreamXfrState { Start { inner: R, maybe_incr: bool, }, Second { inner: R, expected_serial: u32, maybe_incr: bool, }, Axfr { inner: R, expected_serial: u32, }, Ixfr { inner: R, even: bool, expected_serial: u32, }, Ended, Invalid, } impl ClientStreamXfrState { /// Helper to get the stream from the enum fn inner(&mut self) -> &mut R { use ClientStreamXfrState::*; match self { Start { inner, .. } => inner, Second { inner, .. } => inner, Axfr { inner, .. } => inner, Ixfr { inner, .. } => inner, Ended | Invalid => unreachable!(), } } /// Helper to ingest answer Records // TODO: this is complex enough it should get its own tests fn process(&mut self, answers: &[Record]) -> Result<(), ClientError> { use ClientStreamXfrState::*; fn get_serial(r: &Record) -> Option { r.data().and_then(RData::as_soa).map(SOA::serial) } if answers.is_empty() { return Ok(()); } match std::mem::replace(self, Invalid) { Start { inner, maybe_incr } => { if let Some(expected_serial) = get_serial(&answers[0]) { *self = Second { inner, maybe_incr, expected_serial, }; self.process(&answers[1..]) } else { *self = Ended; Ok(()) } } Second { inner, maybe_incr, expected_serial, } => { if let Some(serial) = get_serial(&answers[0]) { // maybe IXFR, or empty AXFR if serial == expected_serial { // empty AXFR *self = Ended; if answers.len() == 1 { Ok(()) } else { // invalid answer : trailing records Err(ClientErrorKind::Message( "invalid zone transfer, contains trailing records", ) .into()) } } else if maybe_incr { *self = Ixfr { inner, expected_serial, even: true, }; self.process(&answers[1..]) } else { *self = Ended; Err(ClientErrorKind::Message( "invalid zone transfer, expected AXFR, got IXFR", ) .into()) } } else { // standard AXFR *self = Axfr { inner, expected_serial, }; self.process(&answers[1..]) } } Axfr { inner, expected_serial, } => { let soa_count = answers .iter() .filter(|a| a.record_type() == RecordType::SOA) .count(); match soa_count { 0 => { *self = Axfr { inner, expected_serial, }; Ok(()) } 1 => { *self = Ended; match answers.last().map(|r| r.record_type()) { Some(RecordType::SOA) => Ok(()), _ => Err(ClientErrorKind::Message( "invalid zone transfer, contains trailing records", ) .into()), } } _ => { *self = Ended; Err(ClientErrorKind::Message( "invalid zone transfer, contains trailing records", ) .into()) } } } Ixfr { inner, even, expected_serial, } => { let even = answers .iter() .fold(even, |even, a| even ^ (a.record_type() == RecordType::SOA)); if even { if let Some(serial) = get_serial(answers.last().unwrap()) { if serial == expected_serial { *self = Ended; return Ok(()); } } } *self = Ixfr { inner, even, expected_serial, }; Ok(()) } Ended | Invalid => { unreachable!(); } } } } impl Stream for ClientStreamXfr where R: Stream> + Send + Unpin + 'static, { type Item = Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { use ClientStreamXfrState::*; if matches!(self.state, Ended) { return Poll::Ready(None); } let message = ready!(self.state.inner().poll_next_unpin(cx)).map(|response| { let ok = response?; self.state.process(ok.answers())?; Ok(ok) }); Poll::Ready(message) } } #[cfg(test)] mod tests { use super::*; use crate::rr::rdata::{A, SOA}; use futures_util::stream::iter; use ClientStreamXfrState::*; fn soa_record(serial: u32) -> Record { let soa = RData::SOA(SOA::new( Name::from_ascii("example.com.").unwrap(), Name::from_ascii("admin.example.com.").unwrap(), serial, 60, 60, 60, 60, )); Record::from_rdata(Name::from_ascii("example.com.").unwrap(), 600, soa) } fn a_record(ip: u8) -> Record { let a = RData::A(A::new(0, 0, 0, ip)); Record::from_rdata(Name::from_ascii("www.example.com.").unwrap(), 600, a) } fn get_stream_testcase( records: Vec>, ) -> impl Stream> + Send + Unpin + 'static { let stream = records.into_iter().map(|r| { Ok({ let mut m = Message::new(); m.insert_answers(r); DnsResponse::from_message(m).unwrap() }) }); iter(stream) } #[tokio::test] async fn test_stream_xfr_valid_axfr() { let stream = get_stream_testcase(vec![vec![ soa_record(3), a_record(1), a_record(2), soa_record(3), ]]); let mut stream = ClientStreamXfr::new(stream, false); assert!(matches!(stream.state, Start { .. })); let response = stream.next().await.unwrap().unwrap(); assert!(matches!(stream.state, Ended)); assert_eq!(response.answers().len(), 4); assert!(stream.next().await.is_none()); } #[tokio::test] async fn test_stream_xfr_valid_axfr_multipart() { let stream = get_stream_testcase(vec![ vec![soa_record(3)], vec![a_record(1)], vec![soa_record(3)], vec![a_record(2)], // will be ignored as connection is dropped before reading this message ]); let mut stream = ClientStreamXfr::new(stream, false); assert!(matches!(stream.state, Start { .. })); let response = stream.next().await.unwrap().unwrap(); assert!(matches!(stream.state, Second { .. })); assert_eq!(response.answers().len(), 1); let response = stream.next().await.unwrap().unwrap(); assert!(matches!(stream.state, Axfr { .. })); assert_eq!(response.answers().len(), 1); let response = stream.next().await.unwrap().unwrap(); assert!(matches!(stream.state, Ended)); assert_eq!(response.answers().len(), 1); assert!(stream.next().await.is_none()); } #[tokio::test] async fn test_stream_xfr_empty_axfr() { let stream = get_stream_testcase(vec![vec![soa_record(3)], vec![soa_record(3)]]); let mut stream = ClientStreamXfr::new(stream, false); assert!(matches!(stream.state, Start { .. })); let response = stream.next().await.unwrap().unwrap(); assert!(matches!(stream.state, Second { .. })); assert_eq!(response.answers().len(), 1); let response = stream.next().await.unwrap().unwrap(); assert!(matches!(stream.state, Ended)); assert_eq!(response.answers().len(), 1); assert!(stream.next().await.is_none()); } #[tokio::test] async fn test_stream_xfr_axfr_with_ixfr_reply() { let stream = get_stream_testcase(vec![vec![ soa_record(3), soa_record(2), a_record(1), soa_record(3), a_record(2), soa_record(3), ]]); let mut stream = ClientStreamXfr::new(stream, false); assert!(matches!(stream.state, Start { .. })); stream.next().await.unwrap().unwrap_err(); assert!(matches!(stream.state, Ended)); assert!(stream.next().await.is_none()); } #[tokio::test] async fn test_stream_xfr_axfr_with_non_xfr_reply() { let stream = get_stream_testcase(vec![ vec![a_record(1)], // assume this is an error response, not a zone transfer vec![a_record(2)], ]); let mut stream = ClientStreamXfr::new(stream, false); assert!(matches!(stream.state, Start { .. })); let response = stream.next().await.unwrap().unwrap(); assert!(matches!(stream.state, Ended)); assert_eq!(response.answers().len(), 1); assert!(stream.next().await.is_none()); } #[tokio::test] async fn test_stream_xfr_invalid_axfr_multipart() { let stream = get_stream_testcase(vec![ vec![soa_record(3)], vec![a_record(1)], vec![soa_record(3), a_record(2)], vec![soa_record(3)], ]); let mut stream = ClientStreamXfr::new(stream, false); assert!(matches!(stream.state, Start { .. })); let response = stream.next().await.unwrap().unwrap(); assert!(matches!(stream.state, Second { .. })); assert_eq!(response.answers().len(), 1); let response = stream.next().await.unwrap().unwrap(); assert!(matches!(stream.state, Axfr { .. })); assert_eq!(response.answers().len(), 1); stream.next().await.unwrap().unwrap_err(); assert!(matches!(stream.state, Ended)); assert!(stream.next().await.is_none()); } #[tokio::test] async fn test_stream_xfr_valid_ixfr() { let stream = get_stream_testcase(vec![vec![ soa_record(3), soa_record(2), a_record(1), soa_record(3), a_record(2), soa_record(3), ]]); let mut stream = ClientStreamXfr::new(stream, true); assert!(matches!(stream.state, Start { .. })); let response = stream.next().await.unwrap().unwrap(); assert!(matches!(stream.state, Ended)); assert_eq!(response.answers().len(), 6); assert!(stream.next().await.is_none()); } #[tokio::test] async fn test_stream_xfr_valid_ixfr_multipart() { let stream = get_stream_testcase(vec![ vec![soa_record(3)], vec![soa_record(2)], vec![a_record(1)], vec![soa_record(3)], vec![a_record(2)], vec![soa_record(3)], vec![a_record(3)], // ]); let mut stream = ClientStreamXfr::new(stream, true); assert!(matches!(stream.state, Start { .. })); let response = stream.next().await.unwrap().unwrap(); assert!(matches!(stream.state, Second { .. })); assert_eq!(response.answers().len(), 1); let response = stream.next().await.unwrap().unwrap(); assert!(matches!(stream.state, Ixfr { even: true, .. })); assert_eq!(response.answers().len(), 1); let response = stream.next().await.unwrap().unwrap(); assert!(matches!(stream.state, Ixfr { even: true, .. })); assert_eq!(response.answers().len(), 1); let response = stream.next().await.unwrap().unwrap(); assert!(matches!(stream.state, Ixfr { even: false, .. })); assert_eq!(response.answers().len(), 1); let response = stream.next().await.unwrap().unwrap(); assert!(matches!(stream.state, Ixfr { even: false, .. })); assert_eq!(response.answers().len(), 1); let response = stream.next().await.unwrap().unwrap(); assert!(matches!(stream.state, Ended)); assert_eq!(response.answers().len(), 1); assert!(stream.next().await.is_none()); } #[tokio::test] async fn async_client() { use crate::client::{AsyncClient, ClientHandle}; use crate::proto::iocompat::AsyncIoTokioAsStd; use crate::rr::{DNSClass, Name, RData, RecordType}; use crate::tcp::TcpClientStream; use std::str::FromStr; use tokio::net::TcpStream as TokioTcpStream; // Since we used UDP in the previous examples, let's change things up a bit and use TCP here let (stream, sender) = TcpClientStream::>::new(([8, 8, 8, 8], 53).into()); // Create a new client, the bg is a background future which handles // the multiplexing of the DNS requests to the server. // the client is a handle to an unbounded queue for sending requests via the // background. The background must be scheduled to run before the client can // send any dns requests let client = AsyncClient::new(stream, sender, None); // await the connection to be established let (mut client, bg) = client.await.expect("connection failed"); // make sure to run the background task tokio::spawn(bg); // Create a query future let query = client.query( Name::from_str("www.example.com.").unwrap(), DNSClass::IN, RecordType::A, ); // wait for its response let (message_returned, buffer) = query.await.unwrap().into_parts(); // validate it's what we expected if let Some(RData::A(addr)) = message_returned.answers()[0].data() { assert_eq!(*addr, A::new(93, 184, 215, 14)); } let message_parsed = Message::from_vec(&buffer) .expect("buffer was parsed already by AsyncClient so we should be able to do it again"); // validate it's what we expected if let Some(RData::A(addr)) = message_parsed.answers()[0].data() { assert_eq!(*addr, A::new(93, 184, 215, 14)); } } } hickory-client-0.24.4/src/client/async_secure_client.rs000064400000000000000000000073021046102023000212750ustar 00000000000000// Copyright 2015-2019 Benjamin Fry // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. use std::future::Future; use std::pin::Pin; use futures_util::stream::Stream; use crate::client::AsyncClient; use crate::proto::error::ProtoError; use crate::proto::rr::dnssec::TrustAnchor; use crate::proto::xfer::{ DnsExchangeBackground, DnsHandle, DnsRequest, DnsRequestSender, DnsResponse, }; use crate::proto::DnssecDnsHandle; use crate::proto::TokioTime; /// A DNSSEC Client implemented over futures-rs. /// /// This Client is generic and capable of wrapping UDP, TCP, and other underlying DNS protocol /// implementations. pub struct AsyncDnssecClient { client: DnssecDnsHandle, } impl AsyncDnssecClient { /// Returns a DNSSEC verifying client with a TrustAnchor that can be replaced pub fn builder(connect_future: F) -> AsyncSecureClientBuilder where F: Future> + 'static + Send + Unpin, S: DnsRequestSender + 'static, { AsyncSecureClientBuilder { connect_future, trust_anchor: None, } } /// Returns a DNSSEC verifying client with the default TrustAnchor pub async fn connect( connect_future: F, ) -> Result<(Self, DnsExchangeBackground), ProtoError> where S: DnsRequestSender, F: Future> + 'static + Send + Unpin, { Self::builder(connect_future).build().await } fn from_client(client: AsyncClient, trust_anchor: TrustAnchor) -> Self { Self { client: DnssecDnsHandle::with_trust_anchor(client, trust_anchor), } } } impl Clone for AsyncDnssecClient { fn clone(&self) -> Self { Self { client: self.client.clone(), } } } impl DnsHandle for AsyncDnssecClient { type Response = Pin> + Send + 'static)>>; type Error = ProtoError; fn send + Unpin + Send + 'static>(&self, request: R) -> Self::Response { self.client.send(request) } } /// A builder to allow a custom trust to be used for validating all signed records #[cfg(feature = "dnssec")] #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))] pub struct AsyncSecureClientBuilder where F: Future> + 'static + Send + Unpin, S: DnsRequestSender + 'static, { connect_future: F, trust_anchor: Option, } #[cfg(feature = "dnssec")] impl AsyncSecureClientBuilder where F: Future> + 'static + Send + Unpin, S: DnsRequestSender + 'static, { /// This variant allows for the trust_anchor to be replaced /// /// # Arguments /// /// * `trust_anchor` - the set of trusted DNSKEY public_keys, by default this only contains the /// root public_key. pub fn trust_anchor(mut self, trust_anchor: TrustAnchor) -> Self { self.trust_anchor = Some(trust_anchor); self } /// Construct the new client pub async fn build( mut self, ) -> Result<(AsyncDnssecClient, DnsExchangeBackground), ProtoError> { let trust_anchor = self.trust_anchor.take().unwrap_or_default(); let result = AsyncClient::connect(self.connect_future).await; result.map(|(client, bg)| (AsyncDnssecClient::from_client(client, trust_anchor), bg)) } } hickory-client-0.24.4/src/client/client.rs000064400000000000000000000567101046102023000165410ustar 00000000000000// Copyright (C) 2015 - 2016 Benjamin Fry // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::future::Future; use std::pin::Pin; use std::sync::Arc; use futures_util::stream::{Stream, StreamExt}; use tokio::runtime::{self, Runtime}; use crate::client::async_client::ClientStreamXfr; use crate::client::{AsyncClient, ClientConnection, ClientHandle, Signer}; use crate::error::*; use crate::proto::{ error::ProtoError, xfer::{DnsExchangeSend, DnsHandle, DnsRequest, DnsResponse}, }; use crate::rr::rdata::SOA; use crate::rr::{DNSClass, Name, Record, RecordSet, RecordType}; #[cfg(feature = "dnssec")] use crate::{ client::AsyncDnssecClient, proto::rr::dnssec::{tsig::TSigner, SigSigner, TrustAnchor}, }; use super::ClientStreamingResponse; #[allow(clippy::type_complexity)] pub(crate) type NewFutureObj = Pin< Box< dyn Future< Output = Result< ( H, Box> + 'static + Send + Unpin>, ), ProtoError, >, > + 'static + Send, >, >; /// Client trait which implements basic DNS Client operations. /// /// As of 0.10.0, the Client is now a wrapper around the `AsyncClient`, which is a futures-rs /// and tokio-rs based implementation. This trait implements synchronous functions for ease of use. /// /// There was a strong attempt to make it backwards compatible, but making it a drop in replacement /// for the old Client was not possible. This trait has two implementations, the `SyncClient` which /// is a standard DNS Client, and the `SyncDnssecClient` which is a wrapper on `DnssecDnsHandle` /// providing DNSSEC validation. /// /// *note* When upgrading from previous usage, both `SyncClient` and `SyncDnssecClient` have an /// signer which can be optionally associated to the Client. This replaces the previous per-function /// parameter, and it will sign all update requests (this matches the `AsyncClient` API). #[allow(unreachable_code)] pub trait Client { /// The result stream that will resolve into a DnsResponse type Response: Stream> + 'static + Send + Unpin; /// The AsyncClient type used type Handle: DnsHandle + 'static + Send + Unpin; /// Return the inner Futures items /// /// Consumes the connection and allows for future based operations afterward. fn new_future(&self) -> NewFutureObj; /// This will create a new AsyncClient and spawn it into a new Runtime fn spawn_client(&self) -> ClientResult<(Self::Handle, Runtime)> { let mut builder = runtime::Builder::new_current_thread(); builder.enable_all(); let reactor = builder.build()?; let client = self.new_future(); let (client, bg) = reactor.block_on(client)?; // TODO: should we return this? let _join_bg = reactor.spawn(bg); Ok((client, reactor)) } /// Sends an arbitrary `DnsRequest` to the client fn send + Unpin + Send + 'static>( &self, msg: R, ) -> Vec> { let (client, runtime) = match self.spawn_client() { Ok(c_r) => c_r, Err(e) => return vec![Err(e)], }; runtime.block_on(ClientStreamingResponse(client.send(msg)).collect::>()) } /// A *classic* DNS query, i.e. does not perform any DNSSEC operations /// /// *Note* As of now, this will not recurse on PTR record responses, that is up to /// the caller. /// /// # Arguments /// /// * `name` - the label to lookup /// * `query_class` - most likely this should always be DNSClass::IN /// * `query_type` - record type to lookup fn query( &self, name: &Name, query_class: DNSClass, query_type: RecordType, ) -> ClientResult { let (mut client, runtime) = self.spawn_client()?; runtime.block_on(client.query(name.clone(), query_class, query_type)) } /// Sends a NOTIFY message to the remote system /// /// # Arguments /// /// * `name` - the label which is being notified /// * `query_class` - most likely this should always be DNSClass::IN /// * `query_type` - record type which has been updated /// * `rrset` - the new version of the record(s) being notified fn notify( &mut self, name: Name, query_class: DNSClass, query_type: RecordType, rrset: Option, ) -> ClientResult where R: Into, { let (mut client, runtime) = self.spawn_client()?; runtime.block_on(client.notify(name, query_class, query_type, rrset)) } /// Sends a record to create on the server, this will fail if the record exists (atomicity /// depends on the server) /// /// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997 /// /// ```text /// 2.4.3 - RRset Does Not Exist /// /// No RRs with a specified NAME and TYPE (in the zone and class denoted /// by the Zone Section) can exist. /// /// For this prerequisite, a requestor adds to the section a single RR /// whose NAME and TYPE are equal to that of the RRset whose nonexistence /// is required. The RDLENGTH of this record is zero (0), and RDATA /// field is therefore empty. CLASS must be specified as NONE in order /// to distinguish this condition from a valid RR whose RDLENGTH is /// naturally zero (0) (for example, the NULL RR). TTL must be specified /// as zero (0). /// /// 2.5.1 - Add To An RRset /// /// RRs are added to the Update Section whose NAME, TYPE, TTL, RDLENGTH /// and RDATA are those being added, and CLASS is the same as the zone /// class. Any duplicate RRs will be silently ignored by the Primary /// Zone Server. /// ``` /// /// # Arguments /// /// * `rrset` - the record(s) to create /// * `zone_origin` - the zone name to update, i.e. SOA name /// /// The update must go to a zone authority (i.e. the server used in the ClientConnection) fn create(&self, rrset: R, zone_origin: Name) -> ClientResult where R: Into, { let (mut client, runtime) = self.spawn_client()?; runtime.block_on(client.create(rrset, zone_origin)) } /// Appends a record to an existing rrset, optionally require the rrset to exist (atomicity /// depends on the server) /// /// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997 /// /// ```text /// 2.4.1 - RRset Exists (Value Independent) /// /// At least one RR with a specified NAME and TYPE (in the zone and class /// specified in the Zone Section) must exist. /// /// For this prerequisite, a requestor adds to the section a single RR /// whose NAME and TYPE are equal to that of the zone RRset whose /// existence is required. RDLENGTH is zero and RDATA is therefore /// empty. CLASS must be specified as ANY to differentiate this /// condition from that of an actual RR whose RDLENGTH is naturally zero /// (0) (e.g., NULL). TTL is specified as zero (0). /// /// 2.5.1 - Add To An RRset /// /// RRs are added to the Update Section whose NAME, TYPE, TTL, RDLENGTH /// and RDATA are those being added, and CLASS is the same as the zone /// class. Any duplicate RRs will be silently ignored by the Primary /// Zone Server. /// ``` /// /// # Arguments /// /// * `rrset` - the record(s) to append to an RRSet /// * `zone_origin` - the zone name to update, i.e. SOA name /// * `must_exist` - if true, the request will fail if the record does not exist /// /// The update must go to a zone authority (i.e. the server used in the ClientConnection). If /// the rrset does not exist and must_exist is false, then the RRSet will be created. fn append(&self, rrset: R, zone_origin: Name, must_exist: bool) -> ClientResult where R: Into, { let (mut client, runtime) = self.spawn_client()?; runtime.block_on(client.append(rrset, zone_origin, must_exist)) } /// Compares and if it matches, swaps it for the new value (atomicity depends on the server) /// /// ```text /// 2.4.2 - RRset Exists (Value Dependent) /// /// A set of RRs with a specified NAME and TYPE exists and has the same /// members with the same RDATAs as the RRset specified here in this /// section. While RRset ordering is undefined and therefore not /// significant to this comparison, the sets be identical in their /// extent. /// /// For this prerequisite, a requestor adds to the section an entire /// RRset whose preexistence is required. NAME and TYPE are that of the /// RRset being denoted. CLASS is that of the zone. TTL must be /// specified as zero (0) and is ignored when comparing RRsets for /// identity. /// /// 2.5.4 - Delete An RR From An RRset /// /// RRs to be deleted are added to the Update Section. The NAME, TYPE, /// RDLENGTH and RDATA must match the RR being deleted. TTL must be /// specified as zero (0) and will otherwise be ignored by the Primary /// Zone Server. CLASS must be specified as NONE to distinguish this from an /// RR addition. If no such RRs exist, then this Update RR will be /// silently ignored by the Primary Zone Server. /// /// 2.5.1 - Add To An RRset /// /// RRs are added to the Update Section whose NAME, TYPE, TTL, RDLENGTH /// and RDATA are those being added, and CLASS is the same as the zone /// class. Any duplicate RRs will be silently ignored by the Primary /// Zone Server. /// ``` /// /// # Arguments /// /// * `current` - the current rrset which must exist for the swap to complete /// * `new` - the new rrset with which to replace the current rrset /// * `zone_origin` - the zone name to update, i.e. SOA name /// /// The update must go to a zone authority (i.e. the server used in the ClientConnection). fn compare_and_swap( &self, current: CR, new: NR, zone_origin: Name, ) -> ClientResult where CR: Into, NR: Into, { let (mut client, runtime) = self.spawn_client()?; runtime.block_on(client.compare_and_swap(current, new, zone_origin)) } /// Deletes a record (by rdata) from an rrset, optionally require the rrset to exist. /// /// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997 /// /// ```text /// 2.4.1 - RRset Exists (Value Independent) /// /// At least one RR with a specified NAME and TYPE (in the zone and class /// specified in the Zone Section) must exist. /// /// For this prerequisite, a requestor adds to the section a single RR /// whose NAME and TYPE are equal to that of the zone RRset whose /// existence is required. RDLENGTH is zero and RDATA is therefore /// empty. CLASS must be specified as ANY to differentiate this /// condition from that of an actual RR whose RDLENGTH is naturally zero /// (0) (e.g., NULL). TTL is specified as zero (0). /// /// 2.5.4 - Delete An RR From An RRset /// /// RRs to be deleted are added to the Update Section. The NAME, TYPE, /// RDLENGTH and RDATA must match the RR being deleted. TTL must be /// specified as zero (0) and will otherwise be ignored by the Primary /// Zone Server. CLASS must be specified as NONE to distinguish this from an /// RR addition. If no such RRs exist, then this Update RR will be /// silently ignored by the Primary Zone Server. /// ``` /// /// # Arguments /// /// * `rrset` - the record(s) to delete from a RRSet, the name, type and rdata must match the /// record to delete /// * `zone_origin` - the zone name to update, i.e. SOA name /// /// The update must go to a zone authority (i.e. the server used in the ClientConnection). If /// the rrset does not exist and must_exist is false, then the RRSet will be deleted. fn delete_by_rdata(&self, record: R, zone_origin: Name) -> ClientResult where R: Into, { let (mut client, runtime) = self.spawn_client()?; runtime.block_on(client.delete_by_rdata(record, zone_origin)) } /// Deletes an entire rrset, optionally require the rrset to exist. /// /// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997 /// /// ```text /// 2.4.1 - RRset Exists (Value Independent) /// /// At least one RR with a specified NAME and TYPE (in the zone and class /// specified in the Zone Section) must exist. /// /// For this prerequisite, a requestor adds to the section a single RR /// whose NAME and TYPE are equal to that of the zone RRset whose /// existence is required. RDLENGTH is zero and RDATA is therefore /// empty. CLASS must be specified as ANY to differentiate this /// condition from that of an actual RR whose RDLENGTH is naturally zero /// (0) (e.g., NULL). TTL is specified as zero (0). /// /// 2.5.2 - Delete An RRset /// /// One RR is added to the Update Section whose NAME and TYPE are those /// of the RRset to be deleted. TTL must be specified as zero (0) and is /// otherwise not used by the Primary Zone Sever. CLASS must be specified as /// ANY. RDLENGTH must be zero (0) and RDATA must therefore be empty. /// If no such RRset exists, then this Update RR will be silently ignored /// by the Primary Zone Server. /// ``` /// /// # Arguments /// /// * `record` - the record to delete from a RRSet, the name, and type must match the /// record set to delete /// * `zone_origin` - the zone name to update, i.e. SOA name /// /// The update must go to a zone authority (i.e. the server used in the ClientConnection). If /// the rrset does not exist and must_exist is false, then the RRSet will be deleted. fn delete_rrset(&self, record: Record, zone_origin: Name) -> ClientResult { let (mut client, runtime) = self.spawn_client()?; runtime.block_on(client.delete_rrset(record, zone_origin)) } /// Deletes all records at the specified name /// /// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997 /// /// ```text /// 2.5.3 - Delete All RRsets From A Name /// /// One RR is added to the Update Section whose NAME is that of the name /// to be cleansed of RRsets. TYPE must be specified as ANY. TTL must /// be specified as zero (0) and is otherwise not used by the Primary /// Zone Server. CLASS must be specified as ANY. RDLENGTH must be zero (0) /// and RDATA must therefore be empty. If no such RRsets exist, then /// this Update RR will be silently ignored by the Primary Zone Server. /// ``` /// /// # Arguments /// /// * `name_of_records` - the name of all the record sets to delete /// * `zone_origin` - the zone name to update, i.e. SOA name /// * `dns_class` - the class of the SOA /// /// The update must go to a zone authority (i.e. the server used in the ClientConnection). This /// operation attempts to delete all resource record sets the specified name regardless of /// the record type. fn delete_all( &self, name_of_records: Name, zone_origin: Name, dns_class: DNSClass, ) -> ClientResult { let (mut client, runtime) = self.spawn_client()?; runtime.block_on(client.delete_all(name_of_records, zone_origin, dns_class)) } /// Download all records from a zone, or all records modified since given SOA was observed. /// The request will either be a AXFR Query (ask for full zone transfer) if a SOA was not /// provided, or a IXFR Query (incremental zone transfer) if a SOA was provided. /// /// # Arguments /// * `zone_origin` - the zone name to update, i.e. SOA name /// * `last_soa` - the last SOA known, if any. If provided, name must match `zone_origin` fn zone_transfer( &self, name: &Name, last_soa: Option, ) -> ClientResult::Response>>> { let (mut client, runtime) = self.spawn_client()?; Ok(BlockingStream { inner: client.zone_transfer(name.clone(), last_soa), runtime, }) } } /// The Client is abstracted over either hickory_client::tcp::TcpClientConnection or /// hickory_client::udp::UdpClientConnection. /// /// Usage of TCP or UDP is up to the user. Some DNS servers /// disallow TCP in some cases, so if TCP double check if UDP works. pub struct SyncClient { conn: CC, signer: Option>, } impl SyncClient { /// Creates a new DNS client with the specified connection type /// /// # Arguments /// /// * `conn` - the [`ClientConnection`] to use for all communication pub fn new(conn: CC) -> Self { Self { conn, signer: None } } /// Creates a new DNS client with the specified connection type and a SIG0 signer. /// /// This is necessary for signed update requests to update hickory-server entries. /// /// # Arguments /// /// * `conn` - the [`ClientConnection`] to use for all communication /// * `signer` - signer to use, this needs an associated private key #[cfg(feature = "dnssec")] #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))] pub fn with_signer(conn: CC, signer: SigSigner) -> Self { Self { conn, signer: Some(Arc::new(signer.into())), } } /// Creates a new DNS client with the specified connection type and TSIG signer. /// /// This is necessary for signed update requests to update certain servers entries. /// /// # Arguments /// /// * `conn` - the [`ClientConnection`] to use for all communication /// * `signer` - signer to use #[cfg(feature = "dnssec")] #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))] pub fn with_tsigner(conn: CC, signer: TSigner) -> Self { Self { conn, signer: Some(Arc::new(signer.into())), } } } impl Client for SyncClient { type Response = DnsExchangeSend; type Handle = AsyncClient; fn new_future(&self) -> NewFutureObj { let stream = self.conn.new_stream(self.signer.clone()); let connect = async move { let (client, bg) = AsyncClient::connect(stream).await?; let bg = Box::new(bg) as _; Ok((client, bg)) }; Box::pin(connect) } } /// An iterator based on a `Stream` of dns response. /// Calling `next` on this iterator is a blocking operation. pub struct BlockingStream { inner: T, runtime: Runtime, } impl Iterator for BlockingStream where T: Stream + Unpin, R: Into>, { type Item = ClientResult; fn next(&mut self) -> Option { self.runtime.block_on(self.inner.next()).map(Into::into) } } /// A DNS client which will validate DNSSEC records upon receipt #[cfg(feature = "dnssec")] #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))] pub struct SyncDnssecClient { conn: CC, signer: Option>, trust_anchor: Option, } #[cfg(feature = "dnssec")] impl SyncDnssecClient { /// Creates a new DNS client with the specified connection type /// /// # Arguments /// /// * `client_connection` - the client_connection to use for all communication #[allow(clippy::new_ret_no_self)] pub fn new(conn: CC) -> SecureSyncClientBuilder { SecureSyncClientBuilder { conn, trust_anchor: None, signer: None, } } } #[cfg(feature = "dnssec")] impl Client for SyncDnssecClient { type Response = Pin> + Send + 'static)>>; type Handle = AsyncDnssecClient; #[allow(clippy::type_complexity)] fn new_future(&self) -> NewFutureObj { let stream = self.conn.new_stream(self.signer.clone()); let mut builder = AsyncDnssecClient::builder(stream); if let Some(trust_anchor) = &self.trust_anchor { builder = builder.trust_anchor(trust_anchor.clone()); } let connect = builder.build(); let connect = async move { let (client, bg) = connect.await?; let bg = Box::new(bg) as _; Ok((client, bg)) }; Box::pin(connect) } } #[cfg(feature = "dnssec")] #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))] pub struct SecureSyncClientBuilder { conn: CC, signer: Option>, trust_anchor: Option, } #[cfg(feature = "dnssec")] impl SecureSyncClientBuilder { /// This variant allows for the trust_anchor to be replaced /// /// # Arguments /// /// * `trust_anchor` - the set of trusted DNSKEY public_keys, by default this only contains the /// root public_key. pub fn trust_anchor(mut self, trust_anchor: TrustAnchor) -> Self { self.trust_anchor = Some(trust_anchor); self } /// Associate a signer to produce a SIG0 for all update requests /// /// This is necessary for signed update requests to update hickory-server entries /// /// # Arguments /// /// * `signer` - signer to use, this needs an associated private key pub fn signer(mut self, signer: Signer) -> Self { self.signer = Some(Arc::new(signer)); self } pub fn build(self) -> SyncDnssecClient { SyncDnssecClient { conn: self.conn, signer: self.signer, trust_anchor: self.trust_anchor, } } } #[cfg(test)] fn assert_send_and_sync() {} #[test] fn test_sync_client_send_and_sync() { use crate::tcp::TcpClientConnection; use crate::udp::UdpClientConnection; assert_send_and_sync::>(); assert_send_and_sync::>(); } #[test] #[cfg(feature = "dnssec")] fn test_secure_client_send_and_sync() { use crate::tcp::TcpClientConnection; use crate::udp::UdpClientConnection; assert_send_and_sync::>(); assert_send_and_sync::>(); } hickory-client-0.24.4/src/client/client_connection.rs000064400000000000000000000057561046102023000207640ustar 00000000000000// Copyright (C) 2016 Benjamin Fry // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Trait for client connections use std::future::Future; use std::sync::Arc; use crate::op::{MessageFinalizer, MessageVerifier}; #[cfg(feature = "dnssec")] #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))] use crate::proto::rr::dnssec::tsig::TSigner; #[cfg(feature = "dnssec")] #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))] use crate::proto::rr::dnssec::SigSigner; use crate::proto::{error::ProtoError, xfer::DnsRequestSender}; use crate::proto::error::ProtoResult; use crate::proto::op::Message; use crate::proto::rr::Record; /// List of currently supported signers #[allow(missing_copy_implementations)] pub enum Signer { /// A Sig0 based signer #[cfg(feature = "dnssec")] #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))] Sig0(Box), /// A TSIG based signer #[cfg(feature = "dnssec")] #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))] TSIG(TSigner), } #[cfg(feature = "dnssec")] #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))] impl From for Signer { fn from(s: SigSigner) -> Self { Self::Sig0(Box::new(s)) } } #[cfg(feature = "dnssec")] #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))] impl From for Signer { fn from(s: TSigner) -> Self { Self::TSIG(s) } } impl MessageFinalizer for Signer { #[allow(unreachable_patterns, unused_variables)] fn finalize_message( &self, message: &Message, time: u32, ) -> ProtoResult<(Vec, Option)> { match self { #[cfg(feature = "dnssec")] #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))] Self::Sig0(s0) => s0.finalize_message(message, time), #[cfg(feature = "dnssec")] #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))] Self::TSIG(tsig) => tsig.finalize_message(message, time), _ => unreachable!("the feature `dnssec` is required for Message signing"), } } } /// Trait for client connections pub trait ClientConnection: 'static + Sized + Send + Sync + Unpin { /// The associated DNS RequestSender type. type Sender: DnsRequestSender; /// A future that resolves to the RequestSender type SenderFuture: Future> + 'static + Send + Unpin; /// Construct a new stream for use in the Client fn new_stream(&self, signer: Option>) -> Self::SenderFuture; } hickory-client-0.24.4/src/client/memoize_client_handle.rs000064400000000000000000000125521046102023000215750ustar 00000000000000// Copyright 2015-2016 Benjamin Fry // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. use std::collections::HashMap; use std::pin::Pin; use std::sync::Arc; use futures_util::future::FutureExt; use futures_util::lock::Mutex; use futures_util::stream::Stream; use hickory_proto::{ error::ProtoError, xfer::{DnsHandle, DnsRequest, DnsResponse}, }; use crate::client::rc_stream::{rc_stream, RcStream}; use crate::client::ClientHandle; use crate::op::Query; // TODO: move to proto /// A ClientHandle for memoized (cached) responses to queries. /// /// This wraps a ClientHandle, changing the implementation `send()` to store the response against /// the Message.Query that was sent. This should reduce network traffic especially during things /// like DNSSEC validation. *Warning* this will currently cache for the life of the Client. #[derive(Clone)] #[must_use = "queries can only be sent through a ClientHandle"] pub struct MemoizeClientHandle { client: H, active_queries: Arc::Response>>>>, } impl MemoizeClientHandle where H: ClientHandle, { /// Returns a new handle wrapping the specified client pub fn new(client: H) -> Self { Self { client, active_queries: Arc::new(Mutex::new(HashMap::new())), } } async fn inner_send( request: DnsRequest, active_queries: Arc::Response>>>>, client: H, ) -> impl Stream> { // TODO: what if we want to support multiple queries (non-standard)? let query = request.queries().first().expect("no query!").clone(); // lock all the currently running queries let mut active_queries = active_queries.lock().await; // TODO: we need to consider TTL on the records here at some point // If the query is running, grab that existing one... if let Some(rc_stream) = active_queries.get(&query) { return rc_stream.clone(); }; // Otherwise issue a new query and store in the map active_queries .entry(query) .or_insert_with(|| rc_stream(client.send(request))) .clone() } } impl DnsHandle for MemoizeClientHandle where H: ClientHandle, { type Response = Pin> + Send>>; type Error = ProtoError; fn send>(&self, request: R) -> Self::Response { let request = request.into(); Box::pin( Self::inner_send( request, Arc::clone(&self.active_queries), self.client.clone(), ) .flatten_stream(), ) } } #[cfg(test)] mod test { #![allow(clippy::dbg_macro, clippy::print_stdout)] use std::pin::Pin; use std::sync::Arc; use futures::lock::Mutex; use futures::*; use hickory_proto::{ error::ProtoError, xfer::{DnsHandle, DnsRequest, DnsResponse}, }; use crate::client::*; use crate::op::*; use crate::rr::*; use hickory_proto::xfer::FirstAnswer; #[derive(Clone)] struct TestClient { i: Arc>, } impl DnsHandle for TestClient { type Response = Pin> + Send>>; type Error = ProtoError; fn send + Send + 'static>(&self, request: R) -> Self::Response { let i = Arc::clone(&self.i); let future = async { let i = i; let request = request; let mut message = Message::new(); let mut i = i.lock().await; message.set_id(*i); println!( "sending {}: {}", *i, request.into().queries().first().expect("no query!").clone() ); *i += 1; Ok(DnsResponse::from_message(message).unwrap()) }; Box::pin(stream::once(future)) } } #[test] fn test_memoized() { use futures::executor::block_on; let client = MemoizeClientHandle::new(TestClient { i: Arc::new(Mutex::new(0)), }); let mut test1 = Message::new(); test1.add_query(Query::new().set_query_type(RecordType::A).clone()); let mut test2 = Message::new(); test2.add_query(Query::new().set_query_type(RecordType::AAAA).clone()); let result = block_on(client.send(test1.clone()).first_answer()) .ok() .unwrap(); assert_eq!(result.id(), 0); let result = block_on(client.send(test2.clone()).first_answer()) .ok() .unwrap(); assert_eq!(result.id(), 1); // should get the same result for each... let result = block_on(client.send(test1).first_answer()).ok().unwrap(); assert_eq!(result.id(), 0); let result = block_on(client.send(test2).first_answer()).ok().unwrap(); assert_eq!(result.id(), 1); } } hickory-client-0.24.4/src/client/mod.rs000064400000000000000000000030671046102023000160370ustar 00000000000000/* * Copyright (C) 2015-2016 Benjamin Fry * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! DNS Client associated classes for performing queries and other operations. pub(crate) mod async_client; #[cfg(feature = "dnssec")] #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))] pub(crate) mod async_secure_client; #[allow(clippy::module_inception)] mod client; pub mod client_connection; mod memoize_client_handle; mod rc_stream; #[allow(deprecated)] pub use self::async_client::{AsyncClient, ClientFuture, ClientHandle, ClientStreamingResponse}; #[cfg(feature = "dnssec")] #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))] pub use self::async_secure_client::{AsyncDnssecClient, AsyncSecureClientBuilder}; #[cfg(feature = "dnssec")] #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))] pub use self::client::SyncDnssecClient; #[allow(deprecated)] pub use self::client::{BlockingStream, Client, SyncClient}; pub use self::client_connection::ClientConnection; pub use self::client_connection::Signer; pub use self::memoize_client_handle::MemoizeClientHandle; hickory-client-0.24.4/src/client/rc_stream.rs000064400000000000000000000070431046102023000172350ustar 00000000000000// Copyright 2015-2016 Benjamin Fry // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; use futures_util::lock::Mutex; use futures_util::stream::{Fuse, Stream, StreamExt}; use futures_util::{ready, FutureExt}; #[allow(clippy::type_complexity)] #[must_use = "stream do nothing unless polled"] pub(crate) struct RcStream where S: Stream + Send + Unpin, S::Item: Clone + Send, { stream_and_result: Arc, Vec)>>, pos: usize, } pub(crate) fn rc_stream(stream: S) -> RcStream where S: Stream + Unpin + Send, S::Item: Clone + Send, { let stream_and_result = Arc::new(Mutex::new((stream.fuse(), vec![]))); RcStream { stream_and_result, pos: 0, } } impl Stream for RcStream where S: Stream + Send + Unpin, S::Item: Clone + Send, { type Item = S::Item; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { // try and get a mutable reference to execute the future // at least one caller should be able to get a mut reference... others will // wait for it to complete. let mut stream_and_result = ready!(self.stream_and_result.lock().poll_unpin(cx)); let (ref mut stream, ref mut stored_result) = *stream_and_result; if stored_result.len() > self.pos { let result = stored_result[self.pos].clone(); drop(stream_and_result); // release lock early to please borrow checker self.pos += 1; return Poll::Ready(Some(result)); } // if pending it's either done, or it's actually pending match stream.poll_next_unpin(cx) { Poll::Pending => Poll::Pending, Poll::Ready(result) => { if let Some(ref result) = result { stored_result.push(result.clone()); } Poll::Ready(result) } } } } impl Clone for RcStream where S: Stream + Send + Unpin, S::Item: Clone + Send + Unpin, { fn clone(&self) -> Self { Self { stream_and_result: Arc::clone(&self.stream_and_result), pos: 0, // index is not kept to allow to read first messages } } } #[cfg(test)] mod tests { use futures::executor::block_on; use futures::future; use futures_util::stream::once; use crate::proto::error::{ProtoError, ProtoErrorKind}; use crate::proto::xfer::FirstAnswer; use super::*; #[test] fn test_rc_stream() { let future = future::ok::(1_usize); let rc = rc_stream(once(future)); let i = block_on(rc.clone().first_answer()).ok().unwrap(); assert_eq!(i, 1); let i = block_on(rc.first_answer()).ok().unwrap(); assert_eq!(i, 1); } #[test] fn test_rc_stream_failed() { let future = future::err::(ProtoError::from(ProtoErrorKind::Busy)); let rc = rc_stream(once(future)); let i = block_on(rc.clone().first_answer()).err().unwrap(); assert!(matches!(i.kind(), ProtoErrorKind::Busy)); let i = block_on(rc.first_answer()).err().unwrap(); assert!(matches!(i.kind(), ProtoErrorKind::Busy)); } } hickory-client-0.24.4/src/error/client_error.rs000064400000000000000000000110521046102023000176130ustar 00000000000000// Copyright 2015-2020 Benjamin Fry // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. //! Error types for the crate use std::{fmt, io}; use futures_channel::mpsc; use thiserror::Error; #[cfg(feature = "backtrace")] use crate::proto::{trace, ExtBacktrace}; use hickory_proto::error::{DnsSecError, DnsSecErrorKind, ProtoError, ProtoErrorKind}; /// An alias for results returned by functions of this crate pub type Result = ::std::result::Result; /// The error kind for errors that get returned in the crate #[derive(Debug, Error)] #[non_exhaustive] pub enum ErrorKind { /// An error with an arbitrary message, referenced as &'static str #[error("{0}")] Message(&'static str), /// An error with an arbitrary message, stored as String #[error("{0}")] Msg(String), // foreign /// A dnssec error #[error("dnssec error")] DnsSec(#[from] DnsSecError), /// An error got returned from IO #[error("io error")] Io(#[from] std::io::Error), /// An error got returned by the hickory-proto crate #[error("proto error")] Proto(#[from] ProtoError), /// Queue send error #[error("error sending to mpsc: {0}")] SendError(#[from] mpsc::SendError), /// A request timed out #[error("request timed out")] Timeout, } impl Clone for ErrorKind { fn clone(&self) -> Self { use self::ErrorKind::*; match self { Message(msg) => Message(msg), Msg(ref msg) => Msg(msg.clone()), // foreign DnsSec(dnssec) => DnsSec(dnssec.clone()), Io(io) => Io(std::io::Error::from(io.kind())), Proto(proto) => Proto(proto.clone()), SendError(e) => SendError(e.clone()), Timeout => Timeout, } } } /// The error type for errors that get returned in the crate #[derive(Debug, Error, Clone)] pub struct Error { kind: ErrorKind, #[cfg(feature = "backtrace")] backtrack: Option, } impl Error { /// Get the kind of the error pub fn kind(&self) -> &ErrorKind { &self.kind } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { cfg_if::cfg_if! { if #[cfg(feature = "backtrace")] { if let Some(ref backtrace) = self.backtrack { fmt::Display::fmt(&self.kind, f)?; fmt::Debug::fmt(backtrace, f) } else { fmt::Display::fmt(&self.kind, f) } } else { fmt::Display::fmt(&self.kind, f) } } } } impl From for Error { fn from(kind: ErrorKind) -> Self { Self { kind, #[cfg(feature = "backtrace")] backtrack: trace!(), } } } impl From<&'static str> for Error { fn from(msg: &'static str) -> Self { ErrorKind::Message(msg).into() } } impl From for Error { fn from(e: mpsc::SendError) -> Self { ErrorKind::from(e).into() } } impl From for Error { fn from(msg: String) -> Self { ErrorKind::Msg(msg).into() } } impl From for Error { fn from(e: DnsSecError) -> Self { match *e.kind() { DnsSecErrorKind::Timeout => ErrorKind::Timeout.into(), _ => ErrorKind::from(e).into(), } } } impl From for Error { fn from(e: io::Error) -> Self { match e.kind() { io::ErrorKind::TimedOut => ErrorKind::Timeout.into(), _ => ErrorKind::from(e).into(), } } } impl From for Error { fn from(e: ProtoError) -> Self { match *e.kind() { ProtoErrorKind::Timeout => ErrorKind::Timeout.into(), _ => ErrorKind::from(e).into(), } } } impl From for io::Error { fn from(e: Error) -> Self { match *e.kind() { ErrorKind::Timeout => Self::new(io::ErrorKind::TimedOut, e), _ => Self::new(io::ErrorKind::Other, e), } } } #[test] fn test_conversion() { let io_error = io::Error::new(io::ErrorKind::TimedOut, "mock timeout"); let error = Error::from(io_error); match *error.kind() { ErrorKind::Timeout => (), _ => panic!("incorrect type: {}", error), } } hickory-client-0.24.4/src/error/mod.rs000064400000000000000000000015411046102023000157050ustar 00000000000000/* * Copyright (C) 2015 Benjamin Fry * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! All defined errors for Hickory DNS #![deny(missing_docs)] mod client_error; pub use self::client_error::Error as ClientError; pub use self::client_error::ErrorKind as ClientErrorKind; pub use self::client_error::Result as ClientResult; hickory-client-0.24.4/src/h2_client_connection.rs000064400000000000000000000063321046102023000200660ustar 00000000000000// Copyright 2015-2018 Benjamin Fry // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. //! UDP based DNS client connection for Client impls use std::marker::PhantomData; use std::net::SocketAddr; use std::sync::Arc; use hickory_proto::h2::{HttpsClientConnect, HttpsClientStream, HttpsClientStreamBuilder}; use hickory_proto::tcp::Connect; use rustls::ClientConfig; use crate::client::{ClientConnection, Signer}; /// UDP based DNS Client connection /// /// Use with `hickory_client::client::Client` impls #[derive(Clone)] pub struct HttpsClientConnection { name_server: SocketAddr, bind_addr: Option, dns_name: String, client_config: Arc, marker: PhantomData, } impl HttpsClientConnection { /// Creates a new client connection. /// /// *Note* this has side affects of starting the listening event_loop. Expect this to change in /// the future. /// /// # Arguments /// /// * `name_server` - IP and Port for the remote DNS resolver /// * `dns_name` - The DNS name, Subject Public Key Info (SPKI) name, as associated to a certificate /// * `client_config` - The TLS config #[allow(clippy::new_ret_no_self)] pub fn new( name_server: SocketAddr, dns_name: String, client_config: Arc, ) -> Self { Self::new_with_bind_addr(name_server, None, dns_name, client_config) } /// Creates a new client connection with a specified source address. /// /// *Note* this has side affects of starting the listening event_loop. Expect this to change in /// the future. /// /// # Arguments /// /// * `name_server` - IP and Port for the remote DNS resolver /// * `bind_addr` - IP and port to connect from /// * `dns_name` - The DNS name, Subject Public Key Info (SPKI) name, as associated to a certificate /// * `client_config` - The TLS config #[allow(clippy::new_ret_no_self)] pub fn new_with_bind_addr( name_server: SocketAddr, bind_addr: Option, dns_name: String, client_config: Arc, ) -> Self { Self { name_server, bind_addr, dns_name, client_config, marker: PhantomData, } } } impl ClientConnection for HttpsClientConnection where T: Connect, { type Sender = HttpsClientStream; type SenderFuture = HttpsClientConnect; fn new_stream( &self, // TODO: maybe signer needs to be applied in https... _signer: Option>, ) -> Self::SenderFuture { // TODO: maybe signer needs to be applied in https... let mut https_builder = HttpsClientStreamBuilder::with_client_config(Arc::clone(&self.client_config)); if let Some(bind_addr) = self.bind_addr { https_builder.bind_addr(bind_addr); } https_builder.build(self.name_server, self.dns_name.clone()) } } hickory-client-0.24.4/src/lib.rs000064400000000000000000000310441046102023000145440ustar 00000000000000/* * Copyright (C) 2015 Benjamin Fry * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // LIBRARY WARNINGS #![warn( clippy::default_trait_access, clippy::dbg_macro, clippy::print_stdout, clippy::unimplemented, clippy::use_self, missing_copy_implementations, missing_docs, non_snake_case, non_upper_case_globals, rust_2018_idioms, unreachable_pub )] #![allow( clippy::needless_doctest_main, clippy::single_component_path_imports, clippy::upper_case_acronyms, // can be removed on a major release boundary clippy::bool_to_int_with_if, )] #![recursion_limit = "1024"] #![cfg_attr(docsrs, feature(doc_cfg))] //! Hickory DNS is intended to be a fully compliant domain name server and client library. //! //! The Client library is responsible for the basic protocols responsible for communicating with DNS servers (authorities) and resolvers. It can be used for managing DNS records through the use of update operations. It is possible to send raw DNS Messages with the Client, but for ease of use the `query` and various other update operations are recommended for general use. //! //! For a system-like resolver, see [hickory-resolver](https://docs.rs/hickory-resolver). This is most likely what you want if all you want to do is lookup IP addresses. //! //! For serving DNS serving, see [hickory-server](https://docs.rs/hickory-server). //! //! # Goals //! //! * Only safe Rust //! * All errors handled //! * Simple to manage servers //! * High level abstraction for clients //! * Secure dynamic update //! * New features for securing public information //! //! # Usage //! //! This shows basic usage of the SyncClient. More examples will be associated directly with other types. //! //! ## Dependency //! //! ```toml //! [dependencies] //! hickory-client = "*" //! ``` //! //! By default DNSSEC validation is built in with OpenSSL, this can be disabled with: //! //! ```toml //! [dependencies] //! hickory-client = { version = "*", default-features = false } //! ``` //! //! ## Objects //! //! There are two variations of implementations of the Client. The `SyncClient`, a synchronous client, and the `AsyncClient`, a Tokio async client. `SyncClient` is an implementation of the `Client` trait, there is another implementation, `SyncDnssecClient`, which validates DNSSEC records. For these basic examples we'll only look at the `SyncClient` //! //! First we must decide on the type of connection, there are three supported by Hickory DNS today, UDP, TCP and TLS. TLS requires OpenSSL by default, see also [hickory-dns-native-tls](https://docs.rs/hickory-dns-native-tls) and [hickory-dns-rustls](https://docs.rs/hickory-dns-rustls) for other TLS options. //! //! ## Setup a connection //! //! ```rust //! use hickory_proto::DnsStreamHandle; //! use hickory_client::client::{Client, ClientConnection, SyncClient}; //! use hickory_client::udp::UdpClientConnection; //! //! let address = "8.8.8.8:53".parse().unwrap(); //! let conn = UdpClientConnection::new(address).unwrap(); //! //! // and then create the Client //! let client = SyncClient::new(conn); //! ``` //! //! At this point the client is ready to be used. See also `client::SyncDnssecClient` for DNSSEC validation. The rest of these examples will assume that the above boilerplate has already been performed. //! //! ## Querying //! //! Using the Client to query for DNS records is easy enough, though it performs no resolution. The `hickory-resolver` has a simpler interface if that's what is desired. Over time that library will gain more features to generically query for different types. //! //! ```rust //! use std::net::Ipv4Addr; //! use std::str::FromStr; //! # use hickory_client::client::{Client, SyncClient}; //! # use hickory_client::udp::UdpClientConnection; //! use hickory_client::op::DnsResponse; //! use hickory_client::rr::{DNSClass, Name, RData, Record, RecordType}; //! use hickory_client::rr::rdata::A; //! # //! # let address = "8.8.8.8:53".parse().unwrap(); //! # let conn = UdpClientConnection::new(address).unwrap(); //! # let client = SyncClient::new(conn); //! //! // Specify the name, note the final '.' which specifies it's an FQDN //! let name = Name::from_str("www.example.com.").unwrap(); //! //! // NOTE: see 'Setup a connection' example above //! // Send the query and get a message response, see RecordType for all supported options //! let response: DnsResponse = client.query(&name, DNSClass::IN, RecordType::A).unwrap(); //! //! // Messages are the packets sent between client and server in DNS. //! // there are many fields to a Message, DnsResponse can be dereferenced into //! // a Message. It's beyond the scope of these examples //! // to explain all the details of a Message. See hickory_client::op::message::Message for more details. //! // generally we will be interested in the Message::answers //! let answers: &[Record] = response.answers(); //! //! // Records are generic objects which can contain any data. //! // In order to access it we need to first check what type of record it is //! // In this case we are interested in A, IPv4 address //! assert!(answers.iter().any(|record| record.data().unwrap().as_a().is_some())); //! ``` //! //! In the above example we successfully queried for a A record. There are many other types, each can be independently queried and the associated `hickory_client::rr::record_data::RData` has a variant with the deserialized data for the record stored. //! //! ## Dynamic update //! //! Currently `hickory-client` supports SIG(0) signed records for authentication and authorization of dynamic DNS updates. It's beyond the scope of these examples to show how to setup SIG(0) authorization on the server. `hickory-client` is known to work with BIND9 and `hickory-server`. Expect in the future for TLS to become a potentially better option for authorization with certificate chains. These examples show using SIG(0) for auth, requires OpenSSL. It's beyond the scope of these examples to describe the configuration for the server. //! //! ```rust,no_run //! //! #[cfg(all(feature = "openssl", feature = "dnssec"))] //! # fn main() { //! //! use std::fs::File; //! use std::io::Read; //! use std::net::Ipv4Addr; //! use std::str::FromStr; //! //! # #[cfg(feature = "openssl")] //! use openssl::rsa::Rsa; //! use hickory_client::client::{Client, SyncClient}; //! use hickory_client::udp::UdpClientConnection; //! use hickory_client::rr::{Name, RData, Record, RecordType}; //! use hickory_client::proto::rr::dnssec::{Algorithm, SigSigner, KeyPair}; //! use hickory_client::op::ResponseCode; //! use hickory_client::rr::rdata::{A, key::KEY}; //! //! # let address = "0.0.0.0:53".parse().unwrap(); //! # let conn = UdpClientConnection::new(address).unwrap(); //! //! // The format of the key is dependent on the KeyPair type, in this example we're using RSA //! // if the key was generated with BIND, the binary in Hickory DNS client lib `dnskey-to-pem` //! // can be used to convert this to a pem file //! let mut pem = File::open("my_private_key.pem").unwrap(); //! let mut pem_buf = Vec::::new(); //! pem.read_to_end(&mut pem_buf).unwrap(); //! //! // Create the RSA key //! let rsa = Rsa::private_key_from_pem(&pem_buf).unwrap(); //! let key = KeyPair::from_rsa(rsa).unwrap(); //! //! // Create the RData KEY associated with the key. This example uses defaults for all the //! // KeyTrust, KeyUsage, UpdateScope, Protocol. Many of these have been deprecated in current //! // DNS RFCs, but are still supported by many servers for auth. See auth docs of the remote //! // server for help in understanding it's requirements and support of these options. //! let sig0key = KEY::new(Default::default(), //! Default::default(), //! Default::default(), //! Default::default(), //! Algorithm::RSASHA256, //! key.to_public_bytes().unwrap()); //! //! // Create the Hickory DNS SIG(0) signing facility. Generally the signer_name is the label //! // associated with KEY record in the server. //! let signer = SigSigner::sig0(sig0key, //! key, //! Name::from_str("update.example.com.").unwrap()); //! //! // Create the DNS client, see above for creating the connection //! let client = SyncClient::with_signer(conn, signer); //! //! // At this point we should have a client capable of sending signed SIG(0) records. //! //! // Now we can send updates... let's create a new Record //! let mut record = Record::with(Name::from_str("new.example.com").unwrap(), //! RecordType::A, //! 300); //! record.set_data(Some(RData::A(A::new(100, 10, 100, 10)))); //! //! // the server must be authoritative for this zone //! let origin = Name::from_str("example.com.").unwrap(); //! //! // Create the record. //! let result = client.create(record, origin).unwrap(); //! assert_eq!(result.response_code(), ResponseCode::NoError); //! # } //! # #[cfg(not(all(feature = "openssl", feature = "dnssec")))] //! # fn main() { //! # } //! ``` //! //! *Note*: The dynamic DNS functions defined by Hickory DNS are expressed as atomic operations, but this depends on support of the remote server. For example, the `create` operation shown above, should only succeed if there is no `RecordSet` of the specified type at the specified label. The other update operations are `append`, `compare_and_swap`, `delete_by_rdata`, `delete_rrset`, and `delete_all`. See the documentation for each of these methods on the `Client` trait. //! //! //! ## Async client usage //! //! This example is meant to show basic usage, using the #[tokio::main] macro to setup a simple runtime. //! The Tokio documentation should be reviewed for more advanced usage. //! //! ```rust //! use std::net::Ipv4Addr; //! use std::str::FromStr; //! use tokio::net::TcpStream as TokioTcpStream; //! use hickory_client::client::{AsyncClient, ClientHandle}; //! use hickory_client::proto::iocompat::AsyncIoTokioAsStd; //! use hickory_client::rr::{DNSClass, Name, RData, RecordType}; //! use hickory_client::rr::rdata::A; //! use hickory_client::tcp::TcpClientStream; //! //! #[tokio::main] //! async fn main() { //! // Since we used UDP in the previous examples, let's change things up a bit and use TCP here //! let (stream, sender) = //! TcpClientStream::>::new(([8, 8, 8, 8], 53).into()); //! //! // Create a new client, the bg is a background future which handles //! // the multiplexing of the DNS requests to the server. //! // the client is a handle to an unbounded queue for sending requests via the //! // background. The background must be scheduled to run before the client can //! // send any dns requests //! let client = AsyncClient::new(stream, sender, None); //! //! // await the connection to be established //! let (mut client, bg) = client.await.expect("connection failed"); //! //! // make sure to run the background task //! tokio::spawn(bg); //! //! // Create a query future //! let query = client.query( //! Name::from_str("www.example.com.").unwrap(), //! DNSClass::IN, //! RecordType::A, //! ); //! //! // wait for its response //! let response = query.await.unwrap(); //! //! // validate it's what we expected //! if let Some(RData::A(addr)) = response.answers()[0].data() { //! assert_eq!(*addr, A::new(93, 184, 215, 14)); //! } //! } //! ``` pub mod client; pub mod error; #[cfg(feature = "mdns")] #[cfg_attr(docsrs, doc(cfg(feature = "mdns")))] pub mod multicast; pub mod op; pub mod rr; pub mod serialize; pub mod tcp; pub mod udp; // TODO: consider removing tcp/udp/https modules... #[cfg(feature = "dns-over-https")] mod h2_client_connection; pub use hickory_proto as proto; /// The https module which contains all https related connection types #[cfg(feature = "dns-over-https")] #[cfg_attr(docsrs, doc(cfg(feature = "dns-over-https")))] pub mod h2 { pub use super::h2_client_connection::HttpsClientConnection; } /// Returns a version as specified in Cargo.toml pub fn version() -> &'static str { env!("CARGO_PKG_VERSION") } hickory-client-0.24.4/src/multicast/mdns_client_connection.rs000064400000000000000000000041531046102023000225220ustar 00000000000000// Copyright 2015-2018 Benjamin Fry // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. //! MDNS based DNS client connection for Client impls use std::net::{Ipv4Addr, SocketAddr}; use std::sync::Arc; use crate::proto::{ multicast::{MdnsClientConnect, MdnsClientStream, MdnsQueryType, MDNS_IPV4, MDNS_IPV6}, xfer::{DnsMultiplexer, DnsMultiplexerConnect}, }; use crate::client::{ClientConnection, Signer}; /// MDNS based DNS Client connection /// /// Use with `hickory_client::client::Client` impls #[derive(Clone, Copy)] pub struct MdnsClientConnection { multicast_addr: SocketAddr, packet_ttl: Option, ipv4_if: Option, ipv6_if: Option, } impl MdnsClientConnection { /// associates the socket to the well-known ipv4 multicast address pub fn new_ipv4(packet_ttl: Option, ipv4_if: Option) -> Self { Self { multicast_addr: *MDNS_IPV4, packet_ttl, ipv4_if, ipv6_if: None, } } /// associates the socket to the well-known ipv6 multicast address pub fn new_ipv6(packet_ttl: Option, ipv6_if: Option) -> Self { Self { multicast_addr: *MDNS_IPV6, packet_ttl, ipv4_if: None, ipv6_if, } } } impl ClientConnection for MdnsClientConnection { type Sender = DnsMultiplexer; type SenderFuture = DnsMultiplexerConnect; fn new_stream(&self, signer: Option>) -> Self::SenderFuture { let (mdns_client_stream, handle) = MdnsClientStream::new( self.multicast_addr, MdnsQueryType::OneShot, self.packet_ttl, self.ipv4_if, self.ipv6_if, ); DnsMultiplexer::new(mdns_client_stream, handle, signer) } } hickory-client-0.24.4/src/multicast/mod.rs000064400000000000000000000011461046102023000165620ustar 00000000000000// Copyright 2015-2018 Benjamin Fry // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. //! UDP protocol related components for DNS mod mdns_client_connection; use crate::proto::multicast; pub use self::mdns_client_connection::MdnsClientConnection; pub use self::multicast::{MdnsClientStream, MdnsQueryType, MdnsStream, MDNS_IPV4, MDNS_IPV6}; hickory-client-0.24.4/src/op/mod.rs000064400000000000000000000013241046102023000151710ustar 00000000000000// Copyright 2015-2017 Benjamin Fry // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. //! Operations to send with a `Client` or server, e.g. `Query`, `Message`, or `UpdateMessage` can //! be used together to either query or update resource records sets. pub use crate::proto::{ op::{ Edns, Header, Message, MessageFinalizer, MessageType, MessageVerifier, OpCode, Query, ResponseCode, UpdateMessage, }, xfer::DnsResponse, }; hickory-client-0.24.4/src/rr/mod.rs000064400000000000000000000023141046102023000151760ustar 00000000000000// Copyright 2015-2023 Benjamin Fry // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. //! Resource record related components, e.g. `Name` aka label, `Record`, `RData`, ... pub mod zone; use crate::proto::rr; pub use crate::proto::rr::dns_class; pub use crate::proto::rr::domain; pub use crate::proto::rr::record_data; pub use crate::proto::rr::record_type; pub use crate::proto::rr::resource; pub use self::dns_class::DNSClass; pub use self::record_data::RData; pub use self::record_type::RecordType; pub use self::resource::Record; pub use self::rr::domain::{IntoName, Label, Name}; #[allow(deprecated)] pub use self::rr::IntoRecordSet; pub use self::rr::RecordData; pub use self::rr::RecordSet; /// All record data structures and related serialization methods pub mod rdata { #[cfg(feature = "dnssec")] #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))] pub use crate::proto::rr::dnssec::rdata::*; pub use crate::proto::rr::rdata::*; } hickory-client-0.24.4/src/rr/zone.rs000064400000000000000000000453231046102023000154010ustar 00000000000000//! Reserved Zone and related information pub use crate::proto::rr::domain::usage::*; use crate::proto::rr::domain::{Label, Name}; use crate::proto::serialize::binary::BinEncodable; use once_cell::sync::Lazy; use radix_trie::{Trie, TrieKey}; // Reserved reverse IPs // // [Special-Use Domain Names](https://tools.ietf.org/html/rfc6761), RFC 6761 February, 2013 // // ```text // 6.1. Domain Name Reservation Considerations for Private Addresses // // The private-address [RFC1918] reverse-mapping domains listed below, // and any names falling within those domains, are Special-Use Domain // Names: // // 10.in-addr.arpa. 21.172.in-addr.arpa. 26.172.in-addr.arpa. // 16.172.in-addr.arpa. 22.172.in-addr.arpa. 27.172.in-addr.arpa. // 17.172.in-addr.arpa. 30.172.in-addr.arpa. 28.172.in-addr.arpa. // 18.172.in-addr.arpa. 23.172.in-addr.arpa. 29.172.in-addr.arpa. // 19.172.in-addr.arpa. 24.172.in-addr.arpa. 31.172.in-addr.arpa. // 20.172.in-addr.arpa. 25.172.in-addr.arpa. 168.192.in-addr.arpa. // ``` /// 10.in-addr.arpa. usage pub static IN_ADDR_ARPA_10: Lazy = Lazy::new(|| { ZoneUsage::reverse( Name::from_ascii("10") .unwrap() .append_domain(&IN_ADDR_ARPA) .unwrap(), ) }); static IN_ADDR_ARPA_172: Lazy = Lazy::new(|| { Name::from_ascii("172") .unwrap() .append_domain(&IN_ADDR_ARPA) .unwrap() }); /// 16.172.in-addr.arpa. usage pub static IN_ADDR_ARPA_172_16: Lazy = Lazy::new(|| { ZoneUsage::reverse( Name::from_ascii("16") .unwrap() .append_domain(&IN_ADDR_ARPA_172) .unwrap(), ) }); /// 17.172.in-addr.arpa. usage pub static IN_ADDR_ARPA_172_17: Lazy = Lazy::new(|| { ZoneUsage::reverse( Name::from_ascii("17") .unwrap() .append_domain(&IN_ADDR_ARPA_172) .unwrap(), ) }); /// 18.172.in-addr.arpa. usage pub static IN_ADDR_ARPA_172_18: Lazy = Lazy::new(|| { ZoneUsage::reverse( Name::from_ascii("18") .unwrap() .append_domain(&IN_ADDR_ARPA_172) .unwrap(), ) }); /// 19.172.in-addr.arpa. usage pub static IN_ADDR_ARPA_172_19: Lazy = Lazy::new(|| { ZoneUsage::reverse( Name::from_ascii("19") .unwrap() .append_domain(&IN_ADDR_ARPA_172) .unwrap(), ) }); /// 20.172.in-addr.arpa. usage pub static IN_ADDR_ARPA_172_20: Lazy = Lazy::new(|| { ZoneUsage::reverse( Name::from_ascii("20") .unwrap() .append_domain(&IN_ADDR_ARPA_172) .unwrap(), ) }); /// 21.172.in-addr.arpa. usage pub static IN_ADDR_ARPA_172_21: Lazy = Lazy::new(|| { ZoneUsage::reverse( Name::from_ascii("21") .unwrap() .append_domain(&IN_ADDR_ARPA_172) .unwrap(), ) }); /// 22.172.in-addr.arpa. usage pub static IN_ADDR_ARPA_172_22: Lazy = Lazy::new(|| { ZoneUsage::reverse( Name::from_ascii("22") .unwrap() .append_domain(&IN_ADDR_ARPA_172) .unwrap(), ) }); /// 23.172.in-addr.arpa. usage pub static IN_ADDR_ARPA_172_23: Lazy = Lazy::new(|| { ZoneUsage::reverse( Name::from_ascii("23") .unwrap() .append_domain(&IN_ADDR_ARPA_172) .unwrap(), ) }); /// 24.172.in-addr.arpa. usage pub static IN_ADDR_ARPA_172_24: Lazy = Lazy::new(|| { ZoneUsage::reverse( Name::from_ascii("24") .unwrap() .append_domain(&IN_ADDR_ARPA_172) .unwrap(), ) }); /// 25.172.in-addr.arpa. usage pub static IN_ADDR_ARPA_172_25: Lazy = Lazy::new(|| { ZoneUsage::reverse( Name::from_ascii("25") .unwrap() .append_domain(&IN_ADDR_ARPA_172) .unwrap(), ) }); /// 26.172.in-addr.arpa. usage pub static IN_ADDR_ARPA_172_26: Lazy = Lazy::new(|| { ZoneUsage::reverse( Name::from_ascii("26") .unwrap() .append_domain(&IN_ADDR_ARPA_172) .unwrap(), ) }); /// 27.172.in-addr.arpa. usage pub static IN_ADDR_ARPA_172_27: Lazy = Lazy::new(|| { ZoneUsage::reverse( Name::from_ascii("27") .unwrap() .append_domain(&IN_ADDR_ARPA_172) .unwrap(), ) }); /// 28.172.in-addr.arpa. usage pub static IN_ADDR_ARPA_172_28: Lazy = Lazy::new(|| { ZoneUsage::reverse( Name::from_ascii("28") .unwrap() .append_domain(&IN_ADDR_ARPA_172) .unwrap(), ) }); /// 29.172.in-addr.arpa. usage pub static IN_ADDR_ARPA_172_29: Lazy = Lazy::new(|| { ZoneUsage::reverse( Name::from_ascii("29") .unwrap() .append_domain(&IN_ADDR_ARPA_172) .unwrap(), ) }); /// 30.172.in-addr.arpa. usage pub static IN_ADDR_ARPA_172_30: Lazy = Lazy::new(|| { ZoneUsage::reverse( Name::from_ascii("30") .unwrap() .append_domain(&IN_ADDR_ARPA_172) .unwrap(), ) }); /// 31.172.in-addr.arpa. usage pub static IN_ADDR_ARPA_172_31: Lazy = Lazy::new(|| { ZoneUsage::reverse( Name::from_ascii("31") .unwrap() .append_domain(&IN_ADDR_ARPA_172) .unwrap(), ) }); /// 168.192.in-addr.arpa. usage pub static IN_ADDR_ARPA_192_168: Lazy = Lazy::new(|| { ZoneUsage::reverse( Name::from_ascii("168.192") .unwrap() .append_domain(&IN_ADDR_ARPA) .unwrap(), ) }); // example., example.com., example.net., and example.org. // // [Special-Use Domain Names](https://tools.ietf.org/html/rfc6761), RFC 6761 February, 2013 // // ```text // 6.5. Domain Name Reservation Considerations for Example Domains // // The domains "example.", "example.com.", "example.net.", // "example.org.", and any names falling within those domains, are // special in the following ways: // ``` static COM: Lazy