From 104a072fd65a3d733d53d8635ccac7e527c66ecf Mon Sep 17 00:00:00 2001 From: Slatian Date: Fri, 4 Aug 2023 00:00:21 +0200 Subject: [PATCH] Configurable multiple dns resolvers --- Cargo.lock | 277 ++++++++++++++++++++++++++++--- Cargo.toml | 2 +- echoip_test.toml | 5 + src/config/dns.rs | 99 +++++++++++ src/{config.rs => config/mod.rs} | 35 ++-- src/main.rs | 117 +++++++------ src/templating_engine.rs | 6 +- 7 files changed, 441 insertions(+), 100 deletions(-) create mode 100644 src/config/dns.rs rename src/{config.rs => config/mod.rs} (73%) diff --git a/Cargo.lock b/Cargo.lock index c655941..43c28fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,7 +77,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -87,7 +87,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -245,6 +245,12 @@ version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "bytes" version = "1.4.0" @@ -344,6 +350,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -385,7 +401,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6943ae99c34386c84a470c499d3414f66502a41340aa895406e0d2e4a207b91d" dependencies = [ "cfg-if", - "hashbrown", + "hashbrown 0.14.0", "lock_api", "once_cell", "parking_lot_core", @@ -462,7 +478,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -595,6 +611,15 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -664,6 +689,31 @@ dependencies = [ "smallvec", ] +[[package]] +name = "h2" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 1.9.3", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.14.0" @@ -861,6 +911,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + [[package]] name = "indexmap" version = "2.0.0" @@ -868,7 +928,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.0", ] [[package]] @@ -879,7 +939,7 @@ checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ "socket2 0.5.3", "widestring", - "windows-sys", + "windows-sys 0.48.0", "winreg", ] @@ -906,7 +966,7 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1057,7 +1117,7 @@ checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1112,6 +1172,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + [[package]] name = "parking_lot" version = "0.12.1" @@ -1302,6 +1368,59 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quinn" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b435e71d9bfa0d8889927231970c51fb89c58fa63bffcab117c9c7a41e5ef8f" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "fxhash", + "quinn-proto", + "quinn-udp", + "rustls", + "thiserror", + "tokio", + "tracing", + "webpki", +] + +[[package]] +name = "quinn-proto" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fce546b9688f767a57530652488420d419a8b1f44a478b451c3d1ab6d992a55" +dependencies = [ + "bytes", + "fxhash", + "rand", + "ring", + "rustls", + "rustls-native-certs", + "rustls-pemfile 0.2.1", + "slab", + "thiserror", + "tinyvec", + "tracing", + "webpki", +] + +[[package]] +name = "quinn-udp" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07946277141531aea269befd949ed16b2c85a780ba1043244eda0969e538e54" +dependencies = [ + "futures-util", + "libc", + "quinn-proto", + "socket2 0.4.9", + "tokio", + "tracing", +] + [[package]] name = "quote" version = "1.0.31" @@ -1429,7 +1548,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1444,6 +1563,27 @@ dependencies = [ "webpki", ] +[[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 1.0.2", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +dependencies = [ + "base64 0.13.1", +] + [[package]] name = "rustls-pemfile" version = "1.0.2" @@ -1474,6 +1614,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1490,6 +1639,29 @@ dependencies = [ "untrusted", ] +[[package]] +name = "security-framework" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.171" @@ -1630,7 +1802,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1756,7 +1928,7 @@ dependencies = [ "signal-hook-registry", "socket2 0.4.9", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1792,6 +1964,7 @@ dependencies = [ "futures-sink", "pin-project-lite", "tokio", + "tracing", ] [[package]] @@ -1821,7 +1994,7 @@ version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ - "indexmap", + "indexmap 2.0.0", "serde", "serde_spanned", "toml_datetime", @@ -1921,18 +2094,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f7f83d1e4a0e4358ac54c5c3681e5d7da5efc5a7a632c90bb6d6669ddd9bc26" dependencies = [ "async-trait", + "bytes", "cfg-if", "data-encoding", "enum-as-inner", "futures-channel", "futures-io", "futures-util", + "h2", + "http", "idna 0.2.3", "ipnet", "lazy_static", + "quinn", "rand", "rustls", - "rustls-pemfile", + "rustls-pemfile 1.0.2", "smallvec", "thiserror", "tinyvec", @@ -1941,6 +2118,7 @@ dependencies = [ "tracing", "url", "webpki", + "webpki-roots", ] [[package]] @@ -2262,6 +2440,21 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -2277,51 +2470,93 @@ version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.0" @@ -2344,5 +2579,5 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys", + "windows-sys 0.48.0", ] diff --git a/Cargo.toml b/Cargo.toml index 36e9e6d..570b1cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,5 +22,5 @@ toml = "0.7" tower = "0.4" tower-http = { version = "0.4", features = ["fs"] } trust-dns-proto = "0.22" -trust-dns-resolver = { version = "0.22", features = ["dns-over-rustls"] } +trust-dns-resolver = { version = "0.22", features = ["dns-over-rustls","dns-over-https","dns-over-quic"] } maxminddb = "0.23" diff --git a/echoip_test.toml b/echoip_test.toml index 825543e..2a6b28a 100644 --- a/echoip_test.toml +++ b/echoip_test.toml @@ -61,3 +61,8 @@ burst = 15 #Note: The ratelimit is implemented using the governor crate +[dns.resolver.digitalcourage] +display_name = "Digitalcourage 3" +servers = ["5.9.164.112:853","[2a01:4f8:251:554::2]:853"] +protocol = "Tls" +tls_dns_name = "dns3.digitalcourage.de" diff --git a/src/config/dns.rs b/src/config/dns.rs new file mode 100644 index 0000000..cf635aa --- /dev/null +++ b/src/config/dns.rs @@ -0,0 +1,99 @@ +use serde::{Deserialize,Serialize}; +use trust_dns_resolver::config::Protocol; +use trust_dns_resolver::Name; + +use std::collections::HashMap; +use std::net::SocketAddr; + +#[derive(Deserialize, Clone)] +pub struct DnsConfig { + pub allow_forward_lookup: bool, + pub allow_reverse_lookup: bool, + pub hidden_suffixes: Vec, + #[serde(default="default_dns_resolver_name")] + pub default_resolver: String, + pub resolver: HashMap, +} + +#[derive(Deserialize, Serialize, Clone)] +pub enum DnsProtocol { + Udp, + Tcp, + Tls, + Https, + Quic, +} + +pub fn default_dns_resolver_name() -> String { + "default".to_string() +} + +#[derive(Deserialize, Serialize, Clone)] +pub struct DnsResolverConfig { + pub display_name: String, + #[serde(default="zero")] + pub weight: i32, + pub servers: Vec, + #[serde(default)] + pub search: Vec, + pub protocol: DnsProtocol, + pub tls_dns_name: Option, + pub bind_address: Option, + #[serde(default="default_true")] + pub trust_nx_responses: bool, +} + +fn zero() -> i32 { + return 0; +} + +fn default_true() -> bool { + return true; +} + +impl Default for DnsConfig { + fn default() -> Self { + DnsConfig { + allow_forward_lookup: true, + allow_reverse_lookup: false, + hidden_suffixes: Vec::new(), + default_resolver: "default".to_string(), + resolver: Default::default(), + } + } +} + +impl Into for DnsProtocol { + fn into(self) -> Protocol { + match self { + Self::Udp => Protocol::Udp, + Self::Tcp => Protocol::Tcp, + Self::Tls => Protocol::Tls, + Self::Https => Protocol::Https, + Self::Quic => Protocol::Quic, + } + } +} + +impl DnsResolverConfig { + pub fn to_trust_resolver_config(&self) -> trust_dns_resolver::config::ResolverConfig { + let mut resolver = trust_dns_resolver::config::ResolverConfig::new(); + for server in &self.servers { + resolver.add_name_server(trust_dns_resolver::config::NameServerConfig{ + socket_addr: *server, + protocol: self.protocol.clone().into(), + tls_dns_name: self.tls_dns_name.clone(), + trust_nx_responses: self.trust_nx_responses, + tls_config: None, + bind_addr: self.bind_address, + }); + } + for search in &self.search { + if let Ok(name) = Name::from_str_relaxed(search) { + resolver.add_search(name); + } + } + return resolver; + } +} + diff --git a/src/config.rs b/src/config/mod.rs similarity index 73% rename from src/config.rs rename to src/config/mod.rs index 0d55893..0608661 100644 --- a/src/config.rs +++ b/src/config/mod.rs @@ -1,8 +1,14 @@ use axum_client_ip::SecureClientIpSource; +use serde::Deserialize; + use std::net::SocketAddr; use std::num::NonZeroU32; -#[derive(serde::Deserialize, Default, Clone)] +mod dns; + +pub use crate::config::dns::{DnsConfig, DnsProtocol, DnsResolverConfig}; + +#[derive(Deserialize, Default, Clone)] pub struct EchoIpServiceConfig { pub server: ServerConfig, pub dns: DnsConfig, @@ -11,7 +17,7 @@ pub struct EchoIpServiceConfig { pub ratelimit: RatelimitConfig, } -#[derive(serde::Deserialize, Clone)] +#[derive(Deserialize, Clone)] pub struct ServerConfig { pub listen_on: SocketAddr, pub ip_header: SecureClientIpSource, @@ -20,33 +26,27 @@ pub struct ServerConfig { pub static_location: Option, } -#[derive(serde::Deserialize, Clone)] -pub struct DnsConfig { - pub allow_forward_lookup: bool, - pub allow_reverse_lookup: bool, - pub hidden_suffixes: Vec, - //Future Idea: allow custom resolver -} -#[derive(serde::Deserialize, Clone)] +#[derive(Deserialize, Clone)] pub struct GeoIpConfig { pub asn_database: Option, pub location_database: Option, } -#[derive(serde::Deserialize, Clone)] +#[derive(Deserialize, Clone)] pub struct TemplateConfig { pub template_location: String, pub extra_config: Option, pub text_user_agents: Vec, } -#[derive(serde::Deserialize, Clone)] +#[derive(Deserialize, Clone)] pub struct RatelimitConfig { pub per_minute: NonZeroU32, pub burst: NonZeroU32, } + impl Default for ServerConfig { fn default() -> Self { ServerConfig { @@ -58,16 +58,6 @@ impl Default for ServerConfig { } } -impl Default for DnsConfig { - fn default() -> Self { - DnsConfig { - allow_forward_lookup: true, - allow_reverse_lookup: false, - hidden_suffixes: Vec::new(), - } - } -} - impl Default for GeoIpConfig { fn default() -> Self { GeoIpConfig { @@ -95,3 +85,4 @@ impl Default for RatelimitConfig { } } } + diff --git a/src/main.rs b/src/main.rs index 2ba8a40..dd94210 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,14 +18,11 @@ use axum_client_ip::SecureClientIp; use clap::Parser; use lazy_static::lazy_static; use regex::Regex; +use serde::{Deserialize,Serialize}; use tera::Tera; use tower::ServiceBuilder; use tower_http::services::ServeDir; -use trust_dns_resolver::{ - TokioAsyncResolver, -// config::ResolverOpts, -// config::ResolverConfig, -}; +use trust_dns_resolver::TokioAsyncResolver; use tokio::signal::unix::{ signal, @@ -62,28 +59,26 @@ use crate::templating_engine::{ use crate::ipinfo::{AddressCast,AddressInfo,AddressScope}; -#[derive(serde::Deserialize, serde::Serialize, Clone)] +#[derive(Deserialize, Serialize, Clone)] pub struct SettingsQuery { format: Option, lang: Option, + dns: Option, } -#[derive(serde::Deserialize, serde::Serialize, Clone)] +#[derive(Clone, Serialize)] +pub struct QuerySettings { + #[serde(skip)] + template: TemplateSettings, + dns_resolver_id: String, +} + +#[derive(Deserialize, Serialize, Clone)] pub struct SearchQuery { query: Option, } -pub fn default_dns_name() -> String { - "default".to_string() -} - -#[derive(serde::Deserialize, serde::Serialize, Clone)] -pub struct ResolverQuery { - #[serde(default="default_dns_name")] - dns: String, -} - -#[derive(serde::Deserialize, serde::Serialize, Clone)] +#[derive(Serialize, Clone)] pub struct IpResult { address: IpAddr, hostname: Option, @@ -95,7 +90,7 @@ pub struct IpResult { // We need this one to hide the partial lookup field when irelevant pub fn not(b: &bool) -> bool { !b } -#[derive(serde::Deserialize, serde::Serialize, Default, Clone)] +#[derive(Serialize, Default, Clone)] pub struct DigResult { records: simple_dns::DnsLookupResult, #[serde(skip_serializing_if = "IdnaName::was_ascii")] @@ -239,7 +234,7 @@ async fn main() { // Initalize DNS resolver with os defaults println!("Initalizing dns resolver ..."); - println!("Using System configuration ..."); + println!("Initalizing System resolver ..."); let res = TokioAsyncResolver::tokio_from_system_conf(); //let res = TokioAsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default()); let dns_resolver = match res { @@ -269,6 +264,15 @@ async fn main() { let mut dns_resolver_map: HashMap = HashMap::new(); + for (key, resolver_config) in &config.dns.resolver { + println!("Initalizing {} resolver ...", key); + let resolver = TokioAsyncResolver::tokio( + resolver_config.to_trust_resolver_config(), + Default::default() + ).unwrap(); + dns_resolver_map.insert(key.clone(), resolver); + } + dns_resolver_map.insert("default".to_string(), dns_resolver); dns_resolver_map.insert("quad9".to_string(), quad9_resolver); dns_resolver_map.insert("google".to_string(), google_resolver); @@ -327,7 +331,7 @@ async fn main() { config.ratelimit.per_minute, config.ratelimit.burst)) .layer(middleware::from_fn(ratelimit::rate_limit_middleware)) .layer(Extension(config)) - .layer(middleware::from_fn(format_and_language_middleware)) + .layer(middleware::from_fn(settings_query_middleware)) ) ; @@ -340,7 +344,7 @@ async fn main() { } -async fn format_and_language_middleware( +async fn settings_query_middleware( Query(query): Query, Extension(config): Extension, user_agent_header: Option>, @@ -361,9 +365,12 @@ async fn format_and_language_middleware( } } // Add the request settings extension - req.extensions_mut().insert(TemplateSettings{ - format: format.unwrap_or(ResponseFormat::TextHtml), - lang: query.lang.unwrap_or("en".to_string()), + req.extensions_mut().insert(QuerySettings{ + template: TemplateSettings{ + format: format.unwrap_or(ResponseFormat::TextHtml), + lang: query.lang.unwrap_or("en".to_string()), + }, + dns_resolver_id: query.dns.unwrap_or(config.dns.default_resolver), }); next.run(req).await } @@ -404,9 +411,8 @@ async fn user_agent_handler( async fn handle_default_route( Query(search_query): Query, - Query(resolver_settings): Query, State(arc_state): State>, - Extension(settings): Extension, + Extension(settings): Extension, user_agent_header: Option>, SecureClientIp(address): SecureClientIp ) -> Response { @@ -419,13 +425,12 @@ async fn handle_default_route( search_query, false, settings, - resolver_settings, state ).await; } } - let result = get_ip_result(&address, &settings.lang, &"default".to_string(), &state).await; + let result = get_ip_result(&address, &settings.template.lang, &"default".to_string(), &state).await; let user_agent: Option = match user_agent_header { Some(TypedHeader(user_agent)) => Some(user_agent.to_string()), @@ -433,7 +438,7 @@ async fn handle_default_route( }; state.templating_engine.render_view( - &settings, + &settings.template, &View::Index{ result: result, user_agent: user_agent, @@ -445,15 +450,16 @@ async fn handle_default_route( async fn handle_search_request( search_query: String, this_should_have_been_an_ip: bool, - settings: TemplateSettings, - resolver_settings: ResolverQuery, + settings: QuerySettings, arc_state: Arc, ) -> Response { - let search_query = search_query.trim(); + let mut search_query = search_query.trim().to_string(); + let mut settings = settings; lazy_static!{ static ref ASN_REGEX: Regex = Regex::new(r"^[Aa][Ss][Nn]?\s*(\d{1,7})$").unwrap(); + static ref VIA_REGEX: Regex = Regex::new(r"[Vv][Ii][Aa]\s+(\S+)").unwrap(); } //If someone asked for an asn, give an asn answer @@ -462,22 +468,31 @@ async fn handle_search_request( // Render a dummy template that can at least link to other pages let state = Arc::clone(&arc_state); return state.templating_engine.render_view( - &settings, + &settings.template, &View::Asn{asn: asn}, ).await } } + if let Some(via_cap) = VIA_REGEX.captures(&search_query) { + if let Some(via) = via_cap.get(1).map(|c| c.as_str().to_string()) { + let state = Arc::clone(&arc_state); + if state.dns_resolvers.contains_key(&via) { + settings.dns_resolver_id = via; + } + } + search_query = VIA_REGEX.replace(&search_query,"").trim().to_string(); + } + // Try to interpret as an IP-Address if let Ok(address) = search_query.parse() { - return handle_ip_request(address, settings, resolver_settings, arc_state).await; + return handle_ip_request(address, settings, arc_state).await; } // Fall back to treating it as a hostname return handle_dig_request( - search_query.to_string(), + search_query, settings, - resolver_settings, arc_state, !this_should_have_been_an_ip, ).await @@ -485,34 +500,32 @@ async fn handle_search_request( } async fn handle_ip_route_with_path( - Extension(settings): Extension, + Extension(settings): Extension, State(arc_state): State>, - Query(resolver_settings): Query, extract::Path(query): extract::Path, ) -> Response { if let Ok(address) = query.parse() { - return handle_ip_request(address, settings, resolver_settings, arc_state).await + return handle_ip_request(address, settings, arc_state).await } else { - return handle_search_request(query, true, settings, resolver_settings, arc_state).await; + return handle_search_request(query, true, settings, arc_state).await; } } async fn handle_ip_request( address: IpAddr, - settings: TemplateSettings, - resolver_settings: ResolverQuery, + settings: QuerySettings, arc_state: Arc, ) -> Response { let state = Arc::clone(&arc_state); let result = get_ip_result( &address, - &settings.lang, - &resolver_settings.dns, + &settings.template.lang, + &settings.dns_resolver_id, &state).await; state.templating_engine.render_view( - &settings, + &settings.template, &View::Ip{result: result} ).await } @@ -580,18 +593,16 @@ async fn get_ip_result( } async fn handle_dig_route_with_path( - Query(resolver_settings): Query, - Extension(settings): Extension, + Extension(settings): Extension, State(arc_state): State>, extract::Path(name): extract::Path, ) -> Response { - return handle_dig_request(name, settings, resolver_settings, arc_state, true).await + return handle_dig_request(name, settings, arc_state, true).await } async fn handle_dig_request( dig_query: String, - settings: TemplateSettings, - resolver_settings: ResolverQuery, + settings: QuerySettings, arc_state: Arc, do_full_lookup: bool, ) -> Response { @@ -600,13 +611,13 @@ async fn handle_dig_request( let dig_result = get_dig_result( &dig_query, - &resolver_settings.dns, + &settings.dns_resolver_id, &state, do_full_lookup ).await; state.templating_engine.render_view( - &settings, + &settings.template, &View::Dig{ query: dig_query, result: dig_result} ).await diff --git a/src/templating_engine.rs b/src/templating_engine.rs index 6f7239b..c4c3d88 100644 --- a/src/templating_engine.rs +++ b/src/templating_engine.rs @@ -58,7 +58,7 @@ pub struct TemplateSettings { /* The echoip view */ -#[derive(serde::Deserialize, serde::Serialize, Clone)] +#[derive(serde::Serialize, Clone)] #[serde(untagged)] pub enum View { Asn { asn: u32 }, @@ -116,10 +116,10 @@ impl Engine { _ => text.into_response(), } Err(e) => { - println!("There was an error while rendering template {template_name}: {e:?}"); + println!("There was an error while rendering template {}: {e:?}", view.template_name()); ( StatusCode::INTERNAL_SERVER_ERROR, - format!("Template error in {template_name}, contact owner or see logs.\n") + format!("Template error in {}, contact owner or see logs.\n", view.template_name()) ).into_response() } }