diff --git a/src/Cargo.lock b/src/Cargo.lock index 091998ffa..b9f7edb8b 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -106,7 +106,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -503,6 +503,36 @@ dependencies = [ "libc", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crypto-bigint" version = "0.4.9" @@ -856,9 +886,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -887,9 +917,9 @@ checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -943,6 +973,20 @@ dependencies = [ "slab", ] +[[package]] +name = "generator" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1103,11 +1147,12 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "hickory-proto" -version = "0.24.4" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248" +checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" dependencies = [ "async-trait", + "bitflags 2.6.0", "cfg-if", "data-encoding", "enum-as-inner", @@ -1117,8 +1162,11 @@ dependencies = [ "idna", "ipnet", "once_cell", - "rand 0.8.5", - "thiserror", + "rand 0.9.1", + "ring", + "rustls-pki-types", + "thiserror 2.0.12", + "time", "tinyvec", "tokio", "tracing", @@ -1127,19 +1175,19 @@ dependencies = [ [[package]] name = "hickory-resolver" -version = "0.24.4" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbb117a1ca520e111743ab2f6688eddee69db4e0ea242545a604dce8a66fd22e" +checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a" dependencies = [ "cfg-if", "futures-util", "hickory-proto", - "lru-cache", + "moka", "once_cell", "parking_lot", - "rand 0.8.5", + "rand 0.9.1", "smallvec", - "thiserror", + "thiserror 2.0.12", "tokio", "tracing", ] @@ -1239,7 +1287,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -1536,12 +1584,6 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -1571,12 +1613,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] -name = "lru-cache" -version = "0.1.2" +name = "loom" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" dependencies = [ - "linked-hash-map", + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + +[[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]] @@ -1632,6 +1687,25 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "moka" +version = "0.12.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" +dependencies = [ + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "loom", + "parking_lot", + "portable-atomic", + "rustc_version", + "smallvec", + "tagptr", + "thiserror 1.0.69", + "uuid", +] + [[package]] name = "multiexp" version = "0.4.0" @@ -1668,6 +1742,16 @@ dependencies = [ "minimal-lexical", ] +[[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 = "num" version = "0.4.3" @@ -1801,6 +1885,10 @@ name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +dependencies = [ + "critical-section", + "portable-atomic", +] [[package]] name = "opaque-debug" @@ -1808,6 +1896,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "p256" version = "0.11.1" @@ -2002,6 +2096,12 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + [[package]] name = "powerfmt" version = "0.2.0" @@ -2129,7 +2229,7 @@ dependencies = [ "rand 0.8.5", "rand_core 0.6.4", "sha2", - "thiserror", + "thiserror 1.0.69", "vsss-rs", ] @@ -2167,6 +2267,7 @@ dependencies = [ "rustls", "serde", "serde_json", + "tokio", "webpki-roots", ] @@ -2307,8 +2408,17 @@ checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[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]] @@ -2319,9 +2429,15 @@ checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.4", ] +[[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.4" @@ -2407,6 +2523,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rusticata-macros" version = "4.1.0" @@ -2477,6 +2602,12 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -2520,6 +2651,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + [[package]] name = "serde" version = "1.0.219" @@ -2664,6 +2801,15 @@ dependencies = [ "keccak", ] +[[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" @@ -2823,6 +2969,12 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + [[package]] name = "tap" version = "1.0.1" @@ -2835,7 +2987,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -2849,6 +3010,26 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + [[package]] name = "time" version = "0.3.36" @@ -2984,28 +3165,46 @@ 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" +name = "tracing-core" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", + "once_cell", + "valuable", ] [[package]] -name = "tracing-core" -version = "0.1.32" +name = "tracing-log" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ + "matchers", + "nu-ansi-term", "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -3094,6 +3293,12 @@ dependencies = [ "getrandom 0.3.3", ] +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "version_check" version = "0.9.5" @@ -3229,6 +3434,50 @@ dependencies = [ "rustix", ] +[[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" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -3238,6 +3487,86 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -3287,6 +3616,15 @@ dependencies = [ "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -3452,7 +3790,7 @@ dependencies = [ "nom", "oid-registry", "rusticata-macros", - "thiserror", + "thiserror 1.0.69", "time", ] diff --git a/src/qos_net/Cargo.toml b/src/qos_net/Cargo.toml index f766754b9..ba49e5c02 100644 --- a/src/qos_net/Cargo.toml +++ b/src/qos_net/Cargo.toml @@ -12,12 +12,14 @@ borsh = { version = "1.0", features = [ "derive", ], default-features = false } serde = { version = "1", features = ["derive"], default-features = false } -hickory-resolver = { version = "0.24.1", features = [ - "tokio-runtime", +hickory-resolver = { version = "0.25.2", features = [ + "tokio", # for async + "dnssec-ring", # for DNSSEC ], default-features = false, optional = true } rand = { version = "0.9.1", features = [ "thread_rng", ], default-features = false, optional = true } +tokio = { version = "1.38.0", default-features = false } [dev-dependencies] qos_test_primitives = { path = "../qos_test_primitives" } diff --git a/src/qos_net/src/error.rs b/src/qos_net/src/error.rs index e9259f8ca..d91d6b88d 100644 --- a/src/qos_net/src/error.rs +++ b/src/qos_net/src/error.rs @@ -3,7 +3,7 @@ use std::net::AddrParseError; use borsh::{BorshDeserialize, BorshSerialize}; #[cfg(feature = "proxy")] -use hickory_resolver::error::ResolveError; +use hickory_resolver::ResolveError; /// Errors related to creating and using proxy connections #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] diff --git a/src/qos_net/src/proxy.rs b/src/qos_net/src/proxy.rs index 1b494eb78..9dcf2fce8 100644 --- a/src/qos_net/src/proxy.rs +++ b/src/qos_net/src/proxy.rs @@ -13,6 +13,7 @@ use crate::{ proxy_msg::ProxyMsg, }; use rand::Rng; +use tokio::runtime::Runtime; const MEGABYTE: usize = 1024 * 1024; const MAX_ENCODED_MSG_LEN: usize = 128 * MEGABYTE; @@ -23,6 +24,7 @@ pub const DEFAULT_MAX_CONNECTION_SIZE: usize = 512; pub struct Proxy { connections: HashMap, max_connections: usize, + tokio_runtime_context: Runtime, } impl Default for Proxy { @@ -33,17 +35,28 @@ impl Default for Proxy { impl Proxy { /// Create a new `Self`. + /// # Panics + /// Panics if Tokio setup fails #[must_use] pub fn new() -> Self { Self { connections: HashMap::new(), max_connections: DEFAULT_MAX_CONNECTION_SIZE, + tokio_runtime_context: Runtime::new() + .expect("Failed to create tokio runtime"), } } #[must_use] + /// # Panics + /// Panics if Tokio setup fails pub fn new_with_max_connections(max_connections: usize) -> Self { - Self { connections: HashMap::new(), max_connections } + Self { + connections: HashMap::new(), + max_connections, + tokio_runtime_context: Runtime::new() + .expect("Failed to create tokio runtime"), + } } /// Save the connection in the proxy and assigns a connection ID @@ -129,6 +142,7 @@ impl Proxy { port, dns_resolvers.clone(), dns_port, + &self.tokio_runtime_context, ) { Ok(conn) => { let remote_ip = conn.ip.clone(); @@ -439,4 +453,111 @@ mod test { ), } } + + /// Check how the upstream resolver deals with a known-bad DNSSEC protected domain + /// It does NOT actively test the security behavior of our local DNSSEC verification + #[test] + fn test_lookup_domain_bad_dnssec_record() { + let mut proxy = Proxy::new(); + assert_eq!(proxy.num_connections(), 0); + + let connect = proxy.connect_by_name( + "sigfail.ippacket.stream".to_string(), + 443, + vec!["8.8.8.8".to_string()], + 53, + ); + + assert_eq!(proxy.num_connections(), 0); + match connect { + ProxyMsg::ProxyError(qos_error) => { + // the upstream resolver lets us know with SERVFAIL that a DNSSEC check failed + assert_eq!( + qos_error, + QosNetError::DNSResolutionError("ResolveError { kind: Proto(ProtoError { kind: Message(\"\ + could not validate negative response missing SOA\") }) }".to_string()) + ); + } + _ => { + panic!("test failure: the resolution should fail: {connect:?}") + } + } + + // test domain seen in https://bind9.readthedocs.io/en/v9.18.14/dnssec-guide.html + let connect = proxy.connect_by_name( + "www.dnssec-failed.org".to_string(), + 443, + vec!["8.8.8.8".to_string()], + 53, + ); + + assert_eq!(proxy.num_connections(), 0); + match connect { + ProxyMsg::ProxyError(qos_error) => { + // the upstream resolver lets us know with SERVFAIL that a DNSSEC check failed + assert_eq!( + qos_error, + QosNetError::DNSResolutionError("ResolveError { kind: Proto(ProtoError { kind: Message(\"\ + could not validate negative response missing SOA\") }) }".to_string()) + ); + } + _ => { + panic!("test failure: the resolution should fail: {connect:?}") + } + } + } + + #[test] + /// Check how the upstream resolver deals with a known-good DNSSEC protected domain + /// It does NOT actively test the security behavior of our local DNSSEC verification + fn test_lookup_domain_good_dnssec_record() { + let mut proxy = Proxy::new(); + assert_eq!(proxy.num_connections(), 0); + + let connect = proxy.connect_by_name( + "sigok.ippacket.stream".to_string(), + 443, + vec!["8.8.8.8".to_string()], + 53, + ); + + assert_eq!(proxy.num_connections(), 1); + match connect { + ProxyMsg::ConnectResponse { connection_id: _, remote_ip } => { + assert_eq!(remote_ip, "195.201.14.36"); + } + _ => { + panic!( + "test failure: the resolution should succeed: {connect:?}" + ) + } + } + } + + /// Test that resolving a domain without DNSSEC records still works + #[test] + fn test_lookup_domain_no_dnssec_successful() { + let mut proxy = Proxy::new(); + assert_eq!(proxy.num_connections(), 0); + + // as of 6/2025, google.com doesn't have DNSSEC records + let connect = proxy.connect_by_name( + "google.com".to_string(), + 443, + vec!["8.8.8.8".to_string()], + 53, + ); + + assert_eq!(proxy.num_connections(), 1); + match connect { + ProxyMsg::ConnectResponse { connection_id: _, remote_ip: _ } => { + // any normal response is OK, we don't expect a specific IP + } + _ => { + panic!( + "test failure: the resolution should succeed: {connect:?}" + ) + } + } + } } diff --git a/src/qos_net/src/proxy_connection.rs b/src/qos_net/src/proxy_connection.rs index 7876e3d70..86dc8c1db 100644 --- a/src/qos_net/src/proxy_connection.rs +++ b/src/qos_net/src/proxy_connection.rs @@ -1,16 +1,16 @@ //! Contains logic for remote connection establishment: DNS resolution and TCP //! connection. -use std::{ - io::{Read, Write}, - net::{AddrParseError, IpAddr, SocketAddr, TcpStream}, -}; - +use crate::error::QosNetError; +use hickory_resolver::name_server::TokioConnectionProvider; use hickory_resolver::{ config::{NameServerConfigGroup, ResolverConfig, ResolverOpts}, Resolver, }; - -use crate::error::QosNetError; +use std::{ + io::{Read, Write}, + net::{AddrParseError, IpAddr, SocketAddr, TcpStream}, +}; +use tokio::runtime::Runtime; /// Struct representing a TCP connection held on our proxy pub struct ProxyConnection { @@ -28,8 +28,14 @@ impl ProxyConnection { port: u16, dns_resolvers: Vec, dns_port: u16, + tokio_runtime_context: &Runtime, ) -> Result { - let ip = resolve_hostname(hostname, dns_resolvers, dns_port)?; + let ip = resolve_hostname( + hostname, + dns_resolvers, + dns_port, + tokio_runtime_context, + )?; let tcp_addr = SocketAddr::new(ip, port); let tcp_stream = TcpStream::connect(tcp_addr)?; @@ -81,6 +87,7 @@ fn resolve_hostname( hostname: String, resolver_addrs: Vec, port: u16, + tokio_runtime_context: &Runtime, ) -> Result { let resolver_parsed_addrs = resolver_addrs .iter() @@ -100,9 +107,32 @@ fn resolve_hostname( true, ), ); - let resolver = Resolver::new(resolver_config, ResolverOpts::default())?; - let response = - resolver.lookup_ip(hostname.clone()).map_err(QosNetError::from)?; + + let mut resolver_builder = Resolver::builder_with_config( + resolver_config, + TokioConnectionProvider::default(), + ); + let mut resolver_options = ResolverOpts::default(); + // this validates DNSSEC in responses if DNSSEC is present + // it still allows responses without DNSSEC to succeed, limiting its effectiveness + // against on-path MITM attackers, but is preferrable to not checking response validity + resolver_options.validate = true; + + // enable case randomization for improved security + // see https://developers.google.com/speed/public-dns/docs/security#randomize_case + resolver_options.case_randomization = true; + + // set our improved resolver options + *resolver_builder.options_mut() = resolver_options; + + let resolver = resolver_builder.build(); + + // needed for borrowing in async block + let cloned_hostname = hostname.clone(); + let response = tokio_runtime_context + .block_on(async move { resolver.lookup_ip(cloned_hostname).await }) + .map_err(QosNetError::from)?; + response.iter().next().ok_or_else(|| { QosNetError::DNSResolutionError(format!( "Empty response when querying for host {hostname}" @@ -127,14 +157,20 @@ mod test { let host = "api.turnkey.com"; let path = "/health"; + // manually set up a Tokio runtime + let runtime = Runtime::new().expect("Failed to create tokio runtime"); + let mut remote_connection = ProxyConnection::new_from_name( host.to_string(), 443, vec!["8.8.8.8".to_string()], 53, + &runtime, ) .unwrap(); + drop(runtime); + let root_store = RootCertStore { roots: webpki_roots::TLS_SERVER_ROOTS.into() };