cdns_rs/sync/
caches.rs

1/*-
2 * cdns-rs - a simple sync/async DNS query library
3 * 
4 * Copyright (C) 2020  Aleksandr Morozov
5 * 
6 * Copyright 2025 Aleksandr Morozov
7 * 
8 * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
9 * the European Commission - subsequent versions of the EUPL (the "Licence").
10 * 
11 * You may not use this work except in compliance with the Licence.
12 * 
13 * You may obtain a copy of the Licence at:
14 * 
15 *    https://joinup.ec.europa.eu/software/page/eupl
16 * 
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
19 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
20 * Licence for the specific language governing permissions and limitations
21 * under the Licence.
22 */
23
24/// This file contains configuration caches.
25
26use std::path::Path;
27use std::sync::{Arc, LazyLock, Mutex};
28use std::time::SystemTime;
29use std::fs;
30
31
32use crate::cfg_host_parser::HostConfig;
33use crate::cfg_resolv_parser::ResolveConfig;
34use crate::{write_error, internal_error, internal_error_map, error::*};
35
36use super::cfg_parsers::*;
37
38pub static CACHE: LazyLock<CachesController> = LazyLock::new(|| { CachesController::new() });
39
40
41pub trait CacheOperations
42{
43    fn is_reload_allowed(&self) -> bool;
44}
45
46#[derive(Clone, Debug)]
47struct CacheTime<'cache>
48{
49    path: &'cache Path,
50    last_modif: SystemTime,
51}
52
53impl<'cache> CacheTime<'cache>
54{
55    fn new(path: &'cache Path) -> CDnsResult<Self>
56    {
57        return Ok(Self { path: path, last_modif: Self::get_last_modified(path)? });
58    }
59
60    fn fake(path: &'cache Path) -> Self
61    {
62        return Self { path: path, last_modif: SystemTime::now() };
63    }
64
65    fn get_last_modified(path: &'cache Path) -> CDnsResult<SystemTime>
66    {
67        let metadata = fs::metadata(path).map_err(|e| 
68            internal_error_map!(CDnsErrorType::InternalError, "fs::metadata::modified() not supported on this platform: '{}'", e)
69        )?;
70
71        let last_modif = 
72            metadata.modified().map_err(|e| 
73                internal_error_map!(CDnsErrorType::InternalError, "fs::metadata::modified() not supported on this platform: '{}'", e)
74            )?;
75
76        return Ok(last_modif);
77    }
78
79    fn check_modified(&mut self) -> CDnsResult<bool>
80    {
81        // in case if get_last_modified() will return Err, return what was cached prevously
82        let last_modif = 
83            match Self::get_last_modified(self.path)
84            {
85                Ok(t) => t,
86                Err(e) => internal_error!(CDnsErrorType::InternalError, "{}", e),
87            };
88
89        return Ok(self.last_modif != last_modif);
90        /*{
91            // reload
92            match ResolveConfEntry::parse_resolve_cfg()
93            {
94                Ok(r) => 
95                    self.cache = Arc::new(r),
96                Err(e) =>
97                    write_error!("{}", e),
98            }
99             
100        }
101        
102        return;*/
103    }
104}
105
106#[derive(Clone, Debug)]
107pub struct CacheInstance<T: Default + ConfigParser<T> + CacheOperations>//, P: ConfigParser<T>>
108{
109    last_modif: CacheTime<'static>,
110    cache: Arc<T>,
111   // f: PhantomData<P>,
112}
113
114impl<T: Default + ConfigParser<T> + CacheOperations> Default for CacheInstance<T>
115{
116    fn default() -> Self 
117    {
118        return 
119            Self 
120            {
121                last_modif: CacheTime::fake(T::get_file_path()), 
122                cache: Arc::new(T::default()),
123              //  f: PhantomData,
124            };
125    }
126}
127
128impl<T: Default + ConfigParser<T> + CacheOperations> CacheInstance<T>
129{
130    fn new() -> CDnsResult<Self>
131    {
132        return Ok(
133            Self 
134            {
135                last_modif: CacheTime::new(T::get_file_path())?, 
136                cache: Arc::new(T::parse_config()?),
137                //f: PhantomData,
138            }
139        );
140    }
141
142    fn read_config() -> CDnsResult<Arc<T>>
143    {
144        return Ok(Arc::new(T::parse_config()?));
145    }
146
147    fn check_modified(&mut self) -> CDnsResult<()>
148    {
149        // in case if get_last_modified() will return Err, save what was cached prevously
150        if self.cache.is_reload_allowed() == false
151        {
152            // just return OK
153            return Ok(());
154        }
155
156        if self.last_modif.check_modified()? == true
157        {
158            // reload
159            self.cache = Self::read_config()?;
160        }
161        
162        return Ok(());
163    }
164
165    fn clone_cache(&self) -> Arc<T>
166    {
167        return self.cache.clone();
168    }
169}
170
171pub struct CachesController
172{
173    resolv_cache: Mutex<CacheInstance<ResolveConfig>>,
174    host_cache: Mutex<CacheInstance<HostConfig>>
175}
176
177unsafe impl Sync for CachesController{}
178unsafe impl Send for CachesController{}
179
180impl CachesController
181{
182    /// Spawns instance and reads the configuration.
183    fn new() -> Self
184    {
185        let resolve_cache_res = CacheInstance::<ResolveConfig>::new();
186
187        let host_cache_res = CacheInstance::<HostConfig>::new();
188
189        let resolve_cache = 
190            match resolve_cache_res
191            {
192                Ok(r) => r,
193                Err(e) =>
194                {
195                    write_error!("{}", e);
196
197                    CacheInstance::default()
198                }
199            };
200        
201        let host_cache = 
202            match host_cache_res
203            {
204                Ok(r) => r,
205                Err(e) =>
206                {
207                    write_error!("{}", e);
208
209                    CacheInstance::default()
210                }
211            };
212    
213
214        return 
215            CachesController
216            { 
217                resolv_cache: Mutex::new(resolve_cache),
218                host_cache: Mutex::new(host_cache),
219            };
220    }
221
222    pub 
223    fn is_resolve_cached(&self) -> bool
224    {
225        return !self.resolv_cache.lock().unwrap().cache.is_default();
226    }
227
228    pub 
229    fn is_host_cached(&self) -> bool
230    {
231        return !self.host_cache.lock().unwrap().cache.is_default();
232    }
233
234    fn try_cache_resolve(&self) -> CDnsResult<Arc<ResolveConfig>>
235    {
236        let mut mutx_resolv = self.resolv_cache.lock().unwrap();
237        mutx_resolv.check_modified()?;
238
239        return Ok(mutx_resolv.clone_cache());
240    }
241
242    /// Clones the [ResolveConfig] [Arc] (atomic reference counter) reference.
243    pub 
244    fn clone_resolve_list(&self) -> CDnsResult<Arc<ResolveConfig>>
245    {
246        // clone from cache and/or reload cache
247        match self.try_cache_resolve()
248        {
249            Ok(r) => return Ok(r),
250            Err(e) => 
251            {
252                write_error!("{}", e);
253
254                // try to read directly, this ignores all reload restrictions
255                return CacheInstance::<ResolveConfig>::read_config();
256            }
257        }
258    }
259
260    fn try_cache_host(&self) -> CDnsResult<Arc<HostConfig>>
261    {
262        let mut mutx_host = self.host_cache.lock().unwrap();
263        mutx_host.check_modified()?;
264
265        return Ok(mutx_host.clone_cache());
266    }
267
268    /// Clones the [HostConfig] [Arc] (atomic reference counter) reference.
269    pub 
270    fn clone_host_list(&self) -> CDnsResult<Arc<HostConfig>>
271    {
272        match self.try_cache_host()
273        {
274            Ok(r) => return Ok(r),
275            Err(e) =>
276            {
277                write_error!("{}", e);
278
279                // try to read directly, this ignores all reload restrictions
280                return CacheInstance::<HostConfig>::read_config();
281            }
282        }
283    }
284}
285
286
287#[test]
288fn test_init()
289{
290    let res = CACHE.clone_resolve_list();
291    assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
292
293    let res = res.unwrap();
294
295    println!("{:?}", res);
296
297    let res = CACHE.clone_host_list();
298    assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
299
300    let res = res.unwrap();
301
302    println!("{:?}", res);
303}