Compare commits

..

No commits in common. "de8333d1646b9450a50943a5d198cea59489168d" and "2a097fb800302fd238244e42581c01e02bfdee10" have entirely different histories.

10 changed files with 93 additions and 855 deletions

570
Cargo.lock generated
View file

@ -17,17 +17,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "async-trait"
version = "0.1.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.3.0" version = "1.3.0"
@ -55,12 +44,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "base64"
version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]] [[package]]
name = "base64ct" name = "base64ct"
version = "1.6.0" version = "1.6.0"
@ -82,18 +65,6 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "bumpalo"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.7.1" version = "1.7.1"
@ -151,12 +122,6 @@ dependencies = [
"parking_lot_core", "parking_lot_core",
] ]
[[package]]
name = "data-encoding"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.10.7" version = "0.10.7"
@ -167,36 +132,12 @@ dependencies = [
"crypto-common", "crypto-common",
] ]
[[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.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.1" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
version = "1.2.1" version = "1.2.1"
@ -206,52 +147,6 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "futures-channel"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
dependencies = [
"futures-core",
]
[[package]]
name = "futures-core"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-io"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
[[package]]
name = "futures-sink"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
[[package]]
name = "futures-task"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]]
name = "futures-util"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]] [[package]]
name = "generic-array" name = "generic-array"
version = "0.14.7" version = "0.14.7"
@ -279,115 +174,18 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
[[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]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.14.5" version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.3.9" version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "hickory-client"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab9683b08d8f8957a857b0236455d80e1886eaa8c6178af556aa7871fb61b55"
dependencies = [
"cfg-if",
"data-encoding",
"futures-channel",
"futures-util",
"hickory-proto",
"once_cell",
"radix_trie",
"rand",
"rustls 0.21.12",
"thiserror",
"tokio",
"tracing",
]
[[package]]
name = "hickory-proto"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512"
dependencies = [
"async-trait",
"bytes",
"cfg-if",
"data-encoding",
"enum-as-inner",
"futures-channel",
"futures-io",
"futures-util",
"h2",
"http",
"idna 0.4.0",
"ipnet",
"once_cell",
"rand",
"ring 0.16.20",
"rustls 0.21.12",
"rustls-pemfile",
"thiserror",
"tinyvec",
"tokio",
"tokio-rustls 0.24.1",
"tracing",
"url",
]
[[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 = "idna"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.5.0" version = "0.5.0"
@ -408,27 +206,6 @@ dependencies = [
"hashbrown", "hashbrown",
] ]
[[package]]
name = "ipnet"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "js-sys"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
dependencies = [
"wasm-bindgen",
]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.155" version = "0.2.155"
@ -445,12 +222,6 @@ dependencies = [
"scopeguard", "scopeguard",
] ]
[[package]]
name = "log"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.4" version = "2.7.4"
@ -484,15 +255,6 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "nibble_vec"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
dependencies = [
"smallvec",
]
[[package]] [[package]]
name = "num_enum" name = "num_enum"
version = "0.7.3" version = "0.7.3"
@ -554,21 +316,6 @@ version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "ppv-lite86"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [
"zerocopy",
]
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "3.1.0" version = "3.1.0"
@ -596,46 +343,6 @@ dependencies = [
"proc-macro2", "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]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.5.3" version = "0.5.3"
@ -645,21 +352,6 @@ dependencies = [
"bitflags", "bitflags",
] ]
[[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]] [[package]]
name = "ring" name = "ring"
version = "0.17.8" version = "0.17.8"
@ -670,8 +362,8 @@ dependencies = [
"cfg-if", "cfg-if",
"getrandom", "getrandom",
"libc", "libc",
"spin 0.9.8", "spin",
"untrusted 0.9.0", "untrusted",
"windows-sys", "windows-sys",
] ]
@ -681,18 +373,6 @@ version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[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 0.101.7",
"sct",
]
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.23.12" version = "0.23.12"
@ -700,47 +380,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"ring 0.17.8", "ring",
"rustls-pki-types", "rustls-pki-types",
"rustls-webpki 0.102.6", "rustls-webpki",
"subtle", "subtle",
"zeroize", "zeroize",
] ]
[[package]]
name = "rustls-pemfile"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
dependencies = [
"base64",
]
[[package]] [[package]]
name = "rustls-pki-types" name = "rustls-pki-types"
version = "1.7.0" version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
[[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]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.102.6" version = "0.102.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e"
dependencies = [ dependencies = [
"ring 0.17.8", "ring",
"rustls-pki-types", "rustls-pki-types",
"untrusted 0.9.0", "untrusted",
] ]
[[package]] [[package]]
@ -749,16 +410,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[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]] [[package]]
name = "sha2" name = "sha2"
version = "0.10.8" version = "0.10.8"
@ -770,15 +421,6 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.13.2" version = "1.13.2"
@ -795,12 +437,6 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]] [[package]]
name = "spin" name = "spin"
version = "0.9.8" version = "0.9.8"
@ -824,26 +460,6 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "thiserror"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.8.0" version = "1.8.0"
@ -883,12 +499,11 @@ dependencies = [
"base64ct", "base64ct",
"bytes", "bytes",
"dashmap", "dashmap",
"hickory-client",
"mime", "mime",
"num_enum", "num_enum",
"sha2", "sha2",
"tokio", "tokio",
"tokio-rustls 0.26.0", "tokio-rustls",
"url", "url",
"webpki-roots", "webpki-roots",
] ]
@ -904,40 +519,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "tokio-rustls"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [
"rustls 0.21.12",
"tokio",
]
[[package]] [[package]]
name = "tokio-rustls" name = "tokio-rustls"
version = "0.26.0" version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
dependencies = [ dependencies = [
"rustls 0.23.12", "rustls",
"rustls-pki-types", "rustls-pki-types",
"tokio", "tokio",
] ]
[[package]]
name = "tokio-util"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
]
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.6.8" version = "0.6.8"
@ -955,37 +547,6 @@ dependencies = [
"winnow", "winnow",
] ]
[[package]]
name = "tracing"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [
"once_cell",
]
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.17.0" version = "1.17.0"
@ -1013,12 +574,6 @@ dependencies = [
"tinyvec", "tinyvec",
] ]
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]] [[package]]
name = "untrusted" name = "untrusted"
version = "0.9.0" version = "0.9.0"
@ -1032,7 +587,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
dependencies = [ dependencies = [
"form_urlencoded", "form_urlencoded",
"idna 0.5.0", "idna",
"percent-encoding", "percent-encoding",
] ]
@ -1048,70 +603,6 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "web-sys"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "webpki-roots" name = "webpki-roots"
version = "0.26.3" version = "0.26.3"
@ -1121,28 +612,6 @@ dependencies = [
"rustls-pki-types", "rustls-pki-types",
] ]
[[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]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.52.0" version = "0.52.0"
@ -1225,27 +694,6 @@ dependencies = [
"memchr", "memchr",
] ]
[[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]] [[package]]
name = "zeroize" name = "zeroize"
version = "1.8.1" version = "1.8.1"

View file

@ -24,7 +24,6 @@ tokio-rustls = { version = "0.26.0", default-features = false, features = ["ring
webpki-roots = { version = "0.26.3", optional = true } webpki-roots = { version = "0.26.3", optional = true }
dashmap = { version = "6.0.1", optional = true } dashmap = { version = "6.0.1", optional = true }
hickory-client = { version = "0.24.1", optional = true }
[dev-dependencies] [dev-dependencies]
tokio = { version = "1.39.2", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.39.2", features = ["macros", "rt-multi-thread"] }
@ -33,12 +32,6 @@ tokio = { version = "1.39.2", features = ["macros", "rt-multi-thread"] }
webpki = ["dep:webpki-roots"] webpki = ["dep:webpki-roots"]
file-sscv = ["dep:dashmap", "tokio/fs"] file-sscv = ["dep:dashmap", "tokio/fs"]
hickory = ["dep:hickory-client"]
hickory-dot = ["hickory", "hickory-client/dns-over-rustls"]
hickory-doh = ["hickory", "hickory-client/dns-over-https-rustls"]
# TODO:
#hickory-dnssec = ["hickory", "hickory-client/dnssec-ring"]
[[example]] [[example]]
name = "simple" name = "simple"
path = "examples/simple.rs" path = "examples/simple.rs"
@ -47,8 +40,3 @@ path = "examples/simple.rs"
name = "main" name = "main"
path = "examples/main.rs" path = "examples/main.rs"
required-features = ["file-sscv"] required-features = ["file-sscv"]
[[example]]
name = "dane"
path = "examples/dane.rs"
required-features = ["hickory"]

View file

@ -1,16 +0,0 @@
use tokio_gemini::dns::DnsClient;
#[tokio::main]
async fn main() {
let mut client = DnsClient::init(("1.1.1.1", 53)).await.unwrap();
println!(
"{}",
client
.query_tlsa("torproject.org", 443)
.await
.unwrap()
.next()
.unwrap()
.hex()
);
}

View file

@ -1,5 +1,8 @@
use tokio_gemini::{ use tokio_gemini::{
certs::{fingerprint::CertFingerprint, SelfsignedCertVerifier}, certs::{
fingerprint::{self, CertFingerprint},
SelfsignedCertVerifier,
},
Client, LibError, Client, LibError,
}; };
@ -44,7 +47,7 @@ impl SelfsignedCertVerifier for CertVerifier {
eprintln!( eprintln!(
"Host = {}\nFingerprint = {}", "Host = {}\nFingerprint = {}",
host, host,
CertFingerprint::new_sha256(cert).base64(), CertFingerprint::<fingerprint::Sha256>::new(cert).base64(),
); );
Ok(true) Ok(true)
} }

View file

@ -5,7 +5,10 @@ use tokio::io::AsyncBufReadExt;
use tokio_rustls::rustls::pki_types::{CertificateDer, UnixTime}; use tokio_rustls::rustls::pki_types::{CertificateDer, UnixTime};
use crate::{ use crate::{
certs::{fingerprint::CertFingerprint, SelfsignedCert, SelfsignedCertVerifier}, certs::{
fingerprint::{self, CertFingerprint, HashAlgo},
SelfsignedCert, SelfsignedCertVerifier,
},
LibError, LibError,
}; };
@ -45,27 +48,20 @@ impl FileBasedCertVerifier {
continue; continue;
}; };
let fp = match algo { let algo = match algo {
"sha256" => CertFingerprint::try_from_sha256_b64(fp), "sha256" => HashAlgo::Sha256,
"sha512" => CertFingerprint::try_from_sha512_b64(fp), "sha512" => HashAlgo::Sha512,
_ => { _ => {
eprintln!("Unknown hash algorithm {:?}, skipping", algo); eprintln!("Unknown hash algorithm {:?}, skipping", algo);
continue; continue;
} }
}; };
let fp = match fp {
Ok(fp) => fp,
Err(e) => {
eprintln!("Fingerprint decoding error: {:?}", e);
continue;
}
};
map.insert( map.insert(
host.to_owned(), host.to_owned(),
SelfsignedCert { SelfsignedCert {
fingerprint: fp, algo,
fingerprint: fp.to_owned(),
expires, expires,
}, },
); );
@ -102,15 +98,25 @@ impl SelfsignedCertVerifier for FileBasedCertVerifier {
if let Some(known_cert) = self.map.get(host) { if let Some(known_cert) = self.map.get(host) {
// if host is found in known_hosts, compare certs // if host is found in known_hosts, compare certs
let this_fp = match known_cert.fingerprint { let this_fp = match known_cert.algo {
CertFingerprint::Sha256(_) => CertFingerprint::new_sha256(cert), HashAlgo::Sha256 => CertFingerprint::<fingerprint::Sha256>::new(cert).base64(),
CertFingerprint::Sha512(_) => CertFingerprint::new_sha512(cert), HashAlgo::Sha512 => CertFingerprint::<fingerprint::Sha512>::new(cert).base64(),
_ => unreachable!(),
}; };
Ok(this_fp == known_cert.fingerprint) if this_fp == known_cert.fingerprint {
// current cert hash matches known cert hash
eprintln!("Cert for {} matched: {}", &host, &this_fp);
Ok(true)
} else {
// TODO (after implementing `expires`) update cert if known is expired
eprintln!(
"Error: certs do not match! Possibly MitM attack.\nKnown FP: {}\nGot: {}",
&known_cert.fingerprint, &this_fp,
);
Ok(false)
}
} else { } else {
// host is unknown, generate hash and add to known_hosts // host is unknown, generate hash and add to known_hosts
let this_hash = CertFingerprint::new_sha256(cert); let this_hash = CertFingerprint::<fingerprint::Sha256>::new(cert);
let this_fp = this_hash.base64(); let this_fp = this_hash.base64();
// TODO: DANE cert check, use this_hash.hex() for this // TODO: DANE cert check, use this_hash.hex() for this
eprintln!( eprintln!(
@ -135,7 +141,8 @@ impl SelfsignedCertVerifier for FileBasedCertVerifier {
self.map.insert( self.map.insert(
host.to_owned(), host.to_owned(),
SelfsignedCert { SelfsignedCert {
fingerprint: this_hash, algo: HashAlgo::Sha256,
fingerprint: this_fp,
expires: 0, // TODO after implementing cert parsing in tokio-gemini expires: 0, // TODO after implementing cert parsing in tokio-gemini
}, },
); );

View file

@ -1,8 +1,5 @@
//! TLS cert fingerprint generators //! TLS cert fingerprint generators
use std::array::TryFromSliceError;
use bytes::Bytes;
pub use sha2::{Digest, Sha256, Sha512}; pub use sha2::{Digest, Sha256, Sha512};
use base16ct::upper as b16; use base16ct::upper as b16;
@ -10,118 +7,75 @@ use base64ct::{Base64Unpadded as b64, Encoding};
use super::verifier::CertificateDer; use super::verifier::CertificateDer;
pub const SHA256_LEN: usize = 32; // 256 / 8
pub const SHA512_LEN: usize = 64; // 512 / 8
#[allow(clippy::inconsistent_digit_grouping)]
pub const SHA256_HEX_LEN: usize = 64_; // (256 / 8) * 2 pub const SHA256_HEX_LEN: usize = 64_; // (256 / 8) * 2
pub const SHA512_HEX_LEN: usize = 128; // (512 / 8) * 2 pub const SHA512_HEX_LEN: usize = 128; // (512 / 8) * 2
pub const SHA256_B64_LEN: usize = 44; // 4 * ((256 / 8) as f64 / 3 as f64).ceil() pub const SHA256_B64_LEN: usize = 44; // 4 * ((256 / 8) as f64 / 3 as f64).ceil()
pub const SHA512_B64_LEN: usize = 88; // 4 * ((512 / 8) as f64 / 3 as f64).ceil() pub const SHA512_B64_LEN: usize = 88; // 4 * ((512 / 8) as f64 / 3 as f64).ceil()
/// Enum holding a TLS cert hash or raw cert bytes. /// Supported hashing algorithms
/// Provides hex (base16) and base64 bin-to-text methods, #[derive(Debug, Clone, Copy)]
pub enum HashAlgo {
Sha256,
Sha512,
}
/// Structure holding a TLS cert hash
/// and providing bin2text methods,
/// mostly for use in [`crate::certs::SelfsignedCertVerifier`] /// mostly for use in [`crate::certs::SelfsignedCertVerifier`]
#[derive(Debug, Clone, PartialEq, Eq)] pub struct CertFingerprint<T: Digest> {
pub enum CertFingerprint { hash: sha2::digest::Output<T>,
Sha256([u8; SHA256_LEN]),
Sha512([u8; SHA512_LEN]),
Raw(Bytes),
} }
impl CertFingerprint { impl<T: Digest> CertFingerprint<T> {
pub fn new_sha256(cert: &CertificateDer) -> Self { /// Generate a TLS cert hash.
let mut hasher = Sha256::new(); ///
/// # Examples
/// ```
/// use tokio_gemini::certs::fingerprint::{CertFingerprint, Sha256};
///
/// let hash = CertFingerprint::<Sha256>::new(rustls_cert);
/// let fingerprint = hash.base64();
/// ```
pub fn new(cert: &CertificateDer) -> Self {
let mut hasher = T::new();
for chunk in cert.chunks(128) { for chunk in cert.chunks(128) {
hasher.update(chunk); hasher.update(chunk);
} }
Self::Sha256(hasher.finalize().into()) CertFingerprint {
hash: hasher.finalize(),
} }
pub fn new_sha512(cert: &CertificateDer) -> Self {
let mut hasher = Sha512::new();
for chunk in cert.chunks(128) {
hasher.update(chunk);
}
Self::Sha512(hasher.finalize().into())
}
#[inline]
pub fn new_raw(cert: &CertificateDer) -> Self {
// TODO: looks like an absolutely inefficient solution
Self::Raw(Bytes::copy_from_slice(cert.as_ref()))
} }
} }
impl CertFingerprint { impl CertFingerprint<Sha256> {
pub fn try_from_sha256(value: &[u8]) -> Result<Self, TryFromSliceError> { /// Encode the TLS cert SHA-256 hash as HEX (base16).
Ok(Self::Sha256(value.try_into()?)) /// Resulting string is 64 bytes length.
}
pub fn try_from_sha512(value: &[u8]) -> Result<Self, TryFromSliceError> {
Ok(Self::Sha512(value.try_into()?))
}
#[inline]
pub fn from_raw(value: &[u8]) -> Self {
Self::Raw(Bytes::copy_from_slice(value))
}
pub fn try_from_sha256_b64(value: &str) -> Result<Self, base64ct::Error> {
let mut buf = [0u8; SHA256_LEN];
b64::decode(value, &mut buf)?;
Ok(Self::Sha256(buf))
}
pub fn try_from_sha512_b64(value: &str) -> Result<Self, base64ct::Error> {
let mut buf = [0u8; SHA512_LEN];
b64::decode(value, &mut buf)?;
Ok(Self::Sha512(buf))
}
}
impl CertFingerprint {
pub fn hex(&self) -> String { pub fn hex(&self) -> String {
match self {
Self::Sha256(hash) => {
let mut buf = [0u8; SHA256_HEX_LEN]; let mut buf = [0u8; SHA256_HEX_LEN];
b16::encode_str(hash.as_slice(), &mut buf) b16::encode_str(&self.hash, &mut buf).unwrap().to_owned()
// Note on `unwrap`:
// encoder returns an error only if an output buffer is too small,
// but we exactly know the required size (it's fixed for hashes).
// See also comments near `const SHA*_LEN` for formulas.
.unwrap()
.to_owned()
}
Self::Sha512(hash) => {
let mut buf = [0u8; SHA512_HEX_LEN];
b16::encode_str(hash.as_slice(), &mut buf)
.unwrap()
.to_owned()
}
Self::Raw(cert) => {
let mut buf: Vec<u8> = Vec::new();
b16::encode_str(cert, &mut buf).unwrap().to_owned()
} }
/// Encode the TLS cert SHA-256 hash as base64.
/// Resulting string is 44 bytes length.
pub fn base64(&self) -> String {
let mut buf = [0u8; SHA256_B64_LEN];
b64::encode(&self.hash, &mut buf).unwrap().to_owned()
} }
} }
impl CertFingerprint<Sha512> {
/// Encode the TLS cert SHA-512 hash as HEX (base16).
/// Resulting string is 128 bytes length.
pub fn hex(&self) -> String {
let mut buf = [0u8; SHA512_HEX_LEN];
b16::encode_str(&self.hash, &mut buf).unwrap().to_owned()
}
/// Encode the TLS cert SHA-512 hash as base64.
/// Resulting string is 88 bytes length.
pub fn base64(&self) -> String { pub fn base64(&self) -> String {
match self {
Self::Sha256(hash) => {
let mut buf = [0u8; SHA256_B64_LEN];
// Same note on `unwrap`, see above
b64::encode(hash.as_slice(), &mut buf).unwrap().to_owned()
}
Self::Sha512(hash) => {
let mut buf = [0u8; SHA512_B64_LEN]; let mut buf = [0u8; SHA512_B64_LEN];
b64::encode(hash.as_slice(), &mut buf).unwrap().to_owned() b64::encode(&self.hash, &mut buf).unwrap().to_owned()
}
Self::Raw(cert) => {
let mut buf: Vec<u8> = Vec::new();
b64::encode(cert, &mut buf).unwrap().to_owned()
}
}
} }
} }

View file

@ -28,6 +28,7 @@ pub trait SelfsignedCertVerifier: Send + Sync {
/// suggested for using in a [`SelfsignedCertVerifier`] cert storage /// suggested for using in a [`SelfsignedCertVerifier`] cert storage
/// (like `HashMap<String, SelfsignedCert>`, as a known_hosts parsing result) /// (like `HashMap<String, SelfsignedCert>`, as a known_hosts parsing result)
pub struct SelfsignedCert { pub struct SelfsignedCert {
pub fingerprint: fingerprint::CertFingerprint, pub algo: fingerprint::HashAlgo,
pub fingerprint: String,
pub expires: u64, pub expires: u64,
} }

View file

@ -1,114 +0,0 @@
use std::net::IpAddr;
use hickory_client::{
client::{AsyncClient, ClientHandle},
proto::iocompat::AsyncIoTokioAsStd,
rr::{
rdata::tlsa::{CertUsage, Matching, Selector},
DNSClass, IntoName, RData, RecordType,
},
tcp::TcpClientStream,
};
use tokio::net::ToSocketAddrs;
use crate::{certs::fingerprint::CertFingerprint, LibError};
pub struct DnsClient(AsyncClient);
impl DnsClient {
pub async fn init(server: impl ToSocketAddrs) -> Result<Self, LibError> {
for addr in tokio::net::lookup_host(server).await? {
let (stream, sender) =
TcpClientStream::<AsyncIoTokioAsStd<tokio::net::TcpStream>>::new(addr);
if let Ok((client, bg)) = AsyncClient::new(stream, sender, None).await {
tokio::spawn(bg);
return Ok(DnsClient(client));
} else {
continue;
}
}
Err(LibError::HostLookupError)
}
pub async fn query_ipv4(
&mut self,
name: &str,
) -> Result<impl Iterator<Item = IpAddr>, LibError> {
self.query_ip(name, RecordType::A).await
}
pub async fn query_ipv6(
&mut self,
name: &str,
) -> Result<impl Iterator<Item = IpAddr>, LibError> {
self.query_ip(name, RecordType::AAAA).await
}
#[inline]
async fn query_ip(
&mut self,
name: &str,
rtype: RecordType,
) -> Result<impl Iterator<Item = IpAddr>, LibError> {
let answers = self
.0
.query(name.into_name()?, DNSClass::IN, rtype)
.await?
.into_message()
.take_answers();
if !answers.is_empty() {
Ok(answers.into_iter().filter_map(|rec| match rec.data() {
Some(RData::A(addr)) => Some(IpAddr::V4(addr.0)),
Some(RData::AAAA(addr)) => Some(IpAddr::V6(addr.0)),
_ => None,
}))
} else {
Err(LibError::HostLookupError)
}
}
pub async fn query_tlsa(
&mut self,
domain: &str,
port: u16,
) -> Result<impl Iterator<Item = CertFingerprint>, LibError> {
let answers = self
.0
.query(
format!("_{}._tcp.{}", port, domain).into_name()?,
DNSClass::IN,
RecordType::TLSA,
)
.await?
.into_message()
.take_answers();
if !answers.is_empty() {
Ok(answers.into_iter().filter_map(|rec| {
if let Some(RData::TLSA(tlsa)) = rec.data() {
if tlsa.cert_usage() == CertUsage::DomainIssued
&& tlsa.selector() == Selector::Spki
{
match tlsa.matching() {
Matching::Sha256 => CertFingerprint::try_from_sha256(tlsa.cert_data())
.map(Some)
.unwrap_or(None),
Matching::Sha512 => CertFingerprint::try_from_sha512(tlsa.cert_data())
.map(Some)
.unwrap_or(None),
Matching::Raw => Some(CertFingerprint::from_raw(tlsa.cert_data())),
_ => None,
}
} else {
None
}
} else {
None
}
}))
} else {
Err(LibError::HostLookupError)
}
}
}

View file

@ -1,10 +1,5 @@
//! Library error structures and enums //! Library error structures and enums
#[cfg(feature = "hickory")]
use hickory_client::{
error::ClientError as HickoryClientError, proto::error::ProtoError as HickoryProtoError,
};
/// Main error structure, also a wrapper for everything else /// Main error structure, also a wrapper for everything else
#[derive(Debug)] #[derive(Debug)]
pub enum LibError { pub enum LibError {
@ -13,9 +8,6 @@ pub enum LibError {
IoError(std::io::Error), IoError(std::io::Error),
/// URL parse or check error /// URL parse or check error
InvalidUrlError(InvalidUrl), InvalidUrlError(InvalidUrl),
/// DNS provided no suitable records
/// (e.&nbsp;g. domain does not exist)
HostLookupError,
/// Response status code is out of [10; 69] range /// Response status code is out of [10; 69] range
StatusOutOfRange(u8), StatusOutOfRange(u8),
/// Response metadata or content cannot be parsed /// Response metadata or content cannot be parsed
@ -23,12 +15,6 @@ pub enum LibError {
DataNotUtf8(std::string::FromUtf8Error), DataNotUtf8(std::string::FromUtf8Error),
/// Provided string is not a valid MIME type /// Provided string is not a valid MIME type
InvalidMime(mime::FromStrError), InvalidMime(mime::FromStrError),
/// Hickory Client error
#[cfg(feature = "hickory")]
DnsClientError(HickoryClientError),
/// Hickory Proto error
#[cfg(feature = "hickory")]
DnsProtoError(HickoryProtoError),
} }
impl From<std::io::Error> for LibError { impl From<std::io::Error> for LibError {
@ -73,22 +59,6 @@ impl From<mime::FromStrError> for LibError {
} }
} }
#[cfg(feature = "hickory")]
impl From<HickoryClientError> for LibError {
#[inline]
fn from(err: HickoryClientError) -> Self {
Self::DnsClientError(err)
}
}
#[cfg(feature = "hickory")]
impl From<HickoryProtoError> for LibError {
#[inline]
fn from(err: HickoryProtoError) -> Self {
Self::DnsProtoError(err)
}
}
/// URL parse or check error /// URL parse or check error
#[derive(Debug)] #[derive(Debug)]
pub enum InvalidUrl { pub enum InvalidUrl {

View file

@ -6,9 +6,6 @@ pub mod client;
pub mod error; pub mod error;
pub mod status; pub mod status;
#[cfg(feature = "hickory")]
pub mod dns;
pub use client::Client; pub use client::Client;
pub use error::*; pub use error::*;
pub use status::*; pub use status::*;