iroh_dns/
dns.rs

1//! Configurable DNS resolver.
2//!
3//! The main export is the [`DnsResolver`] struct. It provides methods to resolve domain names
4//! to IPv4 and IPv6 addresses, and to look up TXT records. Additionally, the resolver features
5//! methods to resolve the [`EndpointInfo`] for an iroh [`EndpointId`] from `_iroh` TXT records.
6//! See the [`crate::endpoint_info`] module documentation for details on how iroh endpoint records
7//! are structured.
8
9use std::{
10    collections::VecDeque,
11    fmt,
12    future::Future,
13    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
14    pin::Pin,
15    sync::Arc,
16};
17
18use arc_swap::ArcSwap;
19use iroh_base::EndpointId;
20use n0_error::{AnyError, StackError, e, stack_error};
21use n0_future::{
22    Either, MaybeFuture, Stream, StreamExt,
23    boxed::BoxFuture,
24    stream,
25    time::{self, Duration},
26};
27use tokio::sync::Notify;
28use url::Url;
29
30use crate::{ParseError, endpoint_info::EndpointInfo};
31
32/// Default DNS query timeout.
33pub const DNS_TIMEOUT: Duration = Duration::from_secs(3);
34
35/// The n0 address lookup DNS origin, for production.
36pub const N0_DNS_ENDPOINT_ORIGIN_PROD: &str = "dns.iroh.link.";
37/// The n0 address lookup DNS origin, for testing.
38pub const N0_DNS_ENDPOINT_ORIGIN_STAGING: &str = "staging-dns.iroh.link.";
39
40/// Exposes a JVM to iroh so that we can read the system's DNS configuration.
41///
42/// This calls [`ndk_context::initialize_android_context`] to expose a
43/// `JavaVM` and Application Context to Rust code so that we can use JNI.
44/// This is required to get the configured nameservers on Android.
45///
46/// If this function is not called, fetching the configured nameservers will fail
47/// and the default [`DnsResolver`] will use fallback nameservers instead.
48///
49/// If you call [`ndk_context::initialize_android_context`] already somewhere
50/// up the stack in your app, or use a crate like `ndk-glue` or `android-activity`
51/// that do this for you, then there's no need to call this function.
52///
53/// If you don't use a glue crate, a typical way to initialize the context is
54/// via `JNI_OnLoad`:
55///
56/// *Note: `install_android_jni_context` is reexported from `iroh`, so you can substitute
57/// `iroh_dns` for `iroh` below.*
58///
59/// ```ignore
60/// #[cfg(target_os = "android")]
61/// #[no_mangle]
62/// pub extern "C" fn JNI_OnLoad(
63///     vm: jni::JavaVM,
64///     res: *mut std::os::raw::c_void,
65/// ) -> jni::sys::jint {
66///     use std::ffi::c_void;
67///
68///     let vm = vm.get_java_vm_pointer() as *mut c_void;
69///     unsafe {
70///         iroh_dns::install_android_jni_context(vm, res);
71///     }
72///     jni::JNIVersion::V6.into()
73/// }
74/// ```
75///
76/// # Safety
77///
78/// Both the `java_vm` and `context_jobject` pointers must remain valid until the process exits.
79/// See also [`ndk_context::initialize_android_context`].
80///
81/// [`ndk_context`]: https://docs.rs/ndk-context
82/// [`ndk_context::initialize_android_context`]: https://docs.rs/ndk-context/latest/ndk_context/fn.initialize_android_context.html
83//
84// Inlined rather than re-exported from `n0-dns-resolver`: a `pub use` of the
85// resolver crate's item does not resolve on a `doc`-only build (the dependency
86// is compiled without the `doc` cfg, so its item is absent), so we mirror the
87// signature and cfg here and delegate to it on Android.
88#[cfg(any(target_os = "android", doc))]
89pub unsafe fn install_android_jni_context(
90    java_vm: *mut std::ffi::c_void,
91    application_context: *mut std::ffi::c_void,
92) {
93    #[cfg(target_os = "android")]
94    unsafe {
95        n0_dns_resolver::install_android_jni_context(java_vm, application_context);
96    }
97    #[cfg(not(target_os = "android"))]
98    let _ = (java_vm, application_context);
99}
100
101/// Percent of total delay to jitter. 20 means +/- 20% of delay.
102const MAX_JITTER_PERCENT: u64 = 20;
103
104/// Trait for DNS resolvers used in iroh.
105pub trait Resolver: fmt::Debug + Send + Sync + 'static {
106    /// Looks up an IPv4 address.
107    fn lookup_ipv4(&self, host: String) -> BoxFuture<Result<BoxIter<Ipv4Addr>, DnsError>>;
108
109    /// Looks up an IPv6 address.
110    fn lookup_ipv6(&self, host: String) -> BoxFuture<Result<BoxIter<Ipv6Addr>, DnsError>>;
111
112    /// Looks up TXT records.
113    fn lookup_txt(&self, host: String) -> BoxFuture<Result<BoxIter<TxtRecordData>, DnsError>>;
114
115    /// Clears the internal cache.
116    fn clear_cache(&self);
117
118    /// Returns a freshly-built resolver to replace `self` after a network change.
119    ///
120    /// The returned resolver replaces the previous one inside [`DnsResolver`] via an
121    /// atomic swap. Build a new instance with re-bound sockets and re-read nameserver
122    /// configuration rather than mutating in place. Must not perform IO: defer DNS
123    /// queries and socket binds until the new resolver is first used. May be called
124    /// concurrently, in which case all but one allocated replacement is dropped unused.
125    fn reset(&self) -> Box<dyn Resolver>;
126}
127
128/// Boxed iterator alias.
129///
130/// Used in return types of [`Resolver`] methods.
131pub type BoxIter<T> = Box<dyn Iterator<Item = T> + Send + 'static>;
132
133/// Potential errors related to DNS operations.
134///
135/// Distinct, matchable causes have their own variant, such as [`Self::NxDomain`]
136/// (a nameserver reported the name does not exist, unlike a transient transport
137/// or timeout failure). Any other cause is reported as [`Self::Resolve`], whose
138/// [`AnyError`] source carries the underlying error.
139///
140/// The resolver backing this crate is an implementation detail and its error
141/// types are not part of the public API. If you depend on `n0-dns-resolver`
142/// directly and need the exact reason, downcast a [`Self::Resolve`] source:
143///
144/// ```ignore
145/// if let DnsError::Resolve { source, .. } = &err {
146///     if let Some(inner) = source.downcast_ref::<n0_dns_resolver::Error>() {
147///         // match on `inner` for the specific failure
148///     }
149/// }
150/// ```
151#[allow(missing_docs)]
152#[stack_error(derive, add_meta, std_sources)]
153#[non_exhaustive]
154pub enum DnsError {
155    #[error("Request timed out")]
156    Timeout {},
157    #[error("No response")]
158    NoResponse {},
159    #[error("Resolve failed, IPv4: {ipv4}, IPv6: {ipv6}")]
160    ResolveBoth {
161        ipv4: Box<DnsError>,
162        ipv6: Box<DnsError>,
163    },
164    #[error("Missing host")]
165    MissingHost {},
166    /// A resolution failure not covered by another variant.
167    ///
168    /// Downcast the source (see the type-level docs) to recover the specific cause.
169    #[error("Failed to resolve")]
170    Resolve {
171        #[error(from)]
172        source: AnyError,
173    },
174    #[error("Invalid DNS response: not a query for _iroh.z32encodedpubkey")]
175    InvalidResponse {},
176    /// The domain name does not exist (NXDOMAIN).
177    ///
178    /// A nameserver authoritatively reported that the name does not exist, as
179    /// opposed to a transient transport or timeout failure, so retrying will not
180    /// make the lookup succeed.
181    //
182    // Appended after `InvalidResponse` so the pre-existing variants keep their
183    // discriminants.
184    #[error("Domain name does not exist (NXDOMAIN)")]
185    NxDomain {},
186}
187
188/// Potential errors related to DNS endpoint address lookups.
189#[allow(missing_docs)]
190#[stack_error(derive, add_meta, from_sources)]
191#[non_exhaustive]
192pub enum LookupError {
193    #[error("Malformed txt from lookup")]
194    ParseError { source: ParseError },
195    #[error("Failed to resolve TXT record")]
196    LookupFailed { source: DnsError },
197}
198
199/// Error returned when a staggered call fails.
200#[stack_error(derive, add_meta)]
201#[error("no calls succeeded: [{}]", errors.iter().map(|e| e.to_string()).collect::<Vec<_>>().join(""))]
202pub struct StaggeredError<E: n0_error::StackError + 'static> {
203    errors: Vec<E>,
204}
205
206impl<E: StackError + 'static> StaggeredError<E> {
207    /// Returns an iterator over all encountered errors.
208    pub fn iter(&self) -> impl Iterator<Item = &E> {
209        self.errors.iter()
210    }
211}
212
213/// Builder for [`DnsResolver`].
214#[derive(Debug, Clone, Default)]
215pub struct Builder {
216    use_system_defaults: bool,
217    nameservers: Vec<NameserverConfig>,
218    fallback_mode: FallbackMode,
219    fallback_nameservers: Vec<NameserverConfig>,
220    #[cfg(not(wasm_browser))]
221    tls_client_config: Option<rustls::ClientConfig>,
222}
223
224/// How the resolver uses its fallback nameservers relative to the primary ones.
225///
226/// The *primary* nameservers come from the system DNS configuration and the
227/// nameservers added on the [`Builder`]. The *fallback* nameservers default to a
228/// set of public resolvers, which [`Builder::fallback_nameserver_configs`] can
229/// override. Select the mode with [`Builder::with_fallback_mode`].
230#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
231#[non_exhaustive]
232pub enum FallbackMode {
233    /// Never queries the fallback nameservers.
234    Never,
235    /// Races the fallback nameservers alongside the primary ones from the start.
236    Eager,
237    /// Uses the fallback nameservers only when the system configuration is empty.
238    ///
239    /// A system configuration counts as empty when it yields no nameservers,
240    /// whether because it could not be read or because it listed none. One that
241    /// did yield nameservers is never supplemented: if those fail at query time
242    /// the lookup fails rather than escalating. Without
243    /// [`Builder::with_system_defaults`] there is no configuration at all, which
244    /// also counts as empty, so this behaves like [`Self::Eager`] then.
245    IfSystemEmpty,
246    /// Keeps the fallback nameservers as a lower-priority tier.
247    ///
248    /// They are queried only once every primary nameserver has failed or timed
249    /// out. This is the default.
250    #[default]
251    Deferred,
252}
253
254impl FallbackMode {
255    /// Converts into the resolver crate's fallback mode.
256    ///
257    /// Returns `None` for [`Self::Never`]. The resolver crate has no such mode,
258    /// because its fallback tier starts empty and querying nothing is simply
259    /// adding nothing. Here the tier defaults to public resolvers, so we keep a
260    /// way to say no.
261    fn to_resolver_mode(self) -> Option<n0_dns_resolver::FallbackMode> {
262        match self {
263            FallbackMode::Never => None,
264            FallbackMode::Eager => Some(n0_dns_resolver::FallbackMode::Eager),
265            FallbackMode::IfSystemEmpty => Some(n0_dns_resolver::FallbackMode::IfSystemEmpty),
266            FallbackMode::Deferred => Some(n0_dns_resolver::FallbackMode::Deferred),
267        }
268    }
269}
270
271/// A DNS nameserver configuration for a single server.
272#[derive(Debug, Clone, PartialEq, Eq)]
273pub struct NameserverConfig {
274    addr: SocketAddr,
275    protocol: DnsProtocol,
276    server_name: Option<String>,
277}
278
279impl NameserverConfig {
280    fn new(addr: IpAddr, port: u16, protocol: DnsProtocol) -> Self {
281        Self {
282            addr: SocketAddr::new(addr, port),
283            protocol,
284            server_name: None,
285        }
286    }
287
288    /// Creates a new nameserver config for DNS over UDP (Do53).
289    ///
290    /// This nameserver will be using DNS over UDP on port 53, see
291    /// [`Self::with_port`] to further customise this nameserver.
292    pub fn udp(addr: IpAddr) -> Self {
293        Self::new(addr, 53, DnsProtocol::Udp)
294    }
295
296    /// Creates a new nameserver config for DNS over TCP (Do53).
297    ///
298    /// This nameserver will be using DNS over TCP on port 53, see
299    /// [`Self::with_port`] to further customise this nameserver.
300    pub fn tcp(addr: IpAddr) -> Self {
301        Self::new(addr, 53, DnsProtocol::Tcp)
302    }
303
304    /// Creates a new nameserver config for DNS over TLS (DoT).
305    ///
306    /// This nameserver will be using DNS over TLS on port 853, see
307    /// [`Self::with_port`] and [`Self::with_tls_server_name`] to further customise
308    /// this nameserver.
309    pub fn tls(addr: IpAddr) -> Self {
310        Self::new(addr, 853, DnsProtocol::Tls)
311    }
312
313    /// Creates a new nameserver config for DNS over HTTPS (DoH).
314    ///
315    /// This nameserver will be using DNS over HTTPS on port 443, see
316    /// [`Self::with_port`] and [`Self::with_tls_server_name`] to further customise
317    /// this nameserver.
318    pub fn https(addr: IpAddr) -> Self {
319        Self::new(addr, 443, DnsProtocol::Https)
320    }
321
322    /// Sets the port for the nameserver.
323    ///
324    /// # Returns
325    ///
326    /// A new instance is returned, this struct is essentially a builder for itself.
327    pub fn with_port(self, port: u16) -> Self {
328        Self {
329            addr: SocketAddr::new(self.addr.ip(), port),
330            ..self
331        }
332    }
333
334    /// Sets the TLS server name for the nameserver.
335    ///
336    /// Nameservers are always connected to via IP address. However for the protocols
337    /// running over TLS (DoT & DoH), the server certificate may use a server name rather
338    /// than IP address. When enabled the TLS `ClientHello` will use this server name in the
339    /// SNI.
340    ///
341    /// # Returns
342    ///
343    /// A new instance is returned, this struct is essentially a builder for itself.
344    pub fn with_tls_server_name(self, server_name: impl Into<String>) -> Self {
345        Self {
346            server_name: Some(server_name.into()),
347            ..self
348        }
349    }
350
351    fn into_resolver_nameserver(self) -> n0_dns_resolver::Nameserver {
352        if let Some(server_name) = self.server_name {
353            return n0_dns_resolver::Nameserver::with_server_name(
354                self.addr,
355                self.protocol.to_resolver_protocol(),
356                server_name,
357            );
358        }
359        n0_dns_resolver::Nameserver::new(self.addr, self.protocol.to_resolver_protocol())
360    }
361}
362
363/// Protocols over which DNS records can be resolved.
364#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
365#[non_exhaustive]
366pub enum DnsProtocol {
367    /// DNS over UDP
368    ///
369    /// This is the classic DNS protocol and supported by most DNS servers.
370    #[default]
371    Udp,
372    /// DNS over TCP
373    ///
374    /// This is specified in the original DNS RFCs, but is not supported by all DNS servers.
375    Tcp,
376    /// DNS over TLS
377    ///
378    /// Performs DNS lookups over TLS-encrypted TCP connections, as defined in [RFC 7858].
379    ///
380    /// [RFC 7858]: https://www.rfc-editor.org/rfc/rfc7858.html
381    Tls,
382    /// DNS over HTTPS
383    ///
384    /// Performs DNS lookups over HTTPS, as defined in [RFC 8484].
385    ///
386    /// [RFC 8484]: https://www.rfc-editor.org/rfc/rfc8484.html
387    Https,
388}
389
390impl DnsProtocol {
391    /// Converts into the resolver crate's protocol type.
392    fn to_resolver_protocol(self) -> n0_dns_resolver::DnsProtocol {
393        match self {
394            DnsProtocol::Udp => n0_dns_resolver::DnsProtocol::Udp,
395            DnsProtocol::Tcp => n0_dns_resolver::DnsProtocol::Tcp,
396            DnsProtocol::Tls => n0_dns_resolver::DnsProtocol::Tls,
397            DnsProtocol::Https => n0_dns_resolver::DnsProtocol::Https,
398        }
399    }
400}
401
402impl Builder {
403    /// Makes the builder respect the host system's DNS configuration.
404    ///
405    /// The configuration is read in a platform-specific way. If that fails, or
406    /// if it lists no nameservers, the fallback tier answers instead, subject
407    /// to the [`FallbackMode`].
408    pub fn with_system_defaults(mut self) -> Self {
409        self.use_system_defaults = true;
410        self
411    }
412
413    /// Adds a single nameserver config.
414    ///
415    /// For DNS-over-TLS and DNS-over-HTTPS, the server name is inferred from the
416    /// IP address. To set a different one, build a [`NameserverConfig`] with
417    /// [`NameserverConfig::with_tls_server_name`] and pass it to
418    /// [`Self::add_nameserver_config`].
419    #[deprecated(since = "1.2.0", note = "use add_nameserver_config()")]
420    pub fn with_nameserver(mut self, addr: SocketAddr, protocol: DnsProtocol) -> Self {
421        self.nameservers.push(NameserverConfig {
422            addr,
423            protocol,
424            server_name: None,
425        });
426        self
427    }
428
429    /// Adds a list of nameserver configs.
430    ///
431    /// For DNS-over-TLS and DNS-over-HTTPS, the server name is inferred from the
432    /// IP address. To set a different one, build a [`NameserverConfig`] with
433    /// [`NameserverConfig::with_tls_server_name`] and pass it to
434    /// [`Self::add_nameserver_configs`].
435    #[deprecated(since = "1.2.0", note = "use add_nameserver_configs()")]
436    pub fn with_nameservers(
437        mut self,
438        nameservers: impl IntoIterator<Item = (SocketAddr, DnsProtocol)>,
439    ) -> Self {
440        self.nameservers.extend(
441            nameservers
442                .into_iter()
443                .map(|(addr, protocol)| NameserverConfig {
444                    addr,
445                    protocol,
446                    server_name: None,
447                }),
448        );
449        self
450    }
451
452    /// Adds a single nameserver config.
453    pub fn add_nameserver_config(mut self, nameserver: NameserverConfig) -> Self {
454        self.nameservers.push(nameserver);
455        self
456    }
457
458    /// Adds a list of nameserver configs.
459    pub fn add_nameserver_configs(
460        mut self,
461        nameservers: impl IntoIterator<Item = NameserverConfig>,
462    ) -> Self {
463        self.nameservers.extend(nameservers);
464        self
465    }
466
467    /// Sets a custom TLS client configuration.
468    ///
469    /// This overrides the default configuration used for DNS-over-TLS and
470    /// DNS-over-HTTPS.
471    ///
472    /// When neither the `tls-ring` nor `tls-aws-lc-rs` feature is enabled,
473    /// DNS-over-TLS and DNS-over-HTTPS require a custom configuration containing
474    /// a crypto provider, supplied with [`Self::tls_client_config`].
475    #[cfg(not(wasm_browser))]
476    pub fn tls_client_config(mut self, client_config: rustls::ClientConfig) -> Self {
477        self.tls_client_config = Some(client_config);
478        self
479    }
480
481    /// Sets how the fallback nameservers are used relative to the primary ones.
482    ///
483    /// The default is [`FallbackMode::Deferred`]: the fallback nameservers are a
484    /// lower-priority tier, queried only when the primary nameservers fail or
485    /// time out. See [`FallbackMode`] for the other modes.
486    pub fn with_fallback_mode(mut self, mode: FallbackMode) -> Self {
487        self.fallback_mode = mode;
488        self
489    }
490
491    /// Disables the fallback nameservers, so only the primary ones are queried.
492    ///
493    /// Shorthand for [`Self::with_fallback_mode`] with [`FallbackMode::Never`].
494    pub fn disable_fallback(self) -> Self {
495        self.with_fallback_mode(FallbackMode::Never)
496    }
497
498    /// Adds nameservers to the fallback tier, in place of the default ones.
499    ///
500    /// Appends, so it can be called repeatedly. Adding any nameserver here
501    /// replaces the default public resolvers, which are only used when this
502    /// list is left empty. Has no effect when the fallback mode is
503    /// [`FallbackMode::Never`].
504    pub fn fallback_nameserver_configs(
505        mut self,
506        nameservers: impl IntoIterator<Item = NameserverConfig>,
507    ) -> Self {
508        self.fallback_nameservers.extend(nameservers);
509        self
510    }
511
512    /// Builds the DNS resolver.
513    pub fn build(self) -> DnsResolver {
514        DnsResolver::custom(DefaultResolver(Arc::new(
515            self.into_resolver_builder().build(),
516        )))
517    }
518
519    /// Maps this configuration onto the resolver crate's builder.
520    ///
521    /// Split out from [`Self::build`] so that the mapping can be asserted on
522    /// without a resolver in the way.
523    fn into_resolver_builder(self) -> n0_dns_resolver::Builder {
524        // The resolver crate's builder starts empty, so every source this
525        // builder was asked for is added explicitly.
526        let mut builder = n0_dns_resolver::DnsResolver::builder().nameservers(
527            self.nameservers
528                .into_iter()
529                .map(NameserverConfig::into_resolver_nameserver),
530        );
531        if self.use_system_defaults {
532            builder = builder.use_system_config();
533        }
534        // Unlike the resolver crate, the fallback tier is opt-out here: leaving
535        // the list empty means the default public resolvers rather than none.
536        if let Some(mode) = self.fallback_mode.to_resolver_mode() {
537            builder = builder.fallback_mode(mode);
538            builder = if self.fallback_nameservers.is_empty() {
539                builder.default_fallback_nameservers()
540            } else {
541                builder.fallback_nameservers(
542                    self.fallback_nameservers
543                        .into_iter()
544                        .map(NameserverConfig::into_resolver_nameserver),
545                )
546            };
547        }
548        #[cfg(not(wasm_browser))]
549        if let Some(tls_client_config) = self.tls_client_config {
550            builder = builder.tls_client_config(tls_client_config);
551        }
552        builder
553    }
554}
555
556/// Adapts [`n0_dns_resolver::DnsResolver`] to the [`Resolver`] trait.
557///
558/// Converts the resolver crate's types to this crate's own, so that
559/// `n0-dns-resolver` stays an internal detail rather than part of the public API.
560#[derive(Debug)]
561struct DefaultResolver(Arc<n0_dns_resolver::DnsResolver>);
562
563impl Resolver for DefaultResolver {
564    fn lookup_ipv4(&self, host: String) -> BoxFuture<Result<BoxIter<Ipv4Addr>, DnsError>> {
565        let this = self.0.clone();
566        Box::pin(async move {
567            let list = this.lookup_ipv4(host).await.map_err(map_resolve_error)?;
568            let iter: BoxIter<_> = Box::new(list.into_iter());
569            Ok(iter)
570        })
571    }
572
573    fn lookup_ipv6(&self, host: String) -> BoxFuture<Result<BoxIter<Ipv6Addr>, DnsError>> {
574        let this = self.0.clone();
575        Box::pin(async move {
576            let list = this.lookup_ipv6(host).await.map_err(map_resolve_error)?;
577            let iter: BoxIter<_> = Box::new(list.into_iter());
578            Ok(iter)
579        })
580    }
581
582    fn lookup_txt(&self, host: String) -> BoxFuture<Result<BoxIter<TxtRecordData>, DnsError>> {
583        let this = self.0.clone();
584        Box::pin(async move {
585            let list = this.lookup_txt(host).await.map_err(map_resolve_error)?;
586            let iter: BoxIter<TxtRecordData> = Box::new(list.into_iter().map(convert_txt));
587            Ok(iter)
588        })
589    }
590
591    fn clear_cache(&self) {
592        self.0.clear_cache();
593    }
594
595    fn reset(&self) -> Box<dyn Resolver> {
596        Box::new(DefaultResolver(Arc::new(self.0.reset())))
597    }
598}
599
600/// Maps an [`n0_dns_resolver::Error`] onto this crate's [`DnsError`].
601///
602/// A private function rather than a `From` impl, so `n0_dns_resolver`'s error
603/// types stay out of this crate's public API. The distinct, matchable causes map
604/// to their own variant; every other cause becomes [`DnsError::Resolve`] carrying
605/// the original error as its [`AnyError`] source, which callers can downcast (see
606/// the [`DnsError`] docs).
607fn map_resolve_error(err: n0_dns_resolver::Error) -> DnsError {
608    use n0_dns_resolver::Error as E;
609    match err {
610        E::Timeout { .. } => e!(DnsError::Timeout),
611        E::NoResponse { .. } => e!(DnsError::NoResponse),
612        E::NxDomain { .. } => e!(DnsError::NxDomain),
613        E::InvalidResponse { .. } => e!(DnsError::InvalidResponse),
614        other => e!(DnsError::Resolve, AnyError::from_stack(other)),
615    }
616}
617
618/// Converts a resolver TXT record into this crate's [`TxtRecordData`].
619///
620/// Both types hold the character-strings as `Box<[Box<[u8]>]>`, so this hands
621/// over the resolver's owned slices directly rather than reallocating each one.
622fn convert_txt(txt: n0_dns_resolver::TxtRecordData) -> TxtRecordData {
623    TxtRecordData(txt.into_boxed_slices())
624}
625
626/// The DNS resolver used throughout `iroh`.
627///
628/// By default, we use a built-in resolver that reads the system's DNS configuration.
629/// The nameservers can be customized by constructing the resolver with [`Self::builder`].
630/// Alternatively, you can create a fully custom DNS resolver by implementing the [`Resolver`]
631/// trait and creating the resolver with [`Self::custom`].
632///
633/// # Usage on Android
634///
635/// The system-defaults reader uses JNI through [`ndk_context`], which must be
636/// initialized with a `JavaVM` and `Application` context before the resolver
637/// is constructed. Glue crates like ndk-glue and android-activity do this
638/// before `main`. Apps that don't use either should call [`install_android_jni_context`]
639/// once at startup, see docs there for details.
640///
641/// If `ndk_context` is not initialized, fetching the system config on Android will fail
642/// and the resolver will use the fallback nameservers instead, subject to the configured
643/// [`FallbackMode`]. Due to how things are implemented in `ndk_context`, detecting the
644/// failure relies on unwinding a panic. If your app uses `panic = "abort"` in its
645/// compilation profile, this doesn't work, so in that case your app will panic if no
646/// JNI context is initialized.
647/// Therefore, either make sure that the JNI context is installed, or don't use
648/// `panic = "abort"`.
649///
650/// [`install_android_jni_context`]: crate::install_android_jni_context
651/// [`ndk_context`]: https://docs.rs/ndk-context
652#[derive(Debug, Clone)]
653pub struct DnsResolver {
654    inner: Arc<Inner>,
655}
656
657/// Shared state behind [`DnsResolver`].
658#[derive(Debug)]
659struct Inner {
660    /// Wakes in-flight [`Self::op`] calls when the resolver is swapped.
661    notify_reset: Notify,
662    resolver: ArcSwap<Box<dyn Resolver>>,
663}
664
665impl Inner {
666    fn new(inner: Box<dyn Resolver>) -> Self {
667        Self {
668            notify_reset: Notify::new(),
669            resolver: ArcSwap::from_pointee(inner),
670        }
671    }
672
673    /// Atomically swaps the resolver and wakes in-flight [`Self::op`] calls.
674    ///
675    /// The swap happens before the wake. An op that observes or misses the wake is
676    /// then guaranteed to load the new resolver. Non-blocking.
677    ///
678    /// Under contention only the first concurrent caller's swap lands; the others
679    /// drop their freshly-built resolver. The winner's notification is enough since
680    /// every in-flight op will pick up the new resolver on its next iteration.
681    fn reset(&self) {
682        let current = self.resolver.load();
683        let new = Arc::new(current.reset());
684        let prev = self.resolver.compare_and_swap(&current, new);
685        if Arc::ptr_eq(&current, &prev) {
686            self.notify_reset.notify_waiters();
687        }
688    }
689
690    fn clear_cache(&self) {
691        self.resolver.load().clear_cache();
692    }
693
694    /// Runs `f(resolver)` with a timeout, restarting against the new resolver if
695    /// [`Self::reset`] fires.
696    ///
697    /// Three things race in `biased` order: the lookup completes (returned even if a
698    /// reset happened concurrently, since a successful result is still valid), a reset
699    /// is observed (drop the in-flight future and re-run `f`), or the timeout elapses.
700    ///
701    /// `timeout` is per-attempt. Each retry starts a fresh sleep, so the wall-clock
702    /// total can exceed it if many resets fire. This is intentional: a fresh attempt
703    /// against a just-changed network should not inherit the previous attempt's
704    /// remaining budget.
705    ///
706    /// `f` may be invoked more than once and so must be `Fn`. Captured state must be
707    /// reusable across calls, typically by cloning inside the closure body.
708    ///
709    /// `notified` is enabled before `load_full`. Combined with [`Self::reset`]'s
710    /// swap-then-notify ordering: a wake missed before `enable()` had already been
711    /// preceded by the swap, so the following `load_full` returns the new resolver.
712    async fn op<F, Fut, R, E>(&self, timeout: Duration, f: F) -> Result<R, DnsError>
713    where
714        E: 'static + Send + Into<DnsError>,
715        R: 'static + Send,
716        F: 'static + Send + Fn(Arc<Box<dyn Resolver>>) -> Fut,
717        Fut: 'static + Send + Future<Output = Result<R, E>>,
718    {
719        loop {
720            let notified = self.notify_reset.notified();
721            tokio::pin!(notified);
722            notified.as_mut().enable();
723
724            let timeout = n0_future::time::sleep(timeout);
725            tokio::pin!(timeout);
726
727            let resolver = self.resolver.load_full();
728            let fut = f(resolver);
729            tokio::pin!(fut);
730
731            tokio::select! {
732                biased;
733                res = fut => return res.map_err(Into::into),
734                _ = notified => continue,
735                _ = timeout => return Err(e!(DnsError::Timeout)),
736            }
737        }
738    }
739}
740
741impl DnsResolver {
742    /// Creates a new DNS resolver with sensible cross-platform defaults.
743    ///
744    /// Reads the host system's DNS configuration, with the default public
745    /// resolvers behind it as a fallback tier. Those are queried only when the
746    /// system configuration cannot be read, which happens on some Android
747    /// versions, or when its nameservers do not answer.
748    pub fn new() -> Self {
749        Builder::default().with_system_defaults().build()
750    }
751
752    /// Creates a new DNS resolver configured with a single UDP DNS nameserver.
753    pub fn with_nameserver(nameserver: SocketAddr) -> Self {
754        Builder::default()
755            .add_nameserver_config(
756                NameserverConfig::udp(nameserver.ip()).with_port(nameserver.port()),
757            )
758            .build()
759    }
760
761    /// Creates a builder to construct a DNS resolver with custom options.
762    pub fn builder() -> Builder {
763        Builder::default()
764    }
765
766    /// Creates a new [`DnsResolver`] from a struct that implements [`Resolver`].
767    ///
768    /// If you need more customization for DNS resolving than the [`Builder`] allows, you can
769    /// implement the [`Resolver`] trait on a struct and implement DNS resolution
770    /// however you see fit.
771    pub fn custom(resolver: impl Resolver) -> Self {
772        Self {
773            inner: Arc::new(Inner::new(Box::new(resolver))),
774        }
775    }
776
777    /// Removes all entries from the cache.
778    pub fn clear_cache(&self) {
779        self.inner.clear_cache();
780    }
781
782    /// Replaces the inner resolver with a freshly-built one.
783    ///
784    /// Call this on a major host network change to pick up the new system DNS
785    /// configuration and rebind sockets. The swap is atomic and non-blocking;
786    /// in-flight lookups retry against the new resolver. See [`Resolver::reset`].
787    pub fn reset(&self) {
788        self.inner.reset();
789    }
790
791    /// Looks up a TXT record.
792    pub async fn lookup_txt<T: ToString>(
793        &self,
794        host: T,
795        timeout: Duration,
796    ) -> Result<impl Iterator<Item = TxtRecordData>, DnsError> {
797        let host = host.to_string();
798        let res = self
799            .inner
800            .op(timeout, move |resolver| resolver.lookup_txt(host.clone()))
801            .await?;
802        Ok(res)
803    }
804
805    /// Performs an IPv4 lookup with a timeout.
806    pub async fn lookup_ipv4<T: ToString>(
807        &self,
808        host: T,
809        timeout: Duration,
810    ) -> Result<impl Iterator<Item = IpAddr> + use<T>, DnsError> {
811        let host = host.to_string();
812        let addrs = self
813            .inner
814            .op(timeout, move |resolver| resolver.lookup_ipv4(host.clone()))
815            .await?;
816        Ok(addrs.into_iter().map(IpAddr::V4))
817    }
818
819    /// Performs an IPv6 lookup with a timeout.
820    pub async fn lookup_ipv6<T: ToString>(
821        &self,
822        host: T,
823        timeout: Duration,
824    ) -> Result<impl Iterator<Item = IpAddr> + use<T>, DnsError> {
825        let host = host.to_string();
826        let addrs = self
827            .inner
828            .op(timeout, move |resolver| resolver.lookup_ipv6(host.clone()))
829            .await?;
830        Ok(addrs.into_iter().map(IpAddr::V6))
831    }
832
833    /// Resolves IPv4 and IPv6 in parallel with a timeout.
834    ///
835    /// `LookupIpStrategy::Ipv4AndIpv6` will wait for ipv6 resolution timeout, even if it is
836    /// not usable on the stack, so we manually query both lookups concurrently and time them out
837    /// individually.
838    pub async fn lookup_ipv4_ipv6<T: ToString>(
839        &self,
840        host: T,
841        timeout: Duration,
842    ) -> Result<impl Iterator<Item = IpAddr> + use<T>, DnsError> {
843        let host = host.to_string();
844        let res = tokio::join!(
845            self.lookup_ipv4(host.clone(), timeout),
846            self.lookup_ipv6(host, timeout)
847        );
848
849        match res {
850            (Ok(ipv4), Ok(ipv6)) => Ok(LookupIter::Both(ipv4.chain(ipv6))),
851            (Ok(ipv4), Err(_)) => Ok(LookupIter::Ipv4(ipv4)),
852            (Err(_), Ok(ipv6)) => Ok(LookupIter::Ipv6(ipv6)),
853            (Err(ipv4_err), Err(ipv6_err)) => Err(e!(DnsError::ResolveBoth {
854                ipv4: Box::new(ipv4_err),
855                ipv6: Box::new(ipv6_err)
856            })),
857        }
858    }
859
860    /// Resolves a hostname from a URL to an IP address.
861    pub async fn resolve_host(
862        &self,
863        url: &Url,
864        prefer_ipv6: bool,
865        timeout: Duration,
866    ) -> Result<IpAddr, DnsError> {
867        let host = url.host().ok_or_else(|| e!(DnsError::MissingHost))?;
868        match host {
869            url::Host::Domain(domain) => {
870                // Need to do a DNS lookup
871                let lookup = tokio::join!(
872                    self.lookup_ipv4(domain, timeout),
873                    self.lookup_ipv6(domain, timeout)
874                );
875                let (v4, v6) = match lookup {
876                    (Err(ipv4_err), Err(ipv6_err)) => {
877                        return Err(e!(DnsError::ResolveBoth {
878                            ipv4: Box::new(ipv4_err),
879                            ipv6: Box::new(ipv6_err)
880                        }));
881                    }
882                    (Err(_), Ok(mut v6)) => (None, v6.next()),
883                    (Ok(mut v4), Err(_)) => (v4.next(), None),
884                    (Ok(mut v4), Ok(mut v6)) => (v4.next(), v6.next()),
885                };
886                if prefer_ipv6 {
887                    v6.or(v4).ok_or_else(|| e!(DnsError::NoResponse))
888                } else {
889                    v4.or(v6).ok_or_else(|| e!(DnsError::NoResponse))
890                }
891            }
892            url::Host::Ipv4(ip) => Ok(IpAddr::V4(ip)),
893            url::Host::Ipv6(ip) => Ok(IpAddr::V6(ip)),
894        }
895    }
896
897    /// Resolves a hostname from a URL to its IP addresses, streamed as they resolve.
898    ///
899    /// IPv4 and IPv6 are looked up concurrently and each address is yielded as
900    /// soon as its lookup completes, so a caller can start dialing without
901    /// waiting for both families.
902    ///
903    /// A lookup failure is swallowed as long as the other family yields an address.
904    /// Only if both lookups fail does the stream yield a single [`DnsError`].
905    ///
906    /// The stream ends once both lookups have finished and every address or the error
907    /// have been yielded.
908    pub fn resolve_host_all<'a>(
909        &'a self,
910        url: &Url,
911        timeout: Duration,
912    ) -> impl Stream<Item = Result<IpAddr, DnsError>> + Send + 'a {
913        let host = match url.host() {
914            None => {
915                return Either::Left(stream::once(Err(e!(DnsError::MissingHost))));
916            }
917            Some(url::Host::Ipv4(ip)) => {
918                return Either::Left(stream::once(Ok(IpAddr::V4(ip))));
919            }
920            Some(url::Host::Ipv6(ip)) => {
921                return Either::Left(stream::once(Ok(IpAddr::V6(ip))));
922            }
923            Some(url::Host::Domain(domain)) => domain.to_string(),
924        };
925
926        type Lookup<'a, A> =
927            Pin<Box<dyn Future<Output = Result<BoxIter<A>, DnsError>> + Send + 'a>>;
928
929        struct State<'a> {
930            v4_fut: MaybeFuture<Lookup<'a, Ipv4Addr>>,
931            v6_fut: MaybeFuture<Lookup<'a, Ipv6Addr>>,
932            v4_err: Option<DnsError>,
933            v6_err: Option<DnsError>,
934            queue: VecDeque<IpAddr>,
935            closed: bool,
936            yielded: bool,
937        }
938
939        let state = State {
940            v4_fut: MaybeFuture::Some(Box::pin({
941                let host = host.clone();
942                self.inner.op(timeout, move |r| r.lookup_ipv4(host.clone()))
943            })),
944            v6_fut: MaybeFuture::Some(Box::pin({
945                let host = host.clone();
946                self.inner.op(timeout, move |r| r.lookup_ipv6(host.clone()))
947            })),
948            v4_err: None,
949            v6_err: None,
950            queue: VecDeque::new(),
951            closed: false,
952            yielded: false,
953        };
954
955        Either::Right(stream::unfold(state, async |mut state| {
956            loop {
957                if state.closed {
958                    return None;
959                }
960
961                if let Some(item) = state.queue.pop_front() {
962                    state.yielded = true;
963                    return Some((Ok(item), state));
964                }
965
966                // Return final error item once both futures completed, or None if items were yielded.
967                if state.v4_fut.is_none() && state.v6_fut.is_none() {
968                    state.closed = true;
969                    if let (Some(v4), Some(v6)) = (state.v4_err.take(), state.v6_err.take()) {
970                        let error = e!(DnsError::ResolveBoth {
971                            ipv4: Box::new(v4),
972                            ipv6: Box::new(v6),
973                        });
974                        return Some((Err(error), state));
975                    } else if !state.yielded {
976                        return Some((Err(e!(DnsError::NoResponse)), state));
977                    } else {
978                        return None;
979                    }
980                }
981                tokio::select! {
982                    // We don't actually care about polling order, but `biased` saves the randomization cost.
983                    biased;
984                    res = &mut state.v4_fut => {
985                        match res {
986                            Ok(items) => state.queue.extend(items.map(IpAddr::V4)),
987                            Err(err) => state.v4_err = Some(err),
988                        }
989                    }
990                    res = &mut state.v6_fut => {
991                        match res {
992                            Ok(items) => state.queue.extend(items.map(IpAddr::V6)),
993                            Err(err) => state.v6_err = Some(err),
994                        }
995                    }
996                }
997            }
998        }))
999    }
1000
1001    /// Performs an IPv4 lookup with a timeout in a staggered fashion.
1002    ///
1003    /// From the moment this function is called, each lookup is scheduled after the delays in
1004    /// `delays_ms` with the first call being done immediately. `[200ms, 300ms]` results in calls
1005    /// at T+0ms, T+200ms and T+300ms. The `timeout` is applied to each call individually. The
1006    /// result of the first successful call is returned, or a summary of all errors otherwise.
1007    pub async fn lookup_ipv4_staggered(
1008        &self,
1009        host: impl ToString,
1010        timeout: Duration,
1011        delays_ms: &[u64],
1012    ) -> Result<impl Iterator<Item = IpAddr>, StaggeredError<DnsError>> {
1013        let host = host.to_string();
1014        let f = || self.lookup_ipv4(host.clone(), timeout);
1015        stagger_call(f, delays_ms).await
1016    }
1017
1018    /// Performs an IPv6 lookup with a timeout in a staggered fashion.
1019    ///
1020    /// From the moment this function is called, each lookup is scheduled after the delays in
1021    /// `delays_ms` with the first call being done immediately. `[200ms, 300ms]` results in calls
1022    /// at T+0ms, T+200ms and T+300ms. The `timeout` is applied to each call individually. The
1023    /// result of the first successful call is returned, or a summary of all errors otherwise.
1024    pub async fn lookup_ipv6_staggered(
1025        &self,
1026        host: impl ToString,
1027        timeout: Duration,
1028        delays_ms: &[u64],
1029    ) -> Result<impl Iterator<Item = IpAddr>, StaggeredError<DnsError>> {
1030        let host = host.to_string();
1031        let f = || self.lookup_ipv6(host.clone(), timeout);
1032        stagger_call(f, delays_ms).await
1033    }
1034
1035    /// Races an IPv4 and IPv6 lookup with a timeout in a staggered fashion.
1036    ///
1037    /// From the moment this function is called, each lookup is scheduled after the delays in
1038    /// `delays_ms` with the first call being done immediately. `[200ms, 300ms]` results in calls
1039    /// at T+0ms, T+200ms and T+300ms. The `timeout` is applied as stated in
1040    /// [`Self::lookup_ipv4_ipv6`]. The result of the first successful call is returned, or a
1041    /// summary of all errors otherwise.
1042    pub async fn lookup_ipv4_ipv6_staggered(
1043        &self,
1044        host: impl ToString,
1045        timeout: Duration,
1046        delays_ms: &[u64],
1047    ) -> Result<impl Iterator<Item = IpAddr>, StaggeredError<DnsError>> {
1048        let host = host.to_string();
1049        let f = || self.lookup_ipv4_ipv6(host.clone(), timeout);
1050        stagger_call(f, delays_ms).await
1051    }
1052
1053    /// Looks up endpoint info by [`EndpointId`] and origin domain name.
1054    ///
1055    /// To lookup endpoints that published their endpoint info to the DNS servers run by n0,
1056    /// pass [`N0_DNS_ENDPOINT_ORIGIN_PROD`] as `origin`.
1057    pub async fn lookup_endpoint_by_id(
1058        &self,
1059        endpoint_id: &EndpointId,
1060        origin: &str,
1061    ) -> Result<EndpointInfo, LookupError> {
1062        let name = format!("_iroh.{}.{}", endpoint_id.to_z32(), origin);
1063        let lookup = self.lookup_txt(name.clone(), DNS_TIMEOUT).await?;
1064        let info = EndpointInfo::from_txt_lookup(name, lookup)?;
1065        Ok(info)
1066    }
1067
1068    /// Looks up endpoint info by DNS name.
1069    pub async fn lookup_endpoint_by_domain_name(
1070        &self,
1071        name: &str,
1072    ) -> Result<EndpointInfo, LookupError> {
1073        let name = if name.starts_with("_iroh.") {
1074            name.to_string()
1075        } else {
1076            format!("_iroh.{name}")
1077        };
1078        let lookup = self.lookup_txt(name.clone(), DNS_TIMEOUT).await?;
1079        let info = EndpointInfo::from_txt_lookup(name, lookup)?;
1080        Ok(info)
1081    }
1082
1083    /// Looks up endpoint info by DNS name in a staggered fashion.
1084    ///
1085    /// From the moment this function is called, each lookup is scheduled after the delays in
1086    /// `delays_ms` with the first call being done immediately. `[200ms, 300ms]` results in calls
1087    /// at T+0ms, T+200ms and T+300ms. The result of the first successful call is returned, or a
1088    /// summary of all errors otherwise.
1089    pub async fn lookup_endpoint_by_domain_name_staggered(
1090        &self,
1091        name: &str,
1092        delays_ms: &[u64],
1093    ) -> Result<EndpointInfo, StaggeredError<LookupError>> {
1094        let f = || self.lookup_endpoint_by_domain_name(name);
1095        stagger_call(f, delays_ms).await
1096    }
1097
1098    /// Looks up endpoint info by [`EndpointId`] and origin domain name.
1099    ///
1100    /// From the moment this function is called, each lookup is scheduled after the delays in
1101    /// `delays_ms` with the first call being done immediately. `[200ms, 300ms]` results in calls
1102    /// at T+0ms, T+200ms and T+300ms. The result of the first successful call is returned, or a
1103    /// summary of all errors otherwise.
1104    pub async fn lookup_endpoint_by_id_staggered(
1105        &self,
1106        endpoint_id: &EndpointId,
1107        origin: &str,
1108        delays_ms: &[u64],
1109    ) -> Result<EndpointInfo, StaggeredError<LookupError>> {
1110        let f = || self.lookup_endpoint_by_id(endpoint_id, origin);
1111        stagger_call(f, delays_ms).await
1112    }
1113}
1114
1115impl Default for DnsResolver {
1116    fn default() -> Self {
1117        Self::new()
1118    }
1119}
1120
1121/// Record data for a TXT record.
1122///
1123/// This contains a list of character strings, as defined in [RFC 1035 Section 3.3.14].
1124///
1125/// [`TxtRecordData`] implements [`fmt::Display`], so you can call [`ToString::to_string`] to
1126/// convert the record data into a string. This will parse each character string with
1127/// [`String::from_utf8_lossy`] and then concatenate all strings without a separator.
1128///
1129/// If you want to process each character string individually, use [`Self::iter`].
1130///
1131/// [RFC 1035 Section 3.3.14]: https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14
1132#[derive(Debug, Clone)]
1133pub struct TxtRecordData(Box<[Box<[u8]>]>);
1134
1135impl TxtRecordData {
1136    /// Returns an iterator over the character strings contained in this TXT record.
1137    pub fn iter(&self) -> impl Iterator<Item = &[u8]> {
1138        self.0.iter().map(|x| x.as_ref())
1139    }
1140}
1141
1142impl fmt::Display for TxtRecordData {
1143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1144        for s in self.iter() {
1145            write!(f, "{}", String::from_utf8_lossy(s))?
1146        }
1147        Ok(())
1148    }
1149}
1150
1151impl FromIterator<Box<[u8]>> for TxtRecordData {
1152    fn from_iter<T: IntoIterator<Item = Box<[u8]>>>(iter: T) -> Self {
1153        Self(iter.into_iter().collect())
1154    }
1155}
1156
1157impl From<Vec<Box<[u8]>>> for TxtRecordData {
1158    fn from(value: Vec<Box<[u8]>>) -> Self {
1159        Self(value.into_boxed_slice())
1160    }
1161}
1162
1163impl From<Vec<String>> for TxtRecordData {
1164    fn from(value: Vec<String>) -> Self {
1165        Self(
1166            value
1167                .into_iter()
1168                .map(|s| s.into_bytes().into_boxed_slice())
1169                .collect(),
1170        )
1171    }
1172}
1173
1174/// Helper enum to give a unified type to the iterators of [`DnsResolver::lookup_ipv4_ipv6`].
1175enum LookupIter<A, B> {
1176    Ipv4(A),
1177    Ipv6(B),
1178    Both(std::iter::Chain<A, B>),
1179}
1180
1181impl<A: Iterator<Item = IpAddr>, B: Iterator<Item = IpAddr>> Iterator for LookupIter<A, B> {
1182    type Item = IpAddr;
1183
1184    fn next(&mut self) -> Option<Self::Item> {
1185        match self {
1186            LookupIter::Ipv4(iter) => iter.next(),
1187            LookupIter::Ipv6(iter) => iter.next(),
1188            LookupIter::Both(iter) => iter.next(),
1189        }
1190    }
1191}
1192
1193/// Staggers calls to the future F with the given delays.
1194///
1195/// The first call is performed immediately. The first call to succeed generates an Ok result
1196/// ignoring any previous error. If all calls fail, an error summarizing all errors is returned.
1197async fn stagger_call<
1198    T,
1199    E: StackError + 'static,
1200    F: Fn() -> Fut,
1201    Fut: Future<Output = Result<T, E>>,
1202>(
1203    f: F,
1204    delays_ms: &[u64],
1205) -> Result<T, StaggeredError<E>> {
1206    let mut calls = n0_future::FuturesUnorderedBounded::new(delays_ms.len() + 1);
1207    // NOTE: we add the 0 delay here to have a uniform set of futures. This is more performant than
1208    // using alternatives that allow futures of different types.
1209    for delay in std::iter::once(&0u64).chain(delays_ms) {
1210        let delay = add_jitter(delay);
1211        let fut = f();
1212        let staggered_fut = async move {
1213            time::sleep(delay).await;
1214            fut.await
1215        };
1216        calls.push(staggered_fut)
1217    }
1218
1219    let mut errors = vec![];
1220    while let Some(call_result) = calls.next().await {
1221        match call_result {
1222            Ok(t) => return Ok(t),
1223            Err(e) => errors.push(e),
1224        }
1225    }
1226
1227    Err(e!(StaggeredError { errors }))
1228}
1229
1230fn add_jitter(delay: &u64) -> Duration {
1231    // If delay is 0, return 0 immediately.
1232    if *delay == 0 {
1233        return Duration::ZERO;
1234    }
1235
1236    // Calculate jitter as a random value in the range of +/- MAX_JITTER_PERCENT of the delay.
1237    let max_jitter = delay.saturating_mul(MAX_JITTER_PERCENT * 2) / 100;
1238    let jitter = rand::random::<u64>() % max_jitter;
1239
1240    Duration::from_millis(delay.saturating_sub(max_jitter / 2).saturating_add(jitter))
1241}
1242
1243#[cfg(test)]
1244pub(crate) mod tests {
1245    use std::sync::atomic::AtomicUsize;
1246
1247    use n0_tracing_test::traced_test;
1248
1249    use super::*;
1250
1251    #[test]
1252    fn builder_named_nameservers_carry_server_name() {
1253        let addr = SocketAddr::new(std::net::Ipv4Addr::new(1, 1, 1, 1).into(), 443);
1254        let builder = Builder::default()
1255            .add_nameserver_config(
1256                NameserverConfig::https(addr.ip()).with_tls_server_name("cloudflare-dns.com"),
1257            )
1258            .add_nameserver_config(
1259                NameserverConfig::tls(addr.ip())
1260                    .with_port(addr.port())
1261                    .with_tls_server_name("cloudflare-dns.com"),
1262            )
1263            .fallback_nameserver_configs([
1264                NameserverConfig::https(addr.ip()).with_tls_server_name("fallback.example.com")
1265            ]);
1266        let ns = &builder.nameservers;
1267        assert_eq!(ns[0].protocol, DnsProtocol::Https);
1268        assert_eq!(ns[0].addr.port(), 443);
1269        assert_eq!(ns[0].server_name.as_deref(), Some("cloudflare-dns.com"));
1270        assert_eq!(ns[1].protocol, DnsProtocol::Tls);
1271        assert_eq!(ns[1].addr.port(), 443);
1272        assert_eq!(ns[1].server_name.as_deref(), Some("cloudflare-dns.com"));
1273        assert_eq!(
1274            builder.fallback_nameservers[0].server_name.as_deref(),
1275            Some("fallback.example.com")
1276        );
1277    }
1278
1279    /// Collects the nameserver addresses the resolver crate ends up configured
1280    /// with, for a builder that reads nothing from the host.
1281    fn resolver_nameservers(builder: Builder) -> Vec<SocketAddr> {
1282        builder
1283            .into_resolver_builder()
1284            .build()
1285            .configured_nameservers()
1286            .iter()
1287            .map(|ns| ns.addr())
1288            .collect()
1289    }
1290
1291    /// An empty fallback list means the default public resolvers, not none.
1292    ///
1293    /// The resolver crate's fallback tier starts empty, so this mapping is ours
1294    /// to keep: leaving the list unset here has to opt into its defaults.
1295    #[test]
1296    fn empty_fallback_list_uses_public_resolvers() {
1297        let addrs = resolver_nameservers(Builder::default());
1298        assert!(!addrs.is_empty());
1299        assert!(addrs.iter().any(|addr| addr.ip() == CLOUDFLARE_IP));
1300    }
1301
1302    /// `disable_fallback` leaves the resolver with no nameservers at all.
1303    #[test]
1304    fn disable_fallback_drops_the_public_resolvers() {
1305        let addrs = resolver_nameservers(Builder::default().disable_fallback());
1306        assert!(addrs.is_empty(), "{addrs:?}");
1307    }
1308
1309    /// An explicit fallback list replaces the defaults rather than adding to them.
1310    #[test]
1311    fn explicit_fallback_list_replaces_the_defaults() {
1312        let custom = SocketAddr::new(std::net::Ipv4Addr::new(192, 0, 2, 1).into(), 53);
1313        let addrs = resolver_nameservers(
1314            Builder::default().fallback_nameserver_configs([NameserverConfig::udp(custom.ip())]),
1315        );
1316        assert_eq!(addrs, vec![custom]);
1317    }
1318
1319    const CLOUDFLARE_IP: IpAddr = IpAddr::V4(std::net::Ipv4Addr::new(1, 1, 1, 1));
1320
1321    #[tokio::test]
1322    #[traced_test]
1323    async fn stagger_basic() {
1324        const CALL_RESULTS: &[Result<u8, u8>] = &[Err(2), Ok(3), Ok(5), Ok(7)];
1325        static DONE_CALL: AtomicUsize = AtomicUsize::new(0);
1326        let f = || {
1327            let r_pos = DONE_CALL.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1328            async move {
1329                tracing::info!(r_pos, "call");
1330                CALL_RESULTS[r_pos].map_err(|_| e!(DnsError::InvalidResponse))
1331            }
1332        };
1333
1334        let delays = [1000, 15];
1335        let result = stagger_call(f, &delays).await.unwrap();
1336        assert_eq!(result, 5)
1337    }
1338
1339    #[test]
1340    #[traced_test]
1341    fn jitter_test_zero() {
1342        let jittered_delay = add_jitter(&0);
1343        assert_eq!(jittered_delay, Duration::from_secs(0));
1344    }
1345
1346    //Sanity checks that I did the math right
1347    #[test]
1348    #[traced_test]
1349    fn jitter_test_nonzero_lower_bound() {
1350        let delay: u64 = 300;
1351        for _ in 0..100 {
1352            assert!(add_jitter(&delay) >= Duration::from_millis(delay * 8 / 10));
1353        }
1354    }
1355
1356    #[test]
1357    #[traced_test]
1358    fn jitter_test_nonzero_upper_bound() {
1359        let delay: u64 = 300;
1360        for _ in 0..100 {
1361            assert!(add_jitter(&delay) < Duration::from_millis(delay * 12 / 10));
1362        }
1363    }
1364
1365    #[tokio::test]
1366    #[traced_test]
1367    async fn custom_resolver() {
1368        #[derive(Debug)]
1369        struct MyResolver;
1370        impl Resolver for MyResolver {
1371            fn lookup_ipv4(&self, host: String) -> BoxFuture<Result<BoxIter<Ipv4Addr>, DnsError>> {
1372                Box::pin(async move {
1373                    let addr = if host == "foo.example" {
1374                        Ipv4Addr::new(1, 1, 1, 1)
1375                    } else {
1376                        return Err(e!(DnsError::NoResponse));
1377                    };
1378                    let iter: BoxIter<Ipv4Addr> = Box::new(vec![addr].into_iter());
1379                    Ok(iter)
1380                })
1381            }
1382
1383            fn lookup_ipv6(&self, _host: String) -> BoxFuture<Result<BoxIter<Ipv6Addr>, DnsError>> {
1384                todo!()
1385            }
1386
1387            fn lookup_txt(
1388                &self,
1389                _host: String,
1390            ) -> BoxFuture<Result<BoxIter<TxtRecordData>, DnsError>> {
1391                todo!()
1392            }
1393
1394            fn clear_cache(&self) {
1395                todo!()
1396            }
1397
1398            fn reset(&self) -> Box<dyn Resolver> {
1399                todo!()
1400            }
1401        }
1402
1403        let resolver = DnsResolver::custom(MyResolver);
1404        let mut iter = resolver
1405            .lookup_ipv4("foo.example", Duration::from_secs(1))
1406            .await
1407            .expect("not to fail");
1408        let addr = iter.next().expect("one result");
1409        assert_eq!(addr, "1.1.1.1".parse::<IpAddr>().unwrap());
1410
1411        let res = resolver
1412            .lookup_ipv4("bar.example", Duration::from_secs(1))
1413            .await;
1414        assert!(matches!(res, Err(DnsError::NoResponse { .. })))
1415    }
1416}