diff --git a/crates/recursor/src/lib.rs b/crates/recursor/src/lib.rs index c9a4b94f..d22b7b64 100644 --- a/crates/recursor/src/lib.rs +++ b/crates/recursor/src/lib.rs @@ -34,7 +34,7 @@ pub use error::{Error, ErrorKind}; pub use hickory_proto as proto; pub use hickory_resolver as resolver; pub use hickory_resolver::config::NameServerConfig; -pub use recursor::Recursor; +pub use recursor::{Recursor, RecursorBuilder}; fn is_security_aware() -> bool { cfg!(feature = "dnssec") diff --git a/crates/recursor/src/recursor.rs b/crates/recursor/src/recursor.rs index 1c477734..56e2eb7f 100644 --- a/crates/recursor/src/recursor.rs +++ b/crates/recursor/src/recursor.rs @@ -37,6 +37,66 @@ use crate::{ /// Set of nameservers by the zone name type NameServerCache

= LruCache>; +/// A `Recursor` builder +#[derive(Clone, Copy)] +pub struct RecursorBuilder { + ns_cache_size: usize, + record_cache_size: usize, + #[cfg(feature = "dnssec")] + security_aware: bool, +} + +impl Default for RecursorBuilder { + fn default() -> Self { + Self { + ns_cache_size: 1024, + record_cache_size: 1048576, + #[cfg(feature = "dnssec")] + security_aware: false, + } + } +} + +impl RecursorBuilder { + /// Sets the size of the list of cached name servers + pub fn ns_cache_size(&mut self, size: usize) -> &mut Self { + self.ns_cache_size = size; + self + } + + /// Sets the size of the list of cached records + pub fn record_cache_size(&mut self, size: usize) -> &mut Self { + self.record_cache_size = size; + self + } + + /// Enables or disables (DNSSEC) security awareness + #[cfg(feature = "dnssec")] + pub fn security_aware(&mut self, security_aware: bool) -> &mut Self { + self.security_aware = security_aware; + self + } + + /// Construct a new recursor using the list of NameServerConfigs for the root node list + /// + /// # Panics + /// + /// This will panic if the roots are empty. + pub fn build(&self, roots: impl Into) -> Result { + #[cfg(not(feature = "dnssec"))] + let security_aware = false; + #[cfg(feature = "dnssec")] + let security_aware = self.security_aware; + + Recursor::build( + roots, + self.ns_cache_size, + self.record_cache_size, + security_aware, + ) + } +} + /// A top down recursive resolver which operates off a list of roots for initial recursive requests. /// /// This is the well known root nodes, referred to as hints in RFCs. See the IANA [Root Servers](https://www.iana.org/domains/root/servers) list. @@ -44,18 +104,21 @@ pub struct Recursor { roots: RecursorPool, name_server_cache: Mutex>, record_cache: DnsLru, + security_aware: bool, } impl Recursor { - /// Construct a new recursor using the list of NameServerConfigs for the root node list - /// - /// # Panics - /// - /// This will panic if the roots are empty. - pub fn new( + /// Short-hand for `RecursorBuilder::default` + #[allow(clippy::new_ret_no_self)] + pub fn new() -> RecursorBuilder { + RecursorBuilder::default() + } + + fn build( roots: impl Into, ns_cache_size: usize, record_cache_size: usize, + security_aware: bool, ) -> Result { // configure the hickory-resolver let roots: NameServerConfigGroup = roots.into(); @@ -74,6 +137,7 @@ impl Recursor { roots, name_server_cache, record_cache, + security_aware, }) } @@ -244,7 +308,7 @@ impl Recursor { request_time: Instant, query_has_dnssec_ok: bool, ) -> Result { - if crate::is_security_aware() { + if self.security_aware { // TODO RFC4035 section 4.5 recommends caching "each response as a single atomic entry // containing the entire answer, including the named RRset and any associated DNSSEC // RRs" @@ -289,7 +353,7 @@ impl Recursor { let ns = ns.ok_or_else(|| Error::from(format!("no nameserver found for {zone}")))?; debug!("found zone {} for {}", ns.zone(), query); - let dnssec = if crate::is_security_aware() { + let dnssec = if self.security_aware { Dnssec::Aware { query_has_dnssec_ok, } diff --git a/crates/server/src/store/recursor/authority.rs b/crates/server/src/store/recursor/authority.rs index a6b94825..56667689 100644 --- a/crates/server/src/store/recursor/authority.rs +++ b/crates/server/src/store/recursor/authority.rs @@ -73,7 +73,10 @@ impl RecursiveAuthority { }); } - let recursor = Recursor::new(roots, config.ns_cache_size, config.record_cache_size) + let recursor = Recursor::new() + .ns_cache_size(config.ns_cache_size) + .record_cache_size(config.record_cache_size) + .build(roots) .map_err(|e| format!("failed to initialize recursor: {e}"))?; Ok(Self { diff --git a/util/src/bin/recurse.rs b/util/src/bin/recurse.rs index 3690d882..1bb10eb2 100644 --- a/util/src/bin/recurse.rs +++ b/util/src/bin/recurse.rs @@ -157,7 +157,7 @@ pub async fn main() -> Result<(), Box> { let name = opts.domainname; let ty = opts.ty; - let recursor = Recursor::new(roots, 1024, 1048576)?; + let recursor = Recursor::new().build(roots)?; // execute query println!(