Skip to main content

hickory_resolver/
config.rs

1// Copyright 2015-2017 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! Configuration for a resolver
9#![allow(clippy::use_self)]
10
11use std::collections::HashSet;
12use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
13use std::path::PathBuf;
14use std::sync::Arc;
15use std::time::Duration;
16#[cfg(all(
17    feature = "toml",
18    feature = "serde",
19    any(feature = "__tls", feature = "__quic")
20))]
21use std::{fs, io};
22
23use ipnet::IpNet;
24#[cfg(feature = "serde")]
25use serde::{Deserialize, Serialize};
26use tracing::warn;
27#[cfg(all(
28    feature = "toml",
29    feature = "serde",
30    any(feature = "__tls", feature = "__quic")
31))]
32use tracing::{debug, info};
33
34#[cfg(all(
35    feature = "toml",
36    feature = "serde",
37    any(feature = "__tls", feature = "__quic")
38))]
39use crate::name_server_pool::NameServerTransportState;
40#[cfg(any(feature = "__https", feature = "__h3"))]
41use crate::net::http::DEFAULT_DNS_QUERY_PATH;
42use crate::net::xfer::Protocol;
43use crate::proto::access_control::{AccessControlSet, AccessControlSetBuilder};
44use crate::proto::op::DEFAULT_MAX_PAYLOAD_LEN;
45use crate::proto::rr::Name;
46
47/// Configuration for the upstream nameservers to use for resolution
48#[non_exhaustive]
49#[derive(Clone, Debug, Default)]
50#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
51pub struct ResolverConfig {
52    /// Base search domain
53    #[cfg_attr(feature = "serde", serde(default))]
54    pub domain: Option<Name>,
55    /// Search domains
56    #[cfg_attr(feature = "serde", serde(default))]
57    pub search: Vec<Name>,
58    /// Name servers to use for resolution
59    pub name_servers: Vec<NameServerConfig>,
60}
61
62impl ResolverConfig {
63    /// Create a new `ResolverConfig` from [`ServerGroup`] configuration.
64    ///
65    /// Connects via UDP and TCP.
66    pub fn udp_and_tcp(config: &ServerGroup<'_>) -> Self {
67        Self {
68            // TODO: this should get the hostname and use the basename as the default
69            domain: None,
70            search: vec![],
71            name_servers: config.udp_and_tcp().collect(),
72        }
73    }
74
75    /// Create a new `ResolverConfig` from [`ServerGroup`] configuration.
76    ///
77    /// Only connects via TLS.
78    #[cfg(feature = "__tls")]
79    pub fn tls(config: &ServerGroup<'_>) -> Self {
80        Self {
81            // TODO: this should get the hostname and use the basename as the default
82            domain: None,
83            search: vec![],
84            name_servers: config.tls().collect(),
85        }
86    }
87
88    /// Create a new `ResolverConfig` from [`ServerGroup`] configuration.
89    ///
90    /// Only connects via HTTPS (HTTP/2).
91    #[cfg(feature = "__https")]
92    pub fn https(config: &ServerGroup<'_>) -> Self {
93        Self {
94            // TODO: this should get the hostname and use the basename as the default
95            domain: None,
96            search: vec![],
97            name_servers: config.https().collect(),
98        }
99    }
100
101    /// Create a new `ResolverConfig` from [`ServerGroup`] configuration.
102    ///
103    /// Only connects via QUIC.
104    #[cfg(feature = "__quic")]
105    pub fn quic(config: &ServerGroup<'_>) -> Self {
106        Self {
107            // TODO: this should get the hostname and use the basename as the default
108            domain: None,
109            search: vec![],
110            name_servers: config.quic().collect(),
111        }
112    }
113
114    /// Create a new `ResolverConfig` from [`ServerGroup`] configuration.
115    ///
116    /// Only connects via HTTP/3.
117    #[cfg(feature = "__h3")]
118    pub fn h3(config: &ServerGroup<'_>) -> Self {
119        Self {
120            // TODO: this should get the hostname and use the basename as the default
121            domain: None,
122            search: vec![],
123            name_servers: config.h3().collect(),
124        }
125    }
126
127    /// Create a ResolverConfig with all parts specified
128    ///
129    /// # Arguments
130    ///
131    /// * `domain` - domain of the entity querying results. If the `Name` being looked up is not an FQDN, then this is the first part appended to attempt a lookup. `ndots` in the `ResolverOption` does take precedence over this.
132    /// * `search` - additional search domains that are attempted if the `Name` is not found in `domain`, defaults to `vec![]`
133    /// * `name_servers` - set of name servers to use for lookups
134    pub fn from_parts(
135        domain: Option<Name>,
136        search: Vec<Name>,
137        name_servers: Vec<NameServerConfig>,
138    ) -> Self {
139        Self {
140            domain,
141            search,
142            name_servers,
143        }
144    }
145
146    /// Take the `domain`, `search`, and `name_servers` from the config.
147    pub fn into_parts(self) -> (Option<Name>, Vec<Name>, Vec<NameServerConfig>) {
148        (self.domain, self.search, self.name_servers)
149    }
150
151    /// Returns the local domain
152    ///
153    /// By default any names will be appended to all non-fully-qualified-domain names, and searched for after any ndots rules
154    pub fn domain(&self) -> Option<&Name> {
155        self.domain.as_ref()
156    }
157
158    /// Set the domain of the entity querying results.
159    pub fn set_domain(&mut self, domain: Name) {
160        self.domain = Some(domain.clone());
161        self.search = vec![domain];
162    }
163
164    /// Returns the search domains
165    ///
166    /// These will be queried after any local domain and then in the order of the set of search domains
167    pub fn search(&self) -> &[Name] {
168        &self.search
169    }
170
171    /// Add a search domain
172    pub fn add_search(&mut self, search: Name) {
173        self.search.push(search)
174    }
175
176    // TODO: consider allowing options per NameServer... like different timeouts?
177    /// Add the configuration for a name server
178    pub fn add_name_server(&mut self, name_server: NameServerConfig) {
179        self.name_servers.push(name_server);
180    }
181
182    /// Returns a reference to the name servers
183    pub fn name_servers(&self) -> &[NameServerConfig] {
184        &self.name_servers
185    }
186}
187
188/// Configuration for the NameServer
189#[derive(Clone, Debug)]
190#[cfg_attr(
191    feature = "serde",
192    derive(Serialize, Deserialize),
193    serde(deny_unknown_fields)
194)]
195#[non_exhaustive]
196pub struct NameServerConfig {
197    /// The address which the DNS NameServer is registered at.
198    pub ip: IpAddr,
199    /// Whether to trust `NXDOMAIN` responses from upstream nameservers.
200    ///
201    /// When this is `true`, and an empty `NXDOMAIN` response with an empty answers set is
202    /// received, the query will not be retried against other configured name servers.
203    ///
204    /// (On a response with any other error response code, the query will still be retried
205    /// regardless of this configuration setting.)
206    ///
207    /// Defaults to `true`.
208    #[cfg_attr(feature = "serde", serde(default = "default_trust_negative_responses"))]
209    pub trust_negative_responses: bool,
210    /// Connection protocols configured for this server.
211    pub connections: Vec<ConnectionConfig>,
212}
213
214impl NameServerConfig {
215    /// Constructs a nameserver configuration with a UDP and TCP connections
216    pub fn udp_and_tcp(ip: IpAddr) -> Self {
217        Self {
218            ip,
219            trust_negative_responses: true,
220            connections: vec![ConnectionConfig::udp(), ConnectionConfig::tcp()],
221        }
222    }
223
224    /// Constructs a nameserver configuration with a single UDP connection
225    pub fn udp(ip: IpAddr) -> Self {
226        Self {
227            ip,
228            trust_negative_responses: true,
229            connections: vec![ConnectionConfig::udp()],
230        }
231    }
232
233    /// Constructs a nameserver configuration with a single TCP connection
234    pub fn tcp(ip: IpAddr) -> Self {
235        Self {
236            ip,
237            trust_negative_responses: true,
238            connections: vec![ConnectionConfig::tcp()],
239        }
240    }
241
242    /// Constructs a nameserver configuration with a single TLS connection
243    #[cfg(feature = "__tls")]
244    pub fn tls(ip: IpAddr, server_name: Arc<str>) -> Self {
245        Self {
246            ip,
247            trust_negative_responses: true,
248            connections: vec![ConnectionConfig::tls(server_name)],
249        }
250    }
251
252    /// Constructs a nameserver configuration with a single HTTP/2 connection
253    #[cfg(feature = "__https")]
254    pub fn https(ip: IpAddr, server_name: Arc<str>, path: Option<Arc<str>>) -> Self {
255        Self {
256            ip,
257            trust_negative_responses: true,
258            connections: vec![ConnectionConfig::https(server_name, path)],
259        }
260    }
261
262    /// Constructs a nameserver configuration with a single QUIC connection
263    #[cfg(feature = "__quic")]
264    pub fn quic(ip: IpAddr, server_name: Arc<str>) -> Self {
265        Self {
266            ip,
267            trust_negative_responses: true,
268            connections: vec![ConnectionConfig::quic(server_name)],
269        }
270    }
271
272    /// Constructs a nameserver configuration with a single HTTP/3 connection
273    #[cfg(feature = "__h3")]
274    pub fn h3(ip: IpAddr, server_name: Arc<str>, path: Option<Arc<str>>) -> Self {
275        Self {
276            ip,
277            trust_negative_responses: true,
278            connections: vec![ConnectionConfig::h3(server_name, path)],
279        }
280    }
281
282    /// Constructs a nameserver configuration for opportunistic encryption.
283    ///
284    /// This will include configurations for plaintext UDP/TCP as well as DNS-over-TLS and/or
285    /// DNS-over-QUIC depending on feature flag support.
286    ///
287    /// Notably, the TLS and QUIC configurations will **not** verify peer certificates, in
288    /// keeping with RFC 9539's requirement. See [RFC 9539 §4.6.3.4] for more information.
289    ///
290    /// [RFC 9539 §4.6.3.4]: https://www.rfc-editor.org/rfc/rfc9539.html#section-4.6.3.4
291    #[cfg(any(feature = "__tls", feature = "__quic"))]
292    pub fn opportunistic_encryption(ip: IpAddr) -> Self {
293        Self {
294            ip,
295            trust_negative_responses: true,
296            connections: vec![
297                ConnectionConfig::udp(),
298                ConnectionConfig::tcp(),
299                #[cfg(feature = "__tls")]
300                ConnectionConfig::tls(Arc::from(ip.to_string())),
301                #[cfg(feature = "__quic")]
302                ConnectionConfig::quic(Arc::from(ip.to_string())),
303            ],
304        }
305    }
306
307    /// Create a new [`NameServerConfig`] from its constituent parts.
308    pub fn new(
309        ip: IpAddr,
310        trust_negative_responses: bool,
311        connections: Vec<ConnectionConfig>,
312    ) -> Self {
313        Self {
314            ip,
315            trust_negative_responses,
316            connections,
317        }
318    }
319}
320
321#[cfg(feature = "serde")]
322fn default_trust_negative_responses() -> bool {
323    true
324}
325
326/// Configuration for a connection to a nameserver
327#[derive(Clone, Debug)]
328#[cfg_attr(feature = "serde", derive(Serialize))]
329#[non_exhaustive]
330pub struct ConnectionConfig {
331    /// The remote port to connect to
332    pub port: u16,
333    /// The protocol to use for the connection
334    pub protocol: ProtocolConfig,
335    /// The client address (IP and port) to use for connecting to the server
336    pub bind_addr: Option<SocketAddr>,
337}
338
339impl ConnectionConfig {
340    /// Constructs a new ConnectionConfig for UDP
341    pub fn udp() -> Self {
342        Self::new(ProtocolConfig::Udp)
343    }
344
345    /// Constructs a new ConnectionConfig for TCP
346    pub fn tcp() -> Self {
347        Self::new(ProtocolConfig::Tcp)
348    }
349
350    /// Constructs a new ConnectionConfig for TLS
351    #[cfg(feature = "__tls")]
352    pub fn tls(server_name: Arc<str>) -> Self {
353        Self::new(ProtocolConfig::Tls { server_name })
354    }
355
356    /// Constructs a new ConnectionConfig for HTTPS (HTTP/2)
357    #[cfg(feature = "__https")]
358    pub fn https(server_name: Arc<str>, path: Option<Arc<str>>) -> Self {
359        Self::new(ProtocolConfig::Https {
360            server_name,
361            path: path.unwrap_or_else(|| Arc::from(DEFAULT_DNS_QUERY_PATH)),
362        })
363    }
364
365    /// Constructs a new ConnectionConfig for QUIC
366    #[cfg(feature = "__quic")]
367    pub fn quic(server_name: Arc<str>) -> Self {
368        Self::new(ProtocolConfig::Quic { server_name })
369    }
370
371    /// Constructs a new ConnectionConfig for HTTP/3
372    #[cfg(feature = "__h3")]
373    pub fn h3(server_name: Arc<str>, path: Option<Arc<str>>) -> Self {
374        Self::new(ProtocolConfig::H3 {
375            server_name,
376            path: path.unwrap_or_else(|| Arc::from(DEFAULT_DNS_QUERY_PATH)),
377            disable_grease: false,
378        })
379    }
380
381    /// Constructs a new ConnectionConfig with the specified [`ProtocolConfig`].
382    pub fn new(protocol: ProtocolConfig) -> Self {
383        Self {
384            port: protocol.default_port(),
385            protocol,
386            bind_addr: None,
387        }
388    }
389}
390
391#[cfg(feature = "serde")]
392impl<'de> Deserialize<'de> for ConnectionConfig {
393    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
394        #[derive(Deserialize)]
395        #[serde(deny_unknown_fields)]
396        struct OptionalParts {
397            #[serde(default)]
398            port: Option<u16>,
399            protocol: ProtocolConfig,
400            #[serde(default)]
401            bind_addr: Option<SocketAddr>,
402        }
403
404        let parts = OptionalParts::deserialize(deserializer)?;
405        Ok(Self {
406            port: parts.port.unwrap_or_else(|| parts.protocol.default_port()),
407            protocol: parts.protocol,
408            bind_addr: parts.bind_addr,
409        })
410    }
411}
412
413/// Protocol configuration
414#[allow(missing_docs)]
415#[derive(Clone, Debug, Default, PartialEq)]
416#[cfg_attr(
417    feature = "serde",
418    derive(Serialize, Deserialize),
419    serde(deny_unknown_fields, rename_all = "snake_case", tag = "type")
420)]
421pub enum ProtocolConfig {
422    #[default]
423    Udp,
424    Tcp,
425    #[cfg(feature = "__tls")]
426    Tls {
427        /// The server name to use in the TLS handshake.
428        server_name: Arc<str>,
429    },
430    #[cfg(feature = "__https")]
431    Https {
432        /// The server name to use in the TLS handshake.
433        server_name: Arc<str>,
434        /// The path (or endpoint) to use for the DNS query.
435        path: Arc<str>,
436    },
437    #[cfg(feature = "__quic")]
438    Quic {
439        /// The server name to use in the TLS handshake.
440        server_name: Arc<str>,
441    },
442    #[cfg(feature = "__h3")]
443    H3 {
444        /// The server name to use in the TLS handshake.
445        server_name: Arc<str>,
446        /// The path (or endpoint) to use for the DNS query.
447        path: Arc<str>,
448        /// Whether to disable sending "grease"
449        #[cfg_attr(feature = "serde", serde(default))]
450        disable_grease: bool,
451    },
452}
453
454impl ProtocolConfig {
455    /// Get the [`Protocol`] for this [`ProtocolConfig`].
456    pub fn to_protocol(&self) -> Protocol {
457        match self {
458            ProtocolConfig::Udp => Protocol::Udp,
459            ProtocolConfig::Tcp => Protocol::Tcp,
460            #[cfg(feature = "__tls")]
461            ProtocolConfig::Tls { .. } => Protocol::Tls,
462            #[cfg(feature = "__https")]
463            ProtocolConfig::Https { .. } => Protocol::Https,
464            #[cfg(feature = "__quic")]
465            ProtocolConfig::Quic { .. } => Protocol::Quic,
466            #[cfg(feature = "__h3")]
467            ProtocolConfig::H3 { .. } => Protocol::H3,
468        }
469    }
470
471    /// Default port for the protocol.
472    pub fn default_port(&self) -> u16 {
473        match self {
474            ProtocolConfig::Udp => 53,
475            ProtocolConfig::Tcp => 53,
476            #[cfg(feature = "__tls")]
477            ProtocolConfig::Tls { .. } => 853,
478            #[cfg(feature = "__https")]
479            ProtocolConfig::Https { .. } => 443,
480            #[cfg(feature = "__quic")]
481            ProtocolConfig::Quic { .. } => 853,
482            #[cfg(feature = "__h3")]
483            ProtocolConfig::H3 { .. } => 443,
484        }
485    }
486}
487
488/// Configuration for the Resolver
489#[derive(Debug, Clone)]
490#[cfg_attr(
491    feature = "serde",
492    derive(Serialize, Deserialize),
493    serde(default, deny_unknown_fields)
494)]
495#[non_exhaustive]
496pub struct ResolverOpts {
497    /// Sets the number of dots that must appear (unless it's a final dot representing the root)
498    ///  before a query is assumed to include the TLD. The default is one, which means that `www`
499    ///  would never be assumed to be a TLD, and would always be appended to either the search
500    #[cfg_attr(feature = "serde", serde(default = "default_ndots"))]
501    pub ndots: usize,
502    /// Specify the timeout for a request. Defaults to 5 seconds
503    #[cfg_attr(
504        feature = "serde",
505        serde(default = "default_timeout", with = "duration")
506    )]
507    pub timeout: Duration,
508    /// Number of retries after lookup failure before giving up. Defaults to 2
509    #[cfg_attr(feature = "serde", serde(default = "default_attempts"))]
510    pub attempts: usize,
511    /// Enable edns, for larger records
512    pub edns0: bool,
513    /// Use DNSSEC to validate the request
514    #[cfg(feature = "__dnssec")]
515    pub validate: bool,
516    /// The strategy for the Resolver to use when looking up host IP addresses
517    pub ip_strategy: LookupIpStrategy,
518    /// Cache size is in number of responses (some responses can be large)
519    #[cfg_attr(feature = "serde", serde(default = "default_cache_size"))]
520    pub cache_size: u64,
521    /// Check /etc/hosts file before dns requery (only works for unix like OS)
522    pub use_hosts_file: ResolveHosts,
523    /// Optional minimum TTL for positive responses.
524    ///
525    /// If this is set, any positive responses with a TTL lower than this value will have a TTL of
526    /// `positive_min_ttl` instead. Otherwise, this will default to 0 seconds.
527    #[cfg_attr(feature = "serde", serde(with = "duration_opt"))]
528    pub positive_min_ttl: Option<Duration>,
529    /// Optional minimum TTL for negative (`NXDOMAIN`) responses.
530    ///
531    /// If this is set, any negative responses with a TTL lower than this value will have a TTL of
532    /// `negative_min_ttl` instead. Otherwise, this will default to 0 seconds.
533    #[cfg_attr(feature = "serde", serde(with = "duration_opt"))]
534    pub negative_min_ttl: Option<Duration>,
535    /// Optional maximum TTL for positive responses.
536    ///
537    /// If this is set, any positive responses with a TTL higher than this value will have a TTL of
538    /// `positive_max_ttl` instead. Otherwise, this will default to [`MAX_TTL`](crate::MAX_TTL) seconds.
539    #[cfg_attr(feature = "serde", serde(with = "duration_opt"))]
540    pub positive_max_ttl: Option<Duration>,
541    /// Optional maximum TTL for negative (`NXDOMAIN`) responses.
542    ///
543    /// If this is set, any negative responses with a TTL higher than this value will have a TTL of
544    /// `negative_max_ttl` instead. Otherwise, this will default to [`MAX_TTL`](crate::MAX_TTL) seconds.
545    #[cfg_attr(feature = "serde", serde(with = "duration_opt"))]
546    pub negative_max_ttl: Option<Duration>,
547    /// Number of concurrent requests per query
548    ///
549    /// Where more than one nameserver is configured, this configures the resolver to send queries
550    /// to a number of servers in parallel. Defaults to 2; 0 or 1 will execute requests serially.
551    #[cfg_attr(feature = "serde", serde(default = "default_num_concurrent_reqs"))]
552    pub num_concurrent_reqs: usize,
553    /// Maximum number of active (in-flight) requests per multiplexed connection.
554    ///
555    /// This limits how many DNS queries can be simultaneously pending on a single
556    /// connection to an upstream nameserver. When the limit is reached, new requests
557    /// will return a busy error.
558    ///
559    /// Defaults to 32. Higher values allow more parallelism but consume more memory.
560    #[cfg_attr(feature = "serde", serde(default = "default_max_active_requests"))]
561    pub max_active_requests: usize,
562    /// Preserve all intermediate records in the lookup response, such as CNAME records
563    #[cfg_attr(feature = "serde", serde(default = "default_preserve_intermediates"))]
564    pub preserve_intermediates: bool,
565    /// Try queries over TCP if they fail over UDP.
566    pub try_tcp_on_error: bool,
567    /// The server ordering strategy that the resolver should use.
568    pub server_ordering_strategy: ServerOrderingStrategy,
569    /// Request upstream recursive resolvers to not perform any recursion.
570    ///
571    /// This is true by default, disabling this is useful for requesting single records, but may prevent successful resolution.
572    #[cfg_attr(feature = "serde", serde(default = "default_recursion_desired"))]
573    pub recursion_desired: bool,
574    /// Local UDP ports to avoid when making outgoing queries
575    pub avoid_local_udp_ports: Arc<HashSet<u16>>,
576    /// Request UDP bind ephemeral ports directly from the OS
577    ///
578    /// Boolean parameter to specify whether to use the operating system's standard UDP port
579    /// selection logic instead of Hickory's logic to securely select a random source port. We do
580    /// not recommend using this option unless absolutely necessary, as the operating system may
581    /// select ephemeral ports from a smaller range than Hickory, which can make response poisoning
582    /// attacks easier to conduct. Some operating systems (notably, Windows) might display a
583    /// user-prompt to allow a Hickory-specified port to be used, and setting this option will
584    /// prevent those prompts from being displayed. If os_port_selection is true, avoid_local_udp_ports
585    /// will be ignored.
586    pub os_port_selection: bool,
587    /// Enable case randomization.
588    ///
589    /// Randomize the case of letters in query names, and require that responses preserve the case
590    /// of the query name, in order to mitigate spoofing attacks. This is only applied over UDP.
591    ///
592    /// This implements the mechanism described in
593    /// [draft-vixie-dnsext-dns0x20-00](https://datatracker.ietf.org/doc/html/draft-vixie-dnsext-dns0x20-00).
594    pub case_randomization: bool,
595    /// Path to a DNSSEC trust anchor file.
596    ///
597    /// If this is provided, `validate` will automatically be set to `true`, enabling DNSSEC validation.
598    pub trust_anchor: Option<PathBuf>,
599    /// Exceptions to `deny_answer_addresses`. Networks listed here will be allowed, even if the IP address
600    /// matches a network in `deny_answer_addresses`.
601    pub allow_answers: Vec<IpNet>,
602    /// Networks listed here will be removed from any answers returned by an upstream server.
603    pub deny_answers: Vec<IpNet>,
604    /// Configure the EDNS UDP payload size used in queries.
605    ///
606    /// See [DnsRequestOptions::edns_payload_len][crate::proto::op::DnsRequestOptions::edns_payload_len].
607    #[cfg_attr(feature = "serde", serde(default = "default_edns_payload_len"))]
608    pub edns_payload_len: u16,
609}
610
611impl ResolverOpts {
612    pub(crate) fn answer_address_filter(&self) -> AccessControlSet {
613        let name = "resolver_answer_filter";
614        AccessControlSetBuilder::new(name)
615            .allow(self.allow_answers.iter())
616            .deny(self.deny_answers.iter())
617            .build()
618            .inspect_err(|err| warn!("{err}"))
619            .unwrap_or_else(|_| AccessControlSet::empty(name))
620    }
621}
622
623impl Default for ResolverOpts {
624    /// Default values for the Resolver configuration.
625    ///
626    /// This follows the resolv.conf defaults as defined in the [Linux man pages](https://man7.org/linux/man-pages/man5/resolv.conf.5.html)
627    fn default() -> Self {
628        Self {
629            ndots: default_ndots(),
630            timeout: default_timeout(),
631            attempts: default_attempts(),
632            edns0: true,
633            #[cfg(feature = "__dnssec")]
634            validate: false,
635            ip_strategy: LookupIpStrategy::default(),
636            cache_size: default_cache_size(),
637            use_hosts_file: ResolveHosts::default(),
638            positive_min_ttl: None,
639            negative_min_ttl: None,
640            positive_max_ttl: None,
641            negative_max_ttl: None,
642            num_concurrent_reqs: default_num_concurrent_reqs(),
643            max_active_requests: default_max_active_requests(),
644
645            // Defaults to `true` to match the behavior of dig and nslookup.
646            preserve_intermediates: default_preserve_intermediates(),
647
648            try_tcp_on_error: false,
649            server_ordering_strategy: ServerOrderingStrategy::default(),
650            recursion_desired: default_recursion_desired(),
651            avoid_local_udp_ports: Arc::default(),
652            os_port_selection: false,
653            case_randomization: false,
654            trust_anchor: None,
655            allow_answers: vec![],
656            deny_answers: vec![],
657            edns_payload_len: default_edns_payload_len(),
658        }
659    }
660}
661
662fn default_ndots() -> usize {
663    1
664}
665
666fn default_timeout() -> Duration {
667    Duration::from_secs(5)
668}
669
670fn default_attempts() -> usize {
671    2
672}
673
674fn default_cache_size() -> u64 {
675    8_192
676}
677
678fn default_num_concurrent_reqs() -> usize {
679    2
680}
681
682fn default_max_active_requests() -> usize {
683    32
684}
685
686fn default_preserve_intermediates() -> bool {
687    true
688}
689
690fn default_recursion_desired() -> bool {
691    true
692}
693
694fn default_edns_payload_len() -> u16 {
695    DEFAULT_MAX_PAYLOAD_LEN
696}
697
698/// The lookup ip strategy
699#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
700#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
701pub enum LookupIpStrategy {
702    /// Only query for A (Ipv4) records
703    Ipv4Only,
704    /// Only query for AAAA (Ipv6) records
705    Ipv6Only,
706    /// Query for A and AAAA in parallel, ordering A before AAAA
707    Ipv4AndIpv6,
708    /// Query for AAAA and A in parallel, ordering AAAA before A
709    #[default]
710    Ipv6AndIpv4,
711    /// Query for Ipv6 if that fails, query for Ipv4
712    Ipv6thenIpv4,
713    /// Query for Ipv4 if that fails, query for Ipv6 (default)
714    Ipv4thenIpv6,
715}
716
717/// The strategy for establishing the query order of name servers in a pool.
718#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
719#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
720#[non_exhaustive]
721pub enum ServerOrderingStrategy {
722    /// Servers are ordered based on collected query statistics. The ordering
723    /// may vary over time.
724    #[default]
725    QueryStatistics,
726    /// The order provided to the resolver is used. The ordering does not vary
727    /// over time.
728    UserProvidedOrder,
729    /// The order of servers is rotated in a round-robin fashion. This is useful for
730    /// load balancing and ensuring that all servers are used evenly.
731    RoundRobin,
732}
733
734/// Whether the system hosts file should be respected by the resolver.
735#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
736#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
737pub enum ResolveHosts {
738    /// Always attempt to look up IP addresses from the system hosts file.
739    /// If the hostname cannot be found, query the DNS.
740    Always,
741    /// The DNS will always be queried.
742    Never,
743    /// Use local resolver configurations only when this resolver is not used in
744    /// a DNS forwarder. This is the default.
745    #[default]
746    Auto,
747}
748
749/// Configuration for enabling RFC 9539 opportunistic encryption.
750///
751/// Controls how a recursive resolver probes name servers to discover if they support
752/// encrypted transports.
753#[derive(Debug, Clone, Default, Eq, PartialEq)]
754#[cfg_attr(
755    feature = "serde",
756    derive(Serialize, Deserialize),
757    serde(rename_all = "snake_case")
758)]
759#[non_exhaustive]
760pub enum OpportunisticEncryption {
761    /// Opportunistic encryption will not be performed.
762    #[default]
763    Disabled,
764    /// Opportunistic encryption will be performed.
765    #[cfg(any(feature = "__tls", feature = "__quic"))]
766    Enabled {
767        /// Configuration parameters for opportunistic encryption.
768        #[cfg_attr(feature = "serde", serde(flatten))]
769        config: OpportunisticEncryptionConfig,
770    },
771}
772
773impl OpportunisticEncryption {
774    #[cfg(all(
775        feature = "toml",
776        feature = "serde",
777        any(feature = "__tls", feature = "__quic")
778    ))]
779    pub(super) fn persisted_state(&self) -> Result<Option<NameServerTransportState>, String> {
780        let OpportunisticEncryption::Enabled {
781            config:
782                OpportunisticEncryptionConfig {
783                    persistence: Some(OpportunisticEncryptionPersistence { path, .. }),
784                    ..
785                },
786        } = self
787        else {
788            return Ok(None);
789        };
790
791        let state = match fs::read_to_string(path) {
792            Ok(toml_content) => toml::from_str(&toml_content).map_err(|e| {
793                format!(
794                    "failed to parse opportunistic encryption state TOML file: {file_path}: {e}",
795                    file_path = path.display()
796                )
797            })?,
798            Err(e) if e.kind() == io::ErrorKind::NotFound => {
799                info!(
800                    state_file = %path.display(),
801                    "no pre-existing opportunistic encryption state TOML file, starting with default state",
802                );
803                NameServerTransportState::default()
804            }
805            Err(e) => {
806                return Err(format!(
807                    "failed to read opportunistic encryption state TOML file: {file_path}: {e}",
808                    file_path = path.display()
809                ));
810            }
811        };
812
813        debug!(
814            path = %path.display(),
815            nameserver_count = state.nameserver_count(),
816            "loaded opportunistic encryption state"
817        );
818
819        Ok(Some(state))
820    }
821
822    /// Returns true if opportunistic encryption is enabled.
823    pub fn is_enabled(&self) -> bool {
824        match self {
825            Self::Disabled => false,
826            #[cfg(any(feature = "__tls", feature = "__quic"))]
827            Self::Enabled { .. } => true,
828        }
829    }
830
831    /// Returns the maximum number of concurrent probes if opportunistic encrypt is enabled.
832    pub fn max_concurrent_probes(&self) -> Option<u8> {
833        match self {
834            Self::Disabled => None,
835            #[cfg(any(feature = "__tls", feature = "__quic"))]
836            Self::Enabled { config, .. } => Some(config.max_concurrent_probes),
837        }
838    }
839}
840
841/// Configuration parameters for opportunistic encryption.
842#[derive(Debug, Clone, Eq, PartialEq)]
843#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
844#[cfg_attr(feature = "serde", serde(default, deny_unknown_fields))]
845pub struct OpportunisticEncryptionConfig {
846    /// How long the recursive resolver remembers a successful encrypted transport connection.
847    #[cfg_attr(
848        feature = "serde",
849        serde(default = "default_persistence_period", with = "duration")
850    )]
851    pub persistence_period: Duration,
852
853    /// How long the recursive resolver remembers a failed encrypted transport connection.
854    #[cfg_attr(
855        feature = "serde",
856        serde(default = "default_damping_period", with = "duration")
857    )]
858    pub damping_period: Duration,
859
860    /// Maximum number of concurrent opportunistic encryption probes.
861    #[cfg_attr(feature = "serde", serde(default = "default_max_concurrent_probes"))]
862    pub max_concurrent_probes: u8,
863
864    /// Optional configuration for persistence of opportunistic encryption probe state.
865    pub persistence: Option<OpportunisticEncryptionPersistence>,
866}
867
868impl Default for OpportunisticEncryptionConfig {
869    fn default() -> Self {
870        Self {
871            persistence_period: default_persistence_period(),
872            damping_period: default_damping_period(),
873            max_concurrent_probes: default_max_concurrent_probes(),
874            persistence: None,
875        }
876    }
877}
878
879/// The RFC 9539 suggested default for the resolver persistence period.
880fn default_persistence_period() -> Duration {
881    Duration::from_secs(60 * 60 * 24 * 3) // 3 days
882}
883
884/// The RFC 9539 suggested default for the resolver damping period.
885fn default_damping_period() -> Duration {
886    Duration::from_secs(24 * 60 * 60) // 1 day
887}
888
889/// A conservative default for the maximum number of in-flight opportunistic probe requests.
890fn default_max_concurrent_probes() -> u8 {
891    10
892}
893
894#[derive(Debug, Clone, Eq, PartialEq)]
895#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
896#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
897/// Configuration for persistence of opportunistic encryption probe state.
898pub struct OpportunisticEncryptionPersistence {
899    /// Path to a TOML state file that may be used for saving/loading opportunistic encryption state.
900    pub path: PathBuf,
901
902    /// Interval after which opportunistic encryption state is periodically saved to `path`.
903    #[cfg_attr(
904        feature = "serde",
905        serde(default = "default_save_interval", with = "duration")
906    )]
907    pub save_interval: Duration,
908}
909
910#[cfg(feature = "serde")]
911fn default_save_interval() -> Duration {
912    Duration::from_secs(60 * 10) // 10 minutes
913}
914
915/// Google Public DNS configuration.
916///
917/// Please see Google's [privacy statement](https://developers.google.com/speed/public-dns/privacy)
918/// for important information about what they track, many ISP's track similar information in DNS.
919/// To use the system configuration see: `Resolver::from_system_conf`.
920pub const GOOGLE: ServerGroup<'static> = ServerGroup {
921    ips: &[
922        IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)),
923        IpAddr::V4(Ipv4Addr::new(8, 8, 4, 4)),
924        IpAddr::V6(Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888)),
925        IpAddr::V6(Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8844)),
926    ],
927    server_name: "dns.google",
928    path: "/dns-query",
929};
930
931/// Cloudflare's 1.1.1.1 DNS service configuration.
932///
933/// See <https://www.cloudflare.com/dns/> for more information.
934pub const CLOUDFLARE: ServerGroup<'static> = ServerGroup {
935    ips: &[
936        IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)),
937        IpAddr::V4(Ipv4Addr::new(1, 0, 0, 1)),
938        IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1111)),
939        IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1001)),
940    ],
941    server_name: "cloudflare-dns.com",
942    path: "/dns-query",
943};
944
945/// The Quad9 DNS service configuration.
946///
947/// See <https://www.quad9.net/faq/> for more information.
948pub const QUAD9: ServerGroup<'static> = ServerGroup {
949    ips: &[
950        IpAddr::V4(Ipv4Addr::new(9, 9, 9, 9)),
951        IpAddr::V4(Ipv4Addr::new(149, 112, 112, 112)),
952        IpAddr::V6(Ipv6Addr::new(0x2620, 0x00fe, 0, 0, 0, 0, 0, 0x00fe)),
953        IpAddr::V6(Ipv6Addr::new(0x2620, 0x00fe, 0, 0, 0, 0, 0, 0x0009)),
954    ],
955    server_name: "dns.quad9.net",
956    path: "/dns-query",
957};
958
959/// A group of DNS servers.
960#[derive(Clone, Copy, Debug)]
961pub struct ServerGroup<'a> {
962    /// IP addresses of the DNS servers in this group.
963    pub ips: &'a [IpAddr],
964    /// The TLS server name to use for servers.
965    pub server_name: &'a str,
966    /// The query path to use for HTTP queries.
967    pub path: &'a str,
968}
969
970impl<'a> ServerGroup<'a> {
971    /// Create an iterator with `NameServerConfig` for each IP address in the group.
972    pub fn udp_and_tcp(&self) -> impl Iterator<Item = NameServerConfig> + 'a {
973        self.ips.iter().map(|&ip| {
974            NameServerConfig::new(
975                ip,
976                true,
977                vec![ConnectionConfig::udp(), ConnectionConfig::tcp()],
978            )
979        })
980    }
981
982    /// Create an iterator with `NameServerConfig` for each IP address in the group.
983    pub fn udp(&self) -> impl Iterator<Item = NameServerConfig> + 'a {
984        self.ips
985            .iter()
986            .map(|&ip| NameServerConfig::new(ip, true, vec![ConnectionConfig::udp()]))
987    }
988
989    /// Create an iterator with `NameServerConfig` for each IP address in the group.
990    pub fn tcp(&self) -> impl Iterator<Item = NameServerConfig> + 'a {
991        self.ips
992            .iter()
993            .map(|&ip| NameServerConfig::new(ip, true, vec![ConnectionConfig::tcp()]))
994    }
995
996    /// Create an iterator with `NameServerConfig` for each IP address in the group.
997    #[cfg(feature = "__tls")]
998    pub fn tls(&self) -> impl Iterator<Item = NameServerConfig> + 'a {
999        let this = *self;
1000        self.ips.iter().map(move |&ip| {
1001            NameServerConfig::new(
1002                ip,
1003                true,
1004                vec![ConnectionConfig::tls(Arc::from(this.server_name))],
1005            )
1006        })
1007    }
1008
1009    /// Create an iterator with `NameServerConfig` for each IP address in the group.
1010    #[cfg(feature = "__https")]
1011    pub fn https(&self) -> impl Iterator<Item = NameServerConfig> + 'a {
1012        let this = *self;
1013        self.ips.iter().map(move |&ip| {
1014            NameServerConfig::new(
1015                ip,
1016                true,
1017                vec![ConnectionConfig::https(
1018                    Arc::from(this.server_name),
1019                    Some(Arc::from(this.path)),
1020                )],
1021            )
1022        })
1023    }
1024
1025    /// Create an iterator with `NameServerConfig` for each IP address in the group.
1026    #[cfg(feature = "__quic")]
1027    pub fn quic(&self) -> impl Iterator<Item = NameServerConfig> + 'a {
1028        let this = *self;
1029        self.ips.iter().map(move |&ip| {
1030            NameServerConfig::new(
1031                ip,
1032                true,
1033                vec![ConnectionConfig::quic(Arc::from(this.server_name))],
1034            )
1035        })
1036    }
1037
1038    /// Create an iterator with `NameServerConfig` for each IP address in the group.
1039    #[cfg(feature = "__h3")]
1040    pub fn h3(&self) -> impl Iterator<Item = NameServerConfig> + 'a {
1041        let this = *self;
1042        self.ips.iter().map(move |&ip| {
1043            NameServerConfig::new(
1044                ip,
1045                true,
1046                vec![ConnectionConfig::h3(
1047                    Arc::from(this.server_name),
1048                    Some(Arc::from(this.path)),
1049                )],
1050            )
1051        })
1052    }
1053}
1054
1055#[cfg(feature = "serde")]
1056pub(crate) mod duration {
1057    use std::time::Duration;
1058
1059    use serde::{Deserialize, Deserializer, Serialize, Serializer};
1060
1061    /// This is an alternate serialization function for a [`Duration`] that emits a single number,
1062    /// representing the number of seconds, instead of a struct with `secs` and `nanos` fields.
1063    pub(super) fn serialize<S: Serializer>(
1064        duration: &Duration,
1065        serializer: S,
1066    ) -> Result<S::Ok, S::Error> {
1067        duration.as_secs().serialize(serializer)
1068    }
1069
1070    /// This is an alternate deserialization function for a [`Duration`] that expects a single number,
1071    /// representing the number of seconds, instead of a struct with `secs` and `nanos` fields.
1072    pub(crate) fn deserialize<'de, D: Deserializer<'de>>(
1073        deserializer: D,
1074    ) -> Result<Duration, D::Error> {
1075        Ok(Duration::from_secs(u64::deserialize(deserializer)?))
1076    }
1077}
1078
1079#[cfg(feature = "serde")]
1080pub(crate) mod duration_opt {
1081    use std::time::Duration;
1082
1083    use serde::{Deserialize, Deserializer, Serialize, Serializer};
1084
1085    /// This is an alternate serialization function for an optional [`Duration`] that emits a single
1086    /// number, representing the number of seconds, instead of a struct with `secs` and `nanos` fields.
1087    pub(super) fn serialize<S: Serializer>(
1088        duration: &Option<Duration>,
1089        serializer: S,
1090    ) -> Result<S::Ok, S::Error> {
1091        struct Wrapper<'a>(&'a Duration);
1092
1093        impl Serialize for Wrapper<'_> {
1094            fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1095                super::duration::serialize(self.0, serializer)
1096            }
1097        }
1098
1099        match duration {
1100            Some(duration) => serializer.serialize_some(&Wrapper(duration)),
1101            None => serializer.serialize_none(),
1102        }
1103    }
1104
1105    /// This is an alternate deserialization function for an optional [`Duration`] that expects a single
1106    /// number, representing the number of seconds, instead of a struct with `secs` and `nanos` fields.
1107    pub(crate) fn deserialize<'de, D: Deserializer<'de>>(
1108        deserializer: D,
1109    ) -> Result<Option<Duration>, D::Error> {
1110        Ok(Option::<u64>::deserialize(deserializer)?.map(Duration::from_secs))
1111    }
1112}
1113
1114#[cfg(all(test, feature = "serde"))]
1115mod tests {
1116    use super::*;
1117
1118    #[cfg(feature = "serde")]
1119    #[test]
1120    fn default_opts() {
1121        let code = ResolverOpts::default();
1122        let json = serde_json::from_str::<ResolverOpts>("{}").unwrap();
1123        assert_eq!(code.ndots, json.ndots);
1124        assert_eq!(code.timeout, json.timeout);
1125        assert_eq!(code.attempts, json.attempts);
1126        assert_eq!(code.edns0, json.edns0);
1127        #[cfg(feature = "__dnssec")]
1128        assert_eq!(code.validate, json.validate);
1129        assert_eq!(code.ip_strategy, json.ip_strategy);
1130        assert_eq!(code.cache_size, json.cache_size);
1131        assert_eq!(code.use_hosts_file, json.use_hosts_file);
1132        assert_eq!(code.positive_min_ttl, json.positive_min_ttl);
1133        assert_eq!(code.negative_min_ttl, json.negative_min_ttl);
1134        assert_eq!(code.positive_max_ttl, json.positive_max_ttl);
1135        assert_eq!(code.negative_max_ttl, json.negative_max_ttl);
1136        assert_eq!(code.num_concurrent_reqs, json.num_concurrent_reqs);
1137        assert_eq!(code.preserve_intermediates, json.preserve_intermediates);
1138        assert_eq!(code.try_tcp_on_error, json.try_tcp_on_error);
1139        assert_eq!(code.recursion_desired, json.recursion_desired);
1140        assert_eq!(code.server_ordering_strategy, json.server_ordering_strategy);
1141        assert_eq!(code.avoid_local_udp_ports, json.avoid_local_udp_ports);
1142        assert_eq!(code.os_port_selection, json.os_port_selection);
1143        assert_eq!(code.case_randomization, json.case_randomization);
1144        assert_eq!(code.trust_anchor, json.trust_anchor);
1145    }
1146}