Skip to main content

mail_auth/common/
cache.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7use crate::{MX, Parameters, ResolverCache, Txt};
8use std::{
9    borrow::Borrow,
10    hash::Hash,
11    marker::PhantomData,
12    net::{IpAddr, Ipv4Addr, Ipv6Addr},
13    sync::Arc,
14};
15
16pub struct NoCache<K, V>(PhantomData<(K, V)>);
17
18impl<K, V> ResolverCache<K, V> for NoCache<K, V> {
19    fn get<Q>(&self, _: &Q) -> Option<V>
20    where
21        K: Borrow<Q>,
22        Q: Hash + Eq + ?Sized,
23    {
24        None
25    }
26
27    fn remove<Q>(&self, _: &Q) -> Option<V>
28    where
29        K: Borrow<Q>,
30        Q: Hash + Eq + ?Sized,
31    {
32        None
33    }
34
35    fn insert(&self, _: K, _: V, _: std::time::Instant) {}
36}
37
38impl<P>
39    Parameters<
40        '_,
41        P,
42        NoCache<Box<str>, Txt>,
43        NoCache<Box<str>, Arc<[MX]>>,
44        NoCache<Box<str>, Arc<[Ipv4Addr]>>,
45        NoCache<Box<str>, Arc<[Ipv6Addr]>>,
46        NoCache<IpAddr, Arc<[Box<str>]>>,
47    >
48{
49    pub fn new(params: P) -> Self {
50        Parameters {
51            params,
52            cache_txt: None,
53            cache_mx: None,
54            cache_ptr: None,
55            cache_ipv4: None,
56            cache_ipv6: None,
57        }
58    }
59}
60
61impl<'x, P, TXT, MXX, IPV4, IPV6, PTR> Parameters<'x, P, TXT, MXX, IPV4, IPV6, PTR>
62where
63    TXT: ResolverCache<Box<str>, Txt>,
64    MXX: ResolverCache<Box<str>, Arc<[MX]>>,
65    IPV4: ResolverCache<Box<str>, Arc<[Ipv4Addr]>>,
66    IPV6: ResolverCache<Box<str>, Arc<[Ipv6Addr]>>,
67    PTR: ResolverCache<IpAddr, Arc<[Box<str>]>>,
68{
69    pub fn with_txt_cache<NewTXT: ResolverCache<Box<str>, Txt>>(
70        self,
71        cache: &'x NewTXT,
72    ) -> Parameters<'x, P, NewTXT, MXX, IPV4, IPV6, PTR> {
73        Parameters {
74            params: self.params,
75            cache_txt: Some(cache),
76            cache_mx: self.cache_mx,
77            cache_ptr: self.cache_ptr,
78            cache_ipv4: self.cache_ipv4,
79            cache_ipv6: self.cache_ipv6,
80        }
81    }
82
83    pub fn with_mx_cache<NewMX: ResolverCache<Box<str>, Arc<[MX]>>>(
84        self,
85        cache: &'x NewMX,
86    ) -> Parameters<'x, P, TXT, NewMX, IPV4, IPV6, PTR> {
87        Parameters {
88            params: self.params,
89            cache_txt: self.cache_txt,
90            cache_mx: Some(cache),
91            cache_ptr: self.cache_ptr,
92            cache_ipv4: self.cache_ipv4,
93            cache_ipv6: self.cache_ipv6,
94        }
95    }
96
97    pub fn with_ptr_cache<NewPTR: ResolverCache<IpAddr, Arc<[Box<str>]>>>(
98        self,
99        cache: &'x NewPTR,
100    ) -> Parameters<'x, P, TXT, MXX, IPV4, IPV6, NewPTR> {
101        Parameters {
102            params: self.params,
103            cache_txt: self.cache_txt,
104            cache_mx: self.cache_mx,
105            cache_ptr: Some(cache),
106            cache_ipv4: self.cache_ipv4,
107            cache_ipv6: self.cache_ipv6,
108        }
109    }
110
111    pub fn with_ipv4_cache<NewIPV4: ResolverCache<Box<str>, Arc<[Ipv4Addr]>>>(
112        self,
113        cache: &'x NewIPV4,
114    ) -> Parameters<'x, P, TXT, MXX, NewIPV4, IPV6, PTR> {
115        Parameters {
116            params: self.params,
117            cache_txt: self.cache_txt,
118            cache_mx: self.cache_mx,
119            cache_ptr: self.cache_ptr,
120            cache_ipv4: Some(cache),
121            cache_ipv6: self.cache_ipv6,
122        }
123    }
124
125    pub fn with_ipv6_cache<NewIPV6: ResolverCache<Box<str>, Arc<[Ipv6Addr]>>>(
126        self,
127        cache: &'x NewIPV6,
128    ) -> Parameters<'x, P, TXT, MXX, IPV4, NewIPV6, PTR> {
129        Parameters {
130            params: self.params,
131            cache_txt: self.cache_txt,
132            cache_mx: self.cache_mx,
133            cache_ptr: self.cache_ptr,
134            cache_ipv4: self.cache_ipv4,
135            cache_ipv6: Some(cache),
136        }
137    }
138
139    pub fn clone_with<NewP>(
140        &self,
141        params: NewP,
142    ) -> Parameters<'x, NewP, TXT, MXX, IPV4, IPV6, PTR> {
143        Parameters {
144            params,
145            cache_txt: self.cache_txt,
146            cache_mx: self.cache_mx,
147            cache_ptr: self.cache_ptr,
148            cache_ipv4: self.cache_ipv4,
149            cache_ipv6: self.cache_ipv6,
150        }
151    }
152}
153
154#[cfg(test)]
155pub mod test {
156    use std::{
157        borrow::Borrow,
158        hash::Hash,
159        net::{IpAddr, Ipv4Addr, Ipv6Addr},
160        sync::Arc,
161    };
162
163    use crate::{MX, Parameters, ResolverCache, Txt, common::resolver::ToFqdn};
164
165    pub(crate) struct DummyCache<K, V>(std::sync::Mutex<std::collections::HashMap<K, V>>);
166
167    impl<K: Hash + Eq, V: Clone> DummyCache<K, V> {
168        pub fn new() -> Self {
169            DummyCache(std::sync::Mutex::new(std::collections::HashMap::new()))
170        }
171    }
172
173    impl<K: Hash + Eq, V: Clone> ResolverCache<K, V> for DummyCache<K, V> {
174        fn get<Q>(&self, key: &Q) -> Option<V>
175        where
176            K: Borrow<Q>,
177            Q: Hash + Eq + ?Sized,
178        {
179            self.0.lock().unwrap().get(key).cloned()
180        }
181
182        fn remove<Q>(&self, key: &Q) -> Option<V>
183        where
184            K: Borrow<Q>,
185            Q: Hash + Eq + ?Sized,
186        {
187            self.0.lock().unwrap().remove(key)
188        }
189
190        fn insert(&self, key: K, value: V, _: std::time::Instant) {
191            self.0.lock().unwrap().insert(key, value);
192        }
193    }
194
195    pub(crate) struct DummyCaches {
196        pub txt: DummyCache<Box<str>, Txt>,
197        pub mx: DummyCache<Box<str>, Arc<[MX]>>,
198        pub ptr: DummyCache<IpAddr, Arc<[Box<str>]>>,
199        pub ipv4: DummyCache<Box<str>, Arc<[Ipv4Addr]>>,
200        pub ipv6: DummyCache<Box<str>, Arc<[Ipv6Addr]>>,
201    }
202
203    impl DummyCaches {
204        pub fn new() -> Self {
205            Self {
206                txt: DummyCache::new(),
207                mx: DummyCache::new(),
208                ptr: DummyCache::new(),
209                ipv4: DummyCache::new(),
210                ipv6: DummyCache::new(),
211            }
212        }
213
214        pub fn with_txt(
215            self,
216            name: impl ToFqdn,
217            value: impl Into<Txt>,
218            valid_until: std::time::Instant,
219        ) -> Self {
220            self.txt.insert(name.to_fqdn(), value.into(), valid_until);
221            self
222        }
223
224        pub fn txt_add(
225            &self,
226            name: impl ToFqdn,
227            value: impl Into<Txt>,
228            valid_until: std::time::Instant,
229        ) {
230            self.txt.insert(name.to_fqdn(), value.into(), valid_until);
231        }
232
233        pub fn ipv4_add(
234            &self,
235            name: impl ToFqdn,
236            value: Vec<Ipv4Addr>,
237            valid_until: std::time::Instant,
238        ) {
239            self.ipv4.insert(
240                name.to_fqdn(),
241                Arc::from(value.into_boxed_slice()),
242                valid_until,
243            );
244        }
245
246        pub fn ipv6_add(
247            &self,
248            name: impl ToFqdn,
249            value: Vec<Ipv6Addr>,
250            valid_until: std::time::Instant,
251        ) {
252            self.ipv6.insert(
253                name.to_fqdn(),
254                Arc::from(value.into_boxed_slice()),
255                valid_until,
256            );
257        }
258
259        pub fn ptr_add(&self, name: IpAddr, value: Vec<Box<str>>, valid_until: std::time::Instant) {
260            self.ptr
261                .insert(name, Arc::from(value.into_boxed_slice()), valid_until);
262        }
263
264        pub fn mx_add(&self, name: impl ToFqdn, value: Vec<MX>, valid_until: std::time::Instant) {
265            self.mx.insert(
266                name.to_fqdn(),
267                Arc::from(value.into_boxed_slice()),
268                valid_until,
269            );
270        }
271
272        #[allow(clippy::type_complexity)]
273        pub fn parameters<T>(
274            &self,
275            param: T,
276        ) -> Parameters<
277            '_,
278            T,
279            DummyCache<Box<str>, Txt>,
280            DummyCache<Box<str>, Arc<[MX]>>,
281            DummyCache<Box<str>, Arc<[Ipv4Addr]>>,
282            DummyCache<Box<str>, Arc<[Ipv6Addr]>>,
283            DummyCache<IpAddr, Arc<[Box<str>]>>,
284        > {
285            Parameters::new(param)
286                .with_txt_cache(&self.txt)
287                .with_mx_cache(&self.mx)
288                .with_ptr_cache(&self.ptr)
289                .with_ipv4_cache(&self.ipv4)
290                .with_ipv6_cache(&self.ipv6)
291        }
292    }
293}