Skip to main content

mail_auth/
authenticator.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7#[cfg(feature = "dns-doh")]
8use crate::dns::DohResolver;
9#[cfg(not(feature = "dns-doh"))]
10use hickory_resolver::TokioResolver;
11use std::time::Duration;
12
13pub(crate) const DEFAULT_MAX_NEGATIVE_TTL: Duration = Duration::from_secs(3600);
14
15/// Entry point for every verifier and DNS lookup in this crate.
16///
17/// A `MessageAuthenticator` wraps a DNS resolver and exposes the verifiers
18/// (`verify_dkim`, `verify_dkim2`, `verify_arc`, `verify_spf`, `verify_dmarc`,
19/// [`verify_iprev`](Self::verify_iprev)) together with the DNS lookup helpers
20/// used by them. Cloning is cheap: clones share the underlying resolver.
21///
22/// With the default `dns-hickory` feature the resolver is a
23/// [`hickory_resolver::TokioResolver`], built with [`new`](Self::new),
24/// [`new_system_conf`](Self::new_system_conf) or one of the provider presets
25/// such as [`new_cloudflare_tls`](Self::new_cloudflare_tls).
26///
27/// # Example
28///
29/// ```rust,no_run
30/// use mail_auth::{AuthenticatedMessage, DkimResult, MessageAuthenticator};
31///
32/// # async fn run(raw_message: &[u8]) {
33/// let authenticator = MessageAuthenticator::new_cloudflare_tls().unwrap();
34/// let message = AuthenticatedMessage::parse(raw_message).unwrap();
35/// let results = authenticator.verify_dkim(&message).await;
36/// assert!(results.iter().all(|r| r.result() == &DkimResult::Pass));
37/// # }
38/// ```
39#[derive(Clone)]
40#[cfg(not(feature = "dns-doh"))]
41pub struct MessageAuthenticator {
42    pub(crate) resolver: TokioResolver,
43    pub(crate) max_negative_ttl: Duration,
44}
45
46/// Entry point for every verifier and DNS lookup in this crate.
47///
48/// A `MessageAuthenticator` wraps a DNS resolver and exposes the verifiers
49/// (`verify_dkim`, `verify_dkim2`, `verify_arc`, `verify_spf`, `verify_dmarc`,
50/// [`verify_iprev`](Self::verify_iprev)) together with the DNS lookup helpers
51/// used by them. Cloning is cheap: clones share the underlying HTTP client.
52///
53/// With the `dns-doh` feature the resolver is a [`DohResolver`] that sends
54/// DNS-over-HTTPS queries (RFC 8484). Build one with
55/// [`new_doh`](Self::new_doh), [`new_doh_wire`](Self::new_doh_wire) or a
56/// provider preset such as [`new_doh_cloudflare`](Self::new_doh_cloudflare).
57///
58/// # Example
59///
60/// ```rust,no_run
61/// use mail_auth::{AuthenticatedMessage, DkimResult, MessageAuthenticator};
62///
63/// # async fn run(raw_message: &[u8]) {
64/// let authenticator = MessageAuthenticator::new_doh_cloudflare();
65/// let message = AuthenticatedMessage::parse(raw_message).unwrap();
66/// let results = authenticator.verify_dkim(&message).await;
67/// assert!(results.iter().all(|r| r.result() == &DkimResult::Pass));
68/// # }
69/// ```
70#[derive(Clone)]
71#[cfg(feature = "dns-doh")]
72pub struct MessageAuthenticator {
73    pub(crate) resolver: DohResolver,
74    pub(crate) max_negative_ttl: Duration,
75}
76
77impl MessageAuthenticator {
78    /// Returns the underlying hickory resolver, for issuing DNS queries that
79    /// this crate does not wrap.
80    #[cfg(not(feature = "dns-doh"))]
81    pub fn resolver(&self) -> &TokioResolver {
82        &self.resolver
83    }
84
85    /// Returns the underlying DNS-over-HTTPS resolver.
86    #[cfg(feature = "dns-doh")]
87    pub fn resolver(&self) -> &DohResolver {
88        &self.resolver
89    }
90
91    /// Sets the longest time a negative answer (`NXDOMAIN` or NODATA) is
92    /// kept in the DNS caches.
93    pub fn with_max_negative_ttl(mut self, ttl: Duration) -> Self {
94        self.max_negative_ttl = ttl;
95        self
96    }
97}