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, RecordSet, ResolverCache, Txt};
8use std::{
9    borrow::Borrow,
10    hash::Hash,
11    marker::PhantomData,
12    net::{IpAddr, Ipv4Addr, Ipv6Addr},
13};
14
15pub struct NoCache<K, V>(PhantomData<(K, V)>);
16
17impl<K, V> ResolverCache<K, V> for NoCache<K, V> {
18    fn get<Q>(&self, _: &Q) -> Option<V>
19    where
20        K: Borrow<Q>,
21        Q: Hash + Eq + ?Sized,
22    {
23        None
24    }
25
26    fn remove<Q>(&self, _: &Q) -> Option<V>
27    where
28        K: Borrow<Q>,
29        Q: Hash + Eq + ?Sized,
30    {
31        None
32    }
33
34    fn insert(&self, _: K, _: V, _: crate::Instant) {}
35}
36
37impl<P>
38    Parameters<
39        '_,
40        P,
41        NoCache<Box<str>, Txt>,
42        NoCache<Box<str>, RecordSet<MX>>,
43        NoCache<Box<str>, RecordSet<Ipv4Addr>>,
44        NoCache<Box<str>, RecordSet<Ipv6Addr>>,
45        NoCache<IpAddr, RecordSet<Box<str>>>,
46    >
47{
48    pub fn new(params: P) -> Self {
49        Parameters {
50            params,
51            cache_txt: None,
52            cache_mx: None,
53            cache_ptr: None,
54            cache_ipv4: None,
55            cache_ipv6: None,
56        }
57    }
58}
59
60impl<'x, P, TXT, MXX, IPV4, IPV6, PTR> Parameters<'x, P, TXT, MXX, IPV4, IPV6, PTR>
61where
62    TXT: ResolverCache<Box<str>, Txt>,
63    MXX: ResolverCache<Box<str>, RecordSet<MX>>,
64    IPV4: ResolverCache<Box<str>, RecordSet<Ipv4Addr>>,
65    IPV6: ResolverCache<Box<str>, RecordSet<Ipv6Addr>>,
66    PTR: ResolverCache<IpAddr, RecordSet<Box<str>>>,
67{
68    pub fn with_txt_cache<NewTXT: ResolverCache<Box<str>, Txt>>(
69        self,
70        cache: &'x NewTXT,
71    ) -> Parameters<'x, P, NewTXT, MXX, IPV4, IPV6, PTR> {
72        Parameters {
73            params: self.params,
74            cache_txt: Some(cache),
75            cache_mx: self.cache_mx,
76            cache_ptr: self.cache_ptr,
77            cache_ipv4: self.cache_ipv4,
78            cache_ipv6: self.cache_ipv6,
79        }
80    }
81
82    pub fn with_mx_cache<NewMX: ResolverCache<Box<str>, RecordSet<MX>>>(
83        self,
84        cache: &'x NewMX,
85    ) -> Parameters<'x, P, TXT, NewMX, IPV4, IPV6, PTR> {
86        Parameters {
87            params: self.params,
88            cache_txt: self.cache_txt,
89            cache_mx: Some(cache),
90            cache_ptr: self.cache_ptr,
91            cache_ipv4: self.cache_ipv4,
92            cache_ipv6: self.cache_ipv6,
93        }
94    }
95
96    pub fn with_ptr_cache<NewPTR: ResolverCache<IpAddr, RecordSet<Box<str>>>>(
97        self,
98        cache: &'x NewPTR,
99    ) -> Parameters<'x, P, TXT, MXX, IPV4, IPV6, NewPTR> {
100        Parameters {
101            params: self.params,
102            cache_txt: self.cache_txt,
103            cache_mx: self.cache_mx,
104            cache_ptr: Some(cache),
105            cache_ipv4: self.cache_ipv4,
106            cache_ipv6: self.cache_ipv6,
107        }
108    }
109
110    pub fn with_ipv4_cache<NewIPV4: ResolverCache<Box<str>, RecordSet<Ipv4Addr>>>(
111        self,
112        cache: &'x NewIPV4,
113    ) -> Parameters<'x, P, TXT, MXX, NewIPV4, IPV6, PTR> {
114        Parameters {
115            params: self.params,
116            cache_txt: self.cache_txt,
117            cache_mx: self.cache_mx,
118            cache_ptr: self.cache_ptr,
119            cache_ipv4: Some(cache),
120            cache_ipv6: self.cache_ipv6,
121        }
122    }
123
124    pub fn with_ipv6_cache<NewIPV6: ResolverCache<Box<str>, RecordSet<Ipv6Addr>>>(
125        self,
126        cache: &'x NewIPV6,
127    ) -> Parameters<'x, P, TXT, MXX, IPV4, NewIPV6, PTR> {
128        Parameters {
129            params: self.params,
130            cache_txt: self.cache_txt,
131            cache_mx: self.cache_mx,
132            cache_ptr: self.cache_ptr,
133            cache_ipv4: self.cache_ipv4,
134            cache_ipv6: Some(cache),
135        }
136    }
137
138    pub fn clone_with<NewP>(
139        &self,
140        params: NewP,
141    ) -> Parameters<'x, NewP, TXT, MXX, IPV4, IPV6, PTR> {
142        Parameters {
143            params,
144            cache_txt: self.cache_txt,
145            cache_mx: self.cache_mx,
146            cache_ptr: self.cache_ptr,
147            cache_ipv4: self.cache_ipv4,
148            cache_ipv6: self.cache_ipv6,
149        }
150    }
151}
152
153#[cfg(test)]
154pub mod test {
155    use crate::{
156        DnssecStatus, MX, Parameters, RecordSet, ResolverCache, Txt, common::resolver::ToFqdn,
157    };
158    use std::{
159        borrow::Borrow,
160        hash::Hash,
161        net::{IpAddr, Ipv4Addr, Ipv6Addr},
162        sync::Arc,
163    };
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>, RecordSet<MX>>,
198        pub ptr: DummyCache<IpAddr, RecordSet<Box<str>>>,
199        pub ipv4: DummyCache<Box<str>, RecordSet<Ipv4Addr>>,
200        pub ipv6: DummyCache<Box<str>, RecordSet<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                RecordSet {
242                    rrset: Arc::from(value.into_boxed_slice()),
243                    dnssec_status: DnssecStatus::Indeterminate,
244                },
245                valid_until,
246            );
247        }
248
249        pub fn ipv6_add(
250            &self,
251            name: impl ToFqdn,
252            value: Vec<Ipv6Addr>,
253            valid_until: std::time::Instant,
254        ) {
255            self.ipv6.insert(
256                name.to_fqdn(),
257                RecordSet {
258                    rrset: Arc::from(value.into_boxed_slice()),
259                    dnssec_status: DnssecStatus::Indeterminate,
260                },
261                valid_until,
262            );
263        }
264
265        pub fn ptr_add(&self, name: IpAddr, value: Vec<Box<str>>, valid_until: std::time::Instant) {
266            self.ptr.insert(
267                name,
268                RecordSet {
269                    rrset: Arc::from(value.into_boxed_slice()),
270                    dnssec_status: DnssecStatus::Indeterminate,
271                },
272                valid_until,
273            );
274        }
275
276        pub fn mx_add(&self, name: impl ToFqdn, value: Vec<MX>, valid_until: std::time::Instant) {
277            self.mx.insert(
278                name.to_fqdn(),
279                RecordSet {
280                    rrset: Arc::from(value.into_boxed_slice()),
281                    dnssec_status: DnssecStatus::Indeterminate,
282                },
283                valid_until,
284            );
285        }
286
287        #[allow(clippy::type_complexity)]
288        pub fn parameters<T>(
289            &self,
290            param: T,
291        ) -> Parameters<
292            '_,
293            T,
294            DummyCache<Box<str>, Txt>,
295            DummyCache<Box<str>, RecordSet<MX>>,
296            DummyCache<Box<str>, RecordSet<Ipv4Addr>>,
297            DummyCache<Box<str>, RecordSet<Ipv6Addr>>,
298            DummyCache<IpAddr, RecordSet<Box<str>>>,
299        > {
300            Parameters::new(param)
301                .with_txt_cache(&self.txt)
302                .with_mx_cache(&self.mx)
303                .with_ptr_cache(&self.ptr)
304                .with_ipv4_cache(&self.ipv4)
305                .with_ipv6_cache(&self.ipv6)
306        }
307    }
308}