//! Perform DNS lookups use parking_lot::RwLock; use std::{collections::HashMap, future::Future}; use tracing::trace; pub use trust_dns_resolver::{error::ResolveError, TokioAsyncResolver}; use trust_dns_resolver::{name_server::ConnectionProvider, AsyncResolver}; /// A type that can lookup DNS entries pub trait Resolver: Send + Sync + Unpin + 'static { /// Performs a textual lookup and returns the first text fn lookup_txt(&self, query: &str) -> impl Future> + Send; } impl Resolver for AsyncResolver

{ async fn lookup_txt(&self, query: &str) -> Option { // See: [AsyncResolver::txt_lookup] // > *hint* queries that end with a '.' are fully qualified names and are cheaper lookups let fqn = if query.ends_with('.') { query.to_string() } else { format!("{query}.") }; match self.txt_lookup(fqn).await { Err(err) => { trace!(target: "disc::dns", %err, ?query, "dns lookup failed"); None } Ok(lookup) => { let txt = lookup.into_iter().next()?; let entry = txt.iter().next()?; String::from_utf8(entry.to_vec()).ok() } } } } /// An asynchronous DNS resolver /// /// See also [`TokioAsyncResolver`] /// /// ``` /// # fn t() { /// use reth_dns_discovery::resolver::DnsResolver; /// let resolver = DnsResolver::from_system_conf().unwrap(); /// # } /// ``` /// /// Note: This [Resolver] can send multiple lookup attempts, See also /// [`ResolverOpts`](trust_dns_resolver::config::ResolverOpts) which configures 2 attempts (1 retry) /// by default. #[derive(Clone, Debug)] pub struct DnsResolver(TokioAsyncResolver); // === impl DnsResolver === impl DnsResolver { /// Create a new resolver by wrapping the given [`AsyncResolver`] pub const fn new(resolver: TokioAsyncResolver) -> Self { Self(resolver) } /// Constructs a new Tokio based Resolver with the system configuration. /// /// This will use `/etc/resolv.conf` on Unix OSes and the registry on Windows. pub fn from_system_conf() -> Result { TokioAsyncResolver::tokio_from_system_conf().map(Self::new) } } impl Resolver for DnsResolver { async fn lookup_txt(&self, query: &str) -> Option { Resolver::lookup_txt(&self.0, query).await } } /// A [Resolver] that uses an in memory map to lookup entries #[derive(Debug, Default)] pub struct MapResolver(RwLock>); // === impl MapResolver === impl MapResolver { /// Inserts a key-value pair into the map. pub fn insert(&self, k: String, v: String) -> Option { self.0.write().insert(k, v) } /// Returns the value corresponding to the key pub fn get(&self, k: &str) -> Option { self.0.read().get(k).cloned() } /// Removes a key from the map, returning the value at the key if the key was previously in the /// map. pub fn remove(&self, k: &str) -> Option { self.0.write().remove(k) } } impl Resolver for MapResolver { async fn lookup_txt(&self, query: &str) -> Option { self.get(query) } } /// A Resolver that always times out. #[cfg(test)] pub(crate) struct TimeoutResolver(pub(crate) std::time::Duration); #[cfg(test)] impl Resolver for TimeoutResolver { async fn lookup_txt(&self, _query: &str) -> Option { tokio::time::sleep(self.0).await; None } }