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<String, Txt>,
43        NoCache<String, Arc<Vec<MX>>>,
44        NoCache<String, Arc<Vec<Ipv4Addr>>>,
45        NoCache<String, Arc<Vec<Ipv6Addr>>>,
46        NoCache<IpAddr, Arc<Vec<String>>>,
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<String, Txt>,
64    MXX: ResolverCache<String, Arc<Vec<MX>>>,
65    IPV4: ResolverCache<String, Arc<Vec<Ipv4Addr>>>,
66    IPV6: ResolverCache<String, Arc<Vec<Ipv6Addr>>>,
67    PTR: ResolverCache<IpAddr, Arc<Vec<String>>>,
68{
69    pub fn with_txt_cache<NewTXT: ResolverCache<String, 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<String, Arc<Vec<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<Vec<String>>>>(
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<String, Arc<Vec<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<String, Arc<Vec<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::IntoFqdn};
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<String, Txt>,
197        pub mx: DummyCache<String, Arc<Vec<MX>>>,
198        pub ptr: DummyCache<IpAddr, Arc<Vec<String>>>,
199        pub ipv4: DummyCache<String, Arc<Vec<Ipv4Addr>>>,
200        pub ipv6: DummyCache<String, Arc<Vec<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<'x>(
215            self,
216            name: impl IntoFqdn<'x>,
217            value: impl Into<Txt>,
218            valid_until: std::time::Instant,
219        ) -> Self {
220            self.txt
221                .insert(name.into_fqdn().into_owned(), value.into(), valid_until);
222            self
223        }
224
225        pub fn txt_add<'x>(
226            &self,
227            name: impl IntoFqdn<'x>,
228            value: impl Into<Txt>,
229            valid_until: std::time::Instant,
230        ) {
231            self.txt
232                .insert(name.into_fqdn().into_owned(), value.into(), valid_until);
233        }
234
235        pub fn ipv4_add<'x>(
236            &self,
237            name: impl IntoFqdn<'x>,
238            value: Vec<Ipv4Addr>,
239            valid_until: std::time::Instant,
240        ) {
241            self.ipv4
242                .insert(name.into_fqdn().into_owned(), Arc::new(value), valid_until);
243        }
244
245        pub fn ipv6_add<'x>(
246            &self,
247            name: impl IntoFqdn<'x>,
248            value: Vec<Ipv6Addr>,
249            valid_until: std::time::Instant,
250        ) {
251            self.ipv6
252                .insert(name.into_fqdn().into_owned(), Arc::new(value), valid_until);
253        }
254
255        pub fn ptr_add(&self, name: IpAddr, value: Vec<String>, valid_until: std::time::Instant) {
256            self.ptr.insert(name, Arc::new(value), valid_until);
257        }
258
259        pub fn mx_add<'x>(
260            &self,
261            name: impl IntoFqdn<'x>,
262            value: Vec<MX>,
263            valid_until: std::time::Instant,
264        ) {
265            self.mx
266                .insert(name.into_fqdn().into_owned(), Arc::new(value), valid_until);
267        }
268
269        #[allow(clippy::type_complexity)]
270        pub fn parameters<T>(
271            &self,
272            param: T,
273        ) -> Parameters<
274            '_,
275            T,
276            DummyCache<String, Txt>,
277            DummyCache<String, Arc<Vec<MX>>>,
278            DummyCache<String, Arc<Vec<Ipv4Addr>>>,
279            DummyCache<String, Arc<Vec<Ipv6Addr>>>,
280            DummyCache<IpAddr, Arc<Vec<String>>>,
281        > {
282            Parameters::new(param)
283                .with_txt_cache(&self.txt)
284                .with_mx_cache(&self.mx)
285                .with_ptr_cache(&self.ptr)
286                .with_ipv4_cache(&self.ipv4)
287                .with_ipv6_cache(&self.ipv6)
288        }
289    }
290}