iroh_relay/
dns.rs

1//! DNS resolver
2
3use std::{
4    fmt,
5    future::Future,
6    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
7    sync::Arc,
8};
9
10use hickory_resolver::{
11    TokioResolver,
12    config::{ResolverConfig, ResolverOpts},
13    name_server::TokioConnectionProvider,
14};
15use iroh_base::NodeId;
16use n0_future::{
17    StreamExt,
18    boxed::BoxFuture,
19    time::{self, Duration},
20};
21use nested_enum_utils::common_fields;
22use snafu::{Backtrace, GenerateImplicitData, OptionExt, ResultExt, Snafu};
23use tokio::sync::RwLock;
24use tracing::debug;
25use url::Url;
26
27use crate::{
28    defaults::timeouts::DNS_TIMEOUT,
29    node_info::{self, NodeInfo, ParseError},
30};
31
32/// The n0 testing DNS node origin, for production.
33pub const N0_DNS_NODE_ORIGIN_PROD: &str = "dns.iroh.link";
34/// The n0 testing DNS node origin, for testing.
35pub const N0_DNS_NODE_ORIGIN_STAGING: &str = "staging-dns.iroh.link";
36
37/// Percent of total delay to jitter. 20 means +/- 20% of delay.
38const MAX_JITTER_PERCENT: u64 = 20;
39
40/// Trait for DNS resolvers used in iroh.
41pub trait Resolver: fmt::Debug + Send + Sync + 'static {
42    /// Looks up an IPv4 address.
43    fn lookup_ipv4(&self, host: String) -> BoxFuture<Result<BoxIter<Ipv4Addr>, DnsError>>;
44
45    /// Looks up an IPv6 address.
46    fn lookup_ipv6(&self, host: String) -> BoxFuture<Result<BoxIter<Ipv6Addr>, DnsError>>;
47
48    /// Looks up TXT records.
49    fn lookup_txt(&self, host: String) -> BoxFuture<Result<BoxIter<TxtRecordData>, DnsError>>;
50
51    /// Clears the internal cache.
52    fn clear_cache(&self);
53
54    /// Completely resets the DNS resolver.
55    ///
56    /// This is called when the host's network changes majorly. Implementations should rebind all sockets
57    /// and refresh the nameserver configuration if read from the host system.
58    fn reset(&mut self);
59}
60
61/// Boxed iterator alias.
62pub type BoxIter<T> = Box<dyn Iterator<Item = T> + Send + 'static>;
63
64/// Potential errors related to dns.
65#[common_fields({
66    backtrace: Option<Backtrace>,
67    #[snafu(implicit)]
68    span_trace: n0_snafu::SpanTrace,
69})]
70#[allow(missing_docs)]
71#[derive(Debug, Snafu)]
72#[non_exhaustive]
73#[snafu(visibility(pub(crate)))]
74pub enum DnsError {
75    #[snafu(transparent)]
76    Timeout { source: tokio::time::error::Elapsed },
77    #[snafu(display("No response"))]
78    NoResponse {},
79    #[snafu(display("Resolve failed ipv4: {ipv4}, ipv6 {ipv6}"))]
80    ResolveBoth {
81        ipv4: Box<DnsError>,
82        ipv6: Box<DnsError>,
83    },
84    #[snafu(display("missing host"))]
85    MissingHost {},
86    #[snafu(transparent)]
87    Resolve {
88        source: hickory_resolver::ResolveError,
89    },
90    #[snafu(display("invalid DNS response: not a query for _iroh.z32encodedpubkey"))]
91    InvalidResponse {},
92}
93
94#[cfg(not(wasm_browser))]
95#[common_fields({
96    backtrace: Option<Backtrace>,
97    #[snafu(implicit)]
98    span_trace: n0_snafu::SpanTrace,
99})]
100#[allow(missing_docs)]
101#[derive(Debug, Snafu)]
102#[non_exhaustive]
103#[snafu(visibility(pub(crate)))]
104pub enum LookupError {
105    #[snafu(display("Malformed txt from lookup"))]
106    ParseError {
107        #[snafu(source(from(ParseError, Box::new)))]
108        source: Box<ParseError>,
109    },
110    #[snafu(display("Failed to resolve TXT record"))]
111    LookupFailed {
112        #[snafu(source(from(DnsError, Box::new)))]
113        source: Box<DnsError>,
114    },
115}
116
117/// Error returned when an input value is too long for [`crate::node_info::UserData`].
118#[allow(missing_docs)]
119#[derive(Debug, Snafu)]
120#[snafu(module)]
121#[snafu(display("no calls succeeded: [{}]", errors.iter().map(|e| e.to_string()).collect::<Vec<_>>().join("")))]
122pub struct StaggeredError<E: std::fmt::Debug + std::fmt::Display> {
123    backtrace: Option<Backtrace>,
124    #[snafu(implicit)]
125    span_trace: n0_snafu::SpanTrace,
126    errors: Vec<E>,
127}
128
129impl<E: std::fmt::Debug + std::fmt::Display> StaggeredError<E> {
130    pub(crate) fn new(errors: Vec<E>) -> Self {
131        Self {
132            errors,
133            backtrace: GenerateImplicitData::generate(),
134            span_trace: n0_snafu::SpanTrace::generate(),
135        }
136    }
137}
138
139/// Builder for [`DnsResolver`].
140#[derive(Debug, Clone, Default)]
141pub struct Builder {
142    use_system_defaults: bool,
143    nameservers: Vec<(SocketAddr, DnsProtocol)>,
144}
145
146/// Protocols over which DNS records can be resolved.
147#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
148#[non_exhaustive]
149pub enum DnsProtocol {
150    /// DNS over UDP
151    ///
152    /// This is the classic DNS protocol and supported by most DNS servers.
153    #[default]
154    Udp,
155    /// DNS over TCP
156    ///
157    /// This is specified in the original DNS RFCs, but is not supported by all DNS servers.
158    Tcp,
159    /// DNS over TLS
160    ///
161    /// Performs DNS lookups over TLS-encrypted TCP connections, as defined in [RFC 7858].
162    ///
163    /// [RFC 7858]: https://www.rfc-editor.org/rfc/rfc7858.html
164    Tls,
165    /// DNS over HTTPS
166    ///
167    /// Performs DNS lookups over HTTPS, as defined in [RFC 8484].
168    ///
169    /// [RFC 8484]: https://www.rfc-editor.org/rfc/rfc8484.html
170    Https,
171}
172
173impl DnsProtocol {
174    fn to_hickory(self) -> hickory_resolver::proto::xfer::Protocol {
175        use hickory_resolver::proto::xfer::Protocol;
176        match self {
177            DnsProtocol::Udp => Protocol::Udp,
178            DnsProtocol::Tcp => Protocol::Tcp,
179            DnsProtocol::Tls => Protocol::Tls,
180            DnsProtocol::Https => Protocol::Https,
181        }
182    }
183}
184
185impl Builder {
186    /// Makes the builder respect the host system's DNS configuration.
187    ///
188    /// We first try to read the system's resolver from `/etc/resolv.conf`.
189    /// This does not work at least on some Androids, therefore we fallback
190    /// to a default config which uses Google's `8.8.8.8` or `8.8.4.4`.
191    pub fn with_system_defaults(mut self) -> Self {
192        self.use_system_defaults = true;
193        self
194    }
195
196    /// Adds a single nameserver.
197    pub fn with_nameserver(mut self, addr: SocketAddr, protocol: DnsProtocol) -> Self {
198        self.nameservers.push((addr, protocol));
199        self
200    }
201
202    /// Adds a list of nameservers.
203    pub fn with_nameservers(
204        mut self,
205        nameservers: impl IntoIterator<Item = (SocketAddr, DnsProtocol)>,
206    ) -> Self {
207        self.nameservers.extend(nameservers);
208        self
209    }
210
211    /// Builds the DNS resolver.
212    pub fn build(self) -> DnsResolver {
213        let resolver = HickoryResolver::new(self);
214        DnsResolver(DnsResolverInner::Hickory(Arc::new(RwLock::new(resolver))))
215    }
216}
217
218/// The DNS resolver used throughout `iroh`.
219#[derive(Debug, Clone)]
220pub struct DnsResolver(DnsResolverInner);
221
222impl DnsResolver {
223    /// Creates a new DNS resolver with sensible cross-platform defaults.
224    ///
225    /// We first try to read the system's resolver from `/etc/resolv.conf`.
226    /// This does not work at least on some Androids, therefore we fallback
227    /// to the default `ResolverConfig` which uses eg. to google's `8.8.8.8` or `8.8.4.4`.
228    pub fn new() -> Self {
229        Builder::default().with_system_defaults().build()
230    }
231
232    /// Creates a new DNS resolver configured with a single UDP DNS nameserver.
233    pub fn with_nameserver(nameserver: SocketAddr) -> Self {
234        Builder::default()
235            .with_nameserver(nameserver, DnsProtocol::Udp)
236            .build()
237    }
238
239    /// Creates a builder to construct a DNS resolver with custom options.
240    pub fn builder() -> Builder {
241        Builder::default()
242    }
243
244    /// Creates a new [`DnsResolver`] from a struct that implements [`Resolver`].
245    ///
246    /// If you need more customization for DNS resolving than the [`Builder`] allows, you can
247    /// implement the [`Resolver`] trait on a struct and implement DNS resolution
248    /// however you see fit.
249    pub fn custom(resolver: impl Resolver) -> Self {
250        Self(DnsResolverInner::Custom(Arc::new(RwLock::new(resolver))))
251    }
252
253    /// Removes all entries from the cache.
254    pub async fn clear_cache(&self) {
255        self.0.clear_cache().await
256    }
257
258    /// Recreates the inner resolver.
259    pub async fn reset(&self) {
260        self.0.reset().await
261    }
262
263    /// Looks up a TXT record.
264    pub async fn lookup_txt<T: ToString>(
265        &self,
266        host: T,
267        timeout: Duration,
268    ) -> Result<impl Iterator<Item = TxtRecordData>, DnsError> {
269        let host = host.to_string();
270        let res = time::timeout(timeout, self.0.lookup_txt(host)).await??;
271        Ok(res)
272    }
273
274    /// Performs an IPv4 lookup with a timeout.
275    pub async fn lookup_ipv4<T: ToString>(
276        &self,
277        host: T,
278        timeout: Duration,
279    ) -> Result<impl Iterator<Item = IpAddr> + use<T>, DnsError> {
280        let host = host.to_string();
281        let addrs = time::timeout(timeout, self.0.lookup_ipv4(host)).await??;
282        Ok(addrs.into_iter().map(IpAddr::V4))
283    }
284
285    /// Performs an IPv6 lookup with a timeout.
286    pub async fn lookup_ipv6<T: ToString>(
287        &self,
288        host: T,
289        timeout: Duration,
290    ) -> Result<impl Iterator<Item = IpAddr> + use<T>, DnsError> {
291        let host = host.to_string();
292        let addrs = time::timeout(timeout, self.0.lookup_ipv6(host)).await??;
293        Ok(addrs.into_iter().map(IpAddr::V6))
294    }
295
296    /// Resolves IPv4 and IPv6 in parallel with a timeout.
297    ///
298    /// `LookupIpStrategy::Ipv4AndIpv6` will wait for ipv6 resolution timeout, even if it is
299    /// not usable on the stack, so we manually query both lookups concurrently and time them out
300    /// individually.
301    pub async fn lookup_ipv4_ipv6<T: ToString>(
302        &self,
303        host: T,
304        timeout: Duration,
305    ) -> Result<impl Iterator<Item = IpAddr> + use<T>, DnsError> {
306        let host = host.to_string();
307        let res = tokio::join!(
308            self.lookup_ipv4(host.clone(), timeout),
309            self.lookup_ipv6(host, timeout)
310        );
311
312        match res {
313            (Ok(ipv4), Ok(ipv6)) => Ok(LookupIter::Both(ipv4.chain(ipv6))),
314            (Ok(ipv4), Err(_)) => Ok(LookupIter::Ipv4(ipv4)),
315            (Err(_), Ok(ipv6)) => Ok(LookupIter::Ipv6(ipv6)),
316            (Err(ipv4_err), Err(ipv6_err)) => Err(ResolveBothSnafu {
317                ipv4: Box::new(ipv4_err),
318                ipv6: Box::new(ipv6_err),
319            }
320            .build()),
321        }
322    }
323
324    /// Resolves a hostname from a URL to an IP address.
325    pub async fn resolve_host(
326        &self,
327        url: &Url,
328        prefer_ipv6: bool,
329        timeout: Duration,
330    ) -> Result<IpAddr, DnsError> {
331        let host = url.host().context(MissingHostSnafu)?;
332        match host {
333            url::Host::Domain(domain) => {
334                // Need to do a DNS lookup
335                let lookup = tokio::join!(
336                    self.lookup_ipv4(domain, timeout),
337                    self.lookup_ipv6(domain, timeout)
338                );
339                let (v4, v6) = match lookup {
340                    (Err(ipv4_err), Err(ipv6_err)) => {
341                        return Err(ResolveBothSnafu {
342                            ipv4: Box::new(ipv4_err),
343                            ipv6: Box::new(ipv6_err),
344                        }
345                        .build());
346                    }
347                    (Err(_), Ok(mut v6)) => (None, v6.next()),
348                    (Ok(mut v4), Err(_)) => (v4.next(), None),
349                    (Ok(mut v4), Ok(mut v6)) => (v4.next(), v6.next()),
350                };
351                if prefer_ipv6 {
352                    v6.or(v4).context(NoResponseSnafu)
353                } else {
354                    v4.or(v6).context(NoResponseSnafu)
355                }
356            }
357            url::Host::Ipv4(ip) => Ok(IpAddr::V4(ip)),
358            url::Host::Ipv6(ip) => Ok(IpAddr::V6(ip)),
359        }
360    }
361
362    /// Performs an IPv4 lookup with a timeout in a staggered fashion.
363    ///
364    /// From the moment this function is called, each lookup is scheduled after the delays in
365    /// `delays_ms` with the first call being done immediately. `[200ms, 300ms]` results in calls
366    /// at T+0ms, T+200ms and T+300ms. The `timeout` is applied to each call individually. The
367    /// result of the first successful call is returned, or a summary of all errors otherwise.
368    pub async fn lookup_ipv4_staggered(
369        &self,
370        host: impl ToString,
371        timeout: Duration,
372        delays_ms: &[u64],
373    ) -> Result<impl Iterator<Item = IpAddr>, StaggeredError<DnsError>> {
374        let host = host.to_string();
375        let f = || self.lookup_ipv4(host.clone(), timeout);
376        stagger_call(f, delays_ms).await
377    }
378
379    /// Performs an IPv6 lookup with a timeout in a staggered fashion.
380    ///
381    /// From the moment this function is called, each lookup is scheduled after the delays in
382    /// `delays_ms` with the first call being done immediately. `[200ms, 300ms]` results in calls
383    /// at T+0ms, T+200ms and T+300ms. The `timeout` is applied to each call individually. The
384    /// result of the first successful call is returned, or a summary of all errors otherwise.
385    pub async fn lookup_ipv6_staggered(
386        &self,
387        host: impl ToString,
388        timeout: Duration,
389        delays_ms: &[u64],
390    ) -> Result<impl Iterator<Item = IpAddr>, StaggeredError<DnsError>> {
391        let host = host.to_string();
392        let f = || self.lookup_ipv6(host.clone(), timeout);
393        stagger_call(f, delays_ms).await
394    }
395
396    /// Races an IPv4 and IPv6 lookup with a timeout in a staggered fashion.
397    ///
398    /// From the moment this function is called, each lookup is scheduled after the delays in
399    /// `delays_ms` with the first call being done immediately. `[200ms, 300ms]` results in calls
400    /// at T+0ms, T+200ms and T+300ms. The `timeout` is applied as stated in
401    /// [`Self::lookup_ipv4_ipv6`]. The result of the first successful call is returned, or a
402    /// summary of all errors otherwise.
403    pub async fn lookup_ipv4_ipv6_staggered(
404        &self,
405        host: impl ToString,
406        timeout: Duration,
407        delays_ms: &[u64],
408    ) -> Result<impl Iterator<Item = IpAddr>, StaggeredError<DnsError>> {
409        let host = host.to_string();
410        let f = || self.lookup_ipv4_ipv6(host.clone(), timeout);
411        stagger_call(f, delays_ms).await
412    }
413
414    /// Looks up node info by [`NodeId`] and origin domain name.
415    ///
416    /// To lookup nodes that published their node info to the DNS servers run by n0,
417    /// pass [`N0_DNS_NODE_ORIGIN_PROD`] as `origin`.
418    pub async fn lookup_node_by_id(
419        &self,
420        node_id: &NodeId,
421        origin: &str,
422    ) -> Result<NodeInfo, LookupError> {
423        let name = node_info::node_domain(node_id, origin);
424        let name = node_info::ensure_iroh_txt_label(name);
425        let lookup = self
426            .lookup_txt(name.clone(), DNS_TIMEOUT)
427            .await
428            .context(LookupFailedSnafu)?;
429        let info = NodeInfo::from_txt_lookup(name, lookup).context(ParseSnafu)?;
430        Ok(info)
431    }
432
433    /// Looks up node info by DNS name.
434    pub async fn lookup_node_by_domain_name(&self, name: &str) -> Result<NodeInfo, LookupError> {
435        let name = node_info::ensure_iroh_txt_label(name.to_string());
436        let lookup = self
437            .lookup_txt(name.clone(), DNS_TIMEOUT)
438            .await
439            .context(LookupFailedSnafu)?;
440        let info = NodeInfo::from_txt_lookup(name, lookup).context(ParseSnafu)?;
441        Ok(info)
442    }
443
444    /// Looks up node info by DNS name in a staggered fashion.
445    ///
446    /// From the moment this function is called, each lookup is scheduled after the delays in
447    /// `delays_ms` with the first call being done immediately. `[200ms, 300ms]` results in calls
448    /// at T+0ms, T+200ms and T+300ms. The result of the first successful call is returned, or a
449    /// summary of all errors otherwise.
450    pub async fn lookup_node_by_domain_name_staggered(
451        &self,
452        name: &str,
453        delays_ms: &[u64],
454    ) -> Result<NodeInfo, StaggeredError<LookupError>> {
455        let f = || self.lookup_node_by_domain_name(name);
456        stagger_call(f, delays_ms).await
457    }
458
459    /// Looks up node info by [`NodeId`] and origin domain name.
460    ///
461    /// From the moment this function is called, each lookup is scheduled after the delays in
462    /// `delays_ms` with the first call being done immediately. `[200ms, 300ms]` results in calls
463    /// at T+0ms, T+200ms and T+300ms. The result of the first successful call is returned, or a
464    /// summary of all errors otherwise.
465    pub async fn lookup_node_by_id_staggered(
466        &self,
467        node_id: &NodeId,
468        origin: &str,
469        delays_ms: &[u64],
470    ) -> Result<NodeInfo, StaggeredError<LookupError>> {
471        let f = || self.lookup_node_by_id(node_id, origin);
472        stagger_call(f, delays_ms).await
473    }
474}
475
476impl Default for DnsResolver {
477    fn default() -> Self {
478        Self::new()
479    }
480}
481
482impl reqwest::dns::Resolve for DnsResolver {
483    fn resolve(&self, name: reqwest::dns::Name) -> reqwest::dns::Resolving {
484        let this = self.clone();
485        let name = name.as_str().to_string();
486        Box::pin(async move {
487            let res = this.lookup_ipv4_ipv6(name, DNS_TIMEOUT).await;
488            match res {
489                Ok(addrs) => {
490                    let addrs: reqwest::dns::Addrs =
491                        Box::new(addrs.map(|addr| SocketAddr::new(addr, 0)));
492                    Ok(addrs)
493                }
494                Err(err) => {
495                    let err: Box<dyn std::error::Error + Send + Sync> = Box::new(err);
496                    Err(err)
497                }
498            }
499        })
500    }
501}
502
503/// Wrapper enum that contains either a hickory resolver or a custom resolver.
504///
505/// We do this to save the cost of boxing the futures and iterators when using
506/// default hickory resolver.
507#[derive(Debug, Clone)]
508enum DnsResolverInner {
509    Hickory(Arc<RwLock<HickoryResolver>>),
510    Custom(Arc<RwLock<dyn Resolver>>),
511}
512
513impl DnsResolverInner {
514    async fn lookup_ipv4(
515        &self,
516        host: String,
517    ) -> Result<impl Iterator<Item = Ipv4Addr> + use<>, DnsError> {
518        Ok(match self {
519            Self::Hickory(resolver) => Either::Left(resolver.read().await.lookup_ipv4(host).await?),
520            Self::Custom(resolver) => Either::Right(resolver.read().await.lookup_ipv4(host).await?),
521        })
522    }
523
524    async fn lookup_ipv6(
525        &self,
526        host: String,
527    ) -> Result<impl Iterator<Item = Ipv6Addr> + use<>, DnsError> {
528        Ok(match self {
529            Self::Hickory(resolver) => Either::Left(resolver.read().await.lookup_ipv6(host).await?),
530            Self::Custom(resolver) => Either::Right(resolver.read().await.lookup_ipv6(host).await?),
531        })
532    }
533
534    async fn lookup_txt(
535        &self,
536        host: String,
537    ) -> Result<impl Iterator<Item = TxtRecordData> + use<>, DnsError> {
538        Ok(match self {
539            Self::Hickory(resolver) => Either::Left(resolver.read().await.lookup_txt(host).await?),
540            Self::Custom(resolver) => Either::Right(resolver.read().await.lookup_txt(host).await?),
541        })
542    }
543
544    async fn clear_cache(&self) {
545        match self {
546            Self::Hickory(resolver) => resolver.read().await.clear_cache(),
547            Self::Custom(resolver) => resolver.read().await.clear_cache(),
548        }
549    }
550
551    async fn reset(&self) {
552        match self {
553            Self::Hickory(resolver) => resolver.write().await.reset(),
554            Self::Custom(resolver) => resolver.write().await.reset(),
555        }
556    }
557}
558
559#[derive(Debug)]
560struct HickoryResolver {
561    resolver: TokioResolver,
562    builder: Builder,
563}
564
565impl HickoryResolver {
566    fn new(builder: Builder) -> Self {
567        let resolver = Self::build_resolver(&builder);
568        Self { resolver, builder }
569    }
570
571    fn build_resolver(builder: &Builder) -> TokioResolver {
572        let (mut config, mut options) = if builder.use_system_defaults {
573            match Self::system_config() {
574                Ok((config, options)) => (config, options),
575                Err(error) => {
576                    debug!(%error, "Failed to read the system's DNS config, using fallback DNS servers.");
577                    (ResolverConfig::google(), ResolverOpts::default())
578                }
579            }
580        } else {
581            (ResolverConfig::new(), ResolverOpts::default())
582        };
583
584        for (addr, proto) in builder.nameservers.iter() {
585            let nameserver =
586                hickory_resolver::config::NameServerConfig::new(*addr, proto.to_hickory());
587            config.add_name_server(nameserver);
588        }
589
590        // see [`DnsResolver::lookup_ipv4_ipv6`] for info on why we avoid `LookupIpStrategy::Ipv4AndIpv6`
591        options.ip_strategy = hickory_resolver::config::LookupIpStrategy::Ipv4thenIpv6;
592
593        let mut hickory_builder =
594            TokioResolver::builder_with_config(config, TokioConnectionProvider::default());
595        *hickory_builder.options_mut() = options;
596        hickory_builder.build()
597    }
598
599    fn system_config() -> Result<(ResolverConfig, ResolverOpts), hickory_resolver::ResolveError> {
600        let (system_config, options) = hickory_resolver::system_conf::read_system_conf()?;
601
602        // Copy all of the system config, but strip the bad windows nameservers.  Unfortunately
603        // there is no easy way to do this.
604        let mut config = hickory_resolver::config::ResolverConfig::new();
605        if let Some(name) = system_config.domain() {
606            config.set_domain(name.clone());
607        }
608        for name in system_config.search() {
609            config.add_search(name.clone());
610        }
611        for nameserver_cfg in system_config.name_servers() {
612            if !WINDOWS_BAD_SITE_LOCAL_DNS_SERVERS.contains(&nameserver_cfg.socket_addr.ip()) {
613                config.add_name_server(nameserver_cfg.clone());
614            }
615        }
616        Ok((config, options))
617    }
618
619    async fn lookup_ipv4(
620        &self,
621        host: String,
622    ) -> Result<impl Iterator<Item = Ipv4Addr> + use<>, DnsError> {
623        Ok(self
624            .resolver
625            .ipv4_lookup(host)
626            .await?
627            .into_iter()
628            .map(Ipv4Addr::from))
629    }
630
631    /// Looks up an IPv6 address.
632    async fn lookup_ipv6(
633        &self,
634        host: String,
635    ) -> Result<impl Iterator<Item = Ipv6Addr> + use<>, DnsError> {
636        Ok(self
637            .resolver
638            .ipv6_lookup(host)
639            .await?
640            .into_iter()
641            .map(Ipv6Addr::from))
642    }
643
644    /// Looks up TXT records.
645    async fn lookup_txt(
646        &self,
647        host: String,
648    ) -> Result<impl Iterator<Item = TxtRecordData> + use<>, DnsError> {
649        Ok(self
650            .resolver
651            .txt_lookup(host)
652            .await?
653            .into_iter()
654            .map(|txt| TxtRecordData::from_iter(txt.iter().cloned())))
655    }
656
657    /// Clears the internal cache.
658    fn clear_cache(&self) {
659        self.resolver.clear_cache()
660    }
661
662    fn reset(&mut self) {
663        self.resolver = Self::build_resolver(&self.builder);
664    }
665}
666
667/// Record data for a TXT record.
668///
669/// This contains a list of character strings, as defined in [RFC 1035 Section 3.3.14].
670///
671/// [`TxtRecordData`] implements [`fmt::Display`], so you can call [`ToString::to_string`] to
672/// convert the record data into a string. This will parse each character string with
673/// [`String::from_utf8_lossy`] and then concatenate all strings without a separator.
674///
675/// If you want to process each character string individually, use [`Self::iter`].
676///
677/// [RFC 1035 Section 3.3.14]: https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14
678#[derive(Debug, Clone)]
679pub struct TxtRecordData(Box<[Box<[u8]>]>);
680
681impl TxtRecordData {
682    /// Returns an iterator over the character strings contained in this TXT record.
683    pub fn iter(&self) -> impl Iterator<Item = &[u8]> {
684        self.0.iter().map(|x| x.as_ref())
685    }
686}
687
688impl fmt::Display for TxtRecordData {
689    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
690        for s in self.iter() {
691            write!(f, "{}", &String::from_utf8_lossy(s))?
692        }
693        Ok(())
694    }
695}
696
697impl FromIterator<Box<[u8]>> for TxtRecordData {
698    fn from_iter<T: IntoIterator<Item = Box<[u8]>>>(iter: T) -> Self {
699        Self(iter.into_iter().collect())
700    }
701}
702
703impl From<Vec<Box<[u8]>>> for TxtRecordData {
704    fn from(value: Vec<Box<[u8]>>) -> Self {
705        Self(value.into_boxed_slice())
706    }
707}
708
709/// Helper enum to give a unified type to either of two iterators
710enum Either<A, B> {
711    Left(A),
712    Right(B),
713}
714
715impl<T, A: Iterator<Item = T>, B: Iterator<Item = T>> Iterator for Either<A, B> {
716    type Item = T;
717
718    fn next(&mut self) -> Option<Self::Item> {
719        match self {
720            Either::Left(iter) => iter.next(),
721            Either::Right(iter) => iter.next(),
722        }
723    }
724}
725
726/// Deprecated IPv6 site-local anycast addresses still configured by windows.
727///
728/// Windows still configures these site-local addresses as soon even as an IPv6 loopback
729/// interface is configured.  We do not want to use these DNS servers, the chances of them
730/// being usable are almost always close to zero, while the chance of DNS configuration
731/// **only** relying on these servers and not also being configured normally are also almost
732/// zero.  The chance of the DNS resolver accidentally trying one of these and taking a
733/// bunch of timeouts to figure out they're no good are on the other hand very high.
734const WINDOWS_BAD_SITE_LOCAL_DNS_SERVERS: [IpAddr; 3] = [
735    IpAddr::V6(Ipv6Addr::new(0xfec0, 0, 0, 0xffff, 0, 0, 0, 1)),
736    IpAddr::V6(Ipv6Addr::new(0xfec0, 0, 0, 0xffff, 0, 0, 0, 2)),
737    IpAddr::V6(Ipv6Addr::new(0xfec0, 0, 0, 0xffff, 0, 0, 0, 3)),
738];
739
740/// Helper enum to give a unified type to the iterators of [`DnsResolver::lookup_ipv4_ipv6`].
741enum LookupIter<A, B> {
742    Ipv4(A),
743    Ipv6(B),
744    Both(std::iter::Chain<A, B>),
745}
746
747impl<A: Iterator<Item = IpAddr>, B: Iterator<Item = IpAddr>> Iterator for LookupIter<A, B> {
748    type Item = IpAddr;
749
750    fn next(&mut self) -> Option<Self::Item> {
751        match self {
752            LookupIter::Ipv4(iter) => iter.next(),
753            LookupIter::Ipv6(iter) => iter.next(),
754            LookupIter::Both(iter) => iter.next(),
755        }
756    }
757}
758
759/// Staggers calls to the future F with the given delays.
760///
761/// The first call is performed immediately. The first call to succeed generates an Ok result
762/// ignoring any previous error. If all calls fail, an error summarizing all errors is returned.
763async fn stagger_call<
764    T,
765    E: std::fmt::Debug + std::fmt::Display,
766    F: Fn() -> Fut,
767    Fut: Future<Output = Result<T, E>>,
768>(
769    f: F,
770    delays_ms: &[u64],
771) -> Result<T, StaggeredError<E>> {
772    let mut calls = n0_future::FuturesUnorderedBounded::new(delays_ms.len() + 1);
773    // NOTE: we add the 0 delay here to have a uniform set of futures. This is more performant than
774    // using alternatives that allow futures of different types.
775    for delay in std::iter::once(&0u64).chain(delays_ms) {
776        let delay = add_jitter(delay);
777        let fut = f();
778        let staggered_fut = async move {
779            time::sleep(delay).await;
780            fut.await
781        };
782        calls.push(staggered_fut)
783    }
784
785    let mut errors = vec![];
786    while let Some(call_result) = calls.next().await {
787        match call_result {
788            Ok(t) => return Ok(t),
789            Err(e) => errors.push(e),
790        }
791    }
792
793    Err(StaggeredError::new(errors))
794}
795
796fn add_jitter(delay: &u64) -> Duration {
797    // If delay is 0, return 0 immediately.
798    if *delay == 0 {
799        return Duration::ZERO;
800    }
801
802    // Calculate jitter as a random value in the range of +/- MAX_JITTER_PERCENT of the delay.
803    let max_jitter = delay.saturating_mul(MAX_JITTER_PERCENT * 2) / 100;
804    let jitter = rand::random::<u64>() % max_jitter;
805
806    Duration::from_millis(delay.saturating_sub(max_jitter / 2).saturating_add(jitter))
807}
808
809#[cfg(test)]
810pub(crate) mod tests {
811    use std::sync::atomic::AtomicUsize;
812
813    use tracing_test::traced_test;
814
815    use super::*;
816
817    #[tokio::test]
818    #[traced_test]
819    async fn stagger_basic() {
820        const CALL_RESULTS: &[Result<u8, u8>] = &[Err(2), Ok(3), Ok(5), Ok(7)];
821        static DONE_CALL: AtomicUsize = AtomicUsize::new(0);
822        let f = || {
823            let r_pos = DONE_CALL.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
824            async move {
825                tracing::info!(r_pos, "call");
826                CALL_RESULTS[r_pos].map_err(|_| InvalidResponseSnafu.build())
827            }
828        };
829
830        let delays = [1000, 15];
831        let result = stagger_call(f, &delays).await.unwrap();
832        assert_eq!(result, 5)
833    }
834
835    #[test]
836    #[traced_test]
837    fn jitter_test_zero() {
838        let jittered_delay = add_jitter(&0);
839        assert_eq!(jittered_delay, Duration::from_secs(0));
840    }
841
842    //Sanity checks that I did the math right
843    #[test]
844    #[traced_test]
845    fn jitter_test_nonzero_lower_bound() {
846        let delay: u64 = 300;
847        for _ in 0..100 {
848            assert!(add_jitter(&delay) >= Duration::from_millis(delay * 8 / 10));
849        }
850    }
851
852    #[test]
853    #[traced_test]
854    fn jitter_test_nonzero_upper_bound() {
855        let delay: u64 = 300;
856        for _ in 0..100 {
857            assert!(add_jitter(&delay) < Duration::from_millis(delay * 12 / 10));
858        }
859    }
860
861    #[tokio::test]
862    #[traced_test]
863    async fn custom_resolver() {
864        #[derive(Debug)]
865        struct MyResolver;
866        impl Resolver for MyResolver {
867            fn lookup_ipv4(&self, host: String) -> BoxFuture<Result<BoxIter<Ipv4Addr>, DnsError>> {
868                Box::pin(async move {
869                    let addr = if host == "foo.example" {
870                        Ipv4Addr::new(1, 1, 1, 1)
871                    } else {
872                        return Err(NoResponseSnafu.build());
873                    };
874                    let iter: BoxIter<Ipv4Addr> = Box::new(vec![addr].into_iter());
875                    Ok(iter)
876                })
877            }
878
879            fn lookup_ipv6(&self, _host: String) -> BoxFuture<Result<BoxIter<Ipv6Addr>, DnsError>> {
880                todo!()
881            }
882
883            fn lookup_txt(
884                &self,
885                _host: String,
886            ) -> BoxFuture<Result<BoxIter<TxtRecordData>, DnsError>> {
887                todo!()
888            }
889
890            fn clear_cache(&self) {
891                todo!()
892            }
893
894            fn reset(&mut self) {
895                todo!()
896            }
897        }
898
899        let resolver = DnsResolver::custom(MyResolver);
900        let mut iter = resolver
901            .lookup_ipv4("foo.example", Duration::from_secs(1))
902            .await
903            .expect("not to fail");
904        let addr = iter.next().expect("one result");
905        assert_eq!(addr, "1.1.1.1".parse::<IpAddr>().unwrap());
906
907        let res = resolver
908            .lookup_ipv4("bar.example", Duration::from_secs(1))
909            .await;
910        assert!(matches!(res, Err(DnsError::NoResponse { .. })))
911    }
912}