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