Skip to main content

falco_plugin/tables/vtable/
reader.rs

1use crate::error::last_error::LastError;
2use crate::tables::vtable::TableError;
3use crate::tables::vtable::TableError::BadVtable;
4use falco_plugin_api::{
5    ss_plugin_bool, ss_plugin_rc, ss_plugin_state_data, ss_plugin_table_entry_t,
6    ss_plugin_table_field_t, ss_plugin_table_iterator_func_t, ss_plugin_table_iterator_state_t,
7    ss_plugin_table_reader_vtable_ext, ss_plugin_table_t,
8};
9use std::marker::PhantomData;
10
11/// A vtable containing table read access methods
12///
13/// It's used as a token to prove you're allowed to read tables in a particular context.
14/// The default implementation is [`crate::tables::LazyTableReader`].
15pub trait TableReader: private::TableReaderImpl {}
16
17impl<T: private::TableReaderImpl> TableReader for T {}
18
19pub(crate) mod private {
20    use super::*;
21    pub trait TableReaderImpl {
22        type Error: std::error::Error + Send + Sync + 'static;
23
24        unsafe fn get_table_name(
25            &self,
26            t: *mut ss_plugin_table_t,
27        ) -> Result<*const ::std::os::raw::c_char, Self::Error>;
28
29        unsafe fn get_table_size(&self, t: *mut ss_plugin_table_t) -> Result<u64, Self::Error>;
30
31        unsafe fn get_table_entry(
32            &self,
33            t: *mut ss_plugin_table_t,
34            key: *const ss_plugin_state_data,
35        ) -> Result<*mut ss_plugin_table_entry_t, Self::Error>;
36
37        unsafe fn read_entry_field(
38            &self,
39            t: *mut ss_plugin_table_t,
40            e: *mut ss_plugin_table_entry_t,
41            f: *const ss_plugin_table_field_t,
42            out: *mut ss_plugin_state_data,
43        ) -> Result<ss_plugin_rc, Self::Error>;
44
45        fn release_table_entry_fn(
46            &self,
47        ) -> Option<unsafe extern "C" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t)>;
48
49        fn iterate_entries_fn(
50            &self,
51        ) -> Result<
52            unsafe extern "C" fn(
53                t: *mut ss_plugin_table_t,
54                it: ss_plugin_table_iterator_func_t,
55                s: *mut ss_plugin_table_iterator_state_t,
56            ) -> ss_plugin_bool,
57            Self::Error,
58        >;
59
60        fn last_error(&self) -> &LastError;
61    }
62}
63
64/// A TableReader that performs validation on demand
65///
66/// This has no overhead when not actively using tables, but after a few accesses
67/// the repeated null checks might add up
68#[derive(Debug)]
69pub struct LazyTableReader<'t> {
70    reader_ext: &'t ss_plugin_table_reader_vtable_ext,
71    pub(crate) last_error: LastError,
72}
73
74impl<'t> LazyTableReader<'t> {
75    pub(crate) fn new(
76        reader_ext: &'t ss_plugin_table_reader_vtable_ext,
77        last_error: LastError,
78    ) -> Self {
79        LazyTableReader {
80            reader_ext,
81            last_error,
82        }
83    }
84
85    /// Validate all vtable entries and skip further NULL checks
86    ///
87    /// This method validates all possible vtable methods to make future
88    /// table accesses faster. If your plugin method does more than a few
89    /// (say, 10) calls to methods that take a `TableReader`, it might be
90    /// faster to get a `ValidatedTableReader`
91    pub fn validate(&self) -> Result<ValidatedTableReader<'_>, TableError> {
92        Ok(ValidatedTableReader {
93            get_table_name: self
94                .reader_ext
95                .get_table_name
96                .ok_or(BadVtable("get_table_name"))?,
97            get_table_size: self
98                .reader_ext
99                .get_table_size
100                .ok_or(BadVtable("get_table_size"))?,
101            get_table_entry: self
102                .reader_ext
103                .get_table_entry
104                .ok_or(BadVtable("get_table_entry"))?,
105            read_entry_field: self
106                .reader_ext
107                .read_entry_field
108                .ok_or(BadVtable("read_entry_field"))?,
109            release_table_entry: self
110                .reader_ext
111                .release_table_entry
112                .ok_or(BadVtable("release_table_entry"))?,
113            iterate_entries: self
114                .reader_ext
115                .iterate_entries
116                .ok_or(BadVtable("iterate_entries"))?,
117            last_error: self.last_error.clone(),
118            lifetime: PhantomData,
119        })
120    }
121}
122
123impl private::TableReaderImpl for LazyTableReader<'_> {
124    type Error = TableError;
125
126    unsafe fn get_table_name(
127        &self,
128        t: *mut ss_plugin_table_t,
129    ) -> Result<*const ::std::os::raw::c_char, Self::Error> {
130        Ok(unsafe {
131            self.reader_ext
132                .get_table_name
133                .ok_or(BadVtable("get_table_name"))?(t)
134        })
135    }
136
137    unsafe fn get_table_size(&self, t: *mut ss_plugin_table_t) -> Result<u64, Self::Error> {
138        Ok(unsafe {
139            self.reader_ext
140                .get_table_size
141                .ok_or(BadVtable("get_table_size"))?(t)
142        })
143    }
144
145    unsafe fn get_table_entry(
146        &self,
147        t: *mut ss_plugin_table_t,
148        key: *const ss_plugin_state_data,
149    ) -> Result<*mut ss_plugin_table_entry_t, Self::Error> {
150        Ok(unsafe {
151            self.reader_ext
152                .get_table_entry
153                .ok_or(BadVtable("get_table_entry"))?(t, key)
154        })
155    }
156
157    unsafe fn read_entry_field(
158        &self,
159        t: *mut ss_plugin_table_t,
160        e: *mut ss_plugin_table_entry_t,
161        f: *const ss_plugin_table_field_t,
162        out: *mut ss_plugin_state_data,
163    ) -> Result<ss_plugin_rc, Self::Error> {
164        Ok(unsafe {
165            self.reader_ext
166                .read_entry_field
167                .ok_or(BadVtable("read_entry_field"))?(t, e, f, out)
168        })
169    }
170
171    fn release_table_entry_fn(
172        &self,
173    ) -> Option<unsafe extern "C" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t)>
174    {
175        self.reader_ext.release_table_entry
176    }
177
178    fn iterate_entries_fn(
179        &self,
180    ) -> Result<
181        unsafe extern "C" fn(
182            t: *mut ss_plugin_table_t,
183            it: ss_plugin_table_iterator_func_t,
184            s: *mut ss_plugin_table_iterator_state_t,
185        ) -> ss_plugin_bool,
186        TableError,
187    > {
188        self.reader_ext
189            .iterate_entries
190            .ok_or(BadVtable("iterate_entries"))
191    }
192
193    fn last_error(&self) -> &LastError {
194        &self.last_error
195    }
196}
197
198/// A TableReader that performs validation when created, with no subsequent checks
199///
200/// This implementation has some overhead when creating, but all subsequent table accesses
201/// should be ever so slightly faster due to skipped NULL checks.
202#[derive(Debug)]
203pub struct ValidatedTableReader<'t> {
204    pub(crate) get_table_name:
205        unsafe extern "C" fn(t: *mut ss_plugin_table_t) -> *const ::std::os::raw::c_char,
206    pub(crate) get_table_size: unsafe extern "C" fn(t: *mut ss_plugin_table_t) -> u64,
207    pub(crate) get_table_entry: unsafe extern "C" fn(
208        t: *mut ss_plugin_table_t,
209        key: *const ss_plugin_state_data,
210    ) -> *mut ss_plugin_table_entry_t,
211    pub(crate) read_entry_field: unsafe extern "C" fn(
212        t: *mut ss_plugin_table_t,
213        e: *mut ss_plugin_table_entry_t,
214        f: *const ss_plugin_table_field_t,
215        out: *mut ss_plugin_state_data,
216    ) -> ss_plugin_rc,
217    pub(crate) release_table_entry:
218        unsafe extern "C" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t),
219    pub(crate) iterate_entries: unsafe extern "C" fn(
220        t: *mut ss_plugin_table_t,
221        it: ss_plugin_table_iterator_func_t,
222        s: *mut ss_plugin_table_iterator_state_t,
223    ) -> ss_plugin_bool,
224
225    pub(crate) last_error: LastError,
226    lifetime: PhantomData<&'t ()>,
227}
228
229impl private::TableReaderImpl for ValidatedTableReader<'_> {
230    type Error = std::convert::Infallible;
231
232    unsafe fn get_table_name(
233        &self,
234        t: *mut ss_plugin_table_t,
235    ) -> Result<*const ::std::os::raw::c_char, Self::Error> {
236        unsafe { Ok((self.get_table_name)(t)) }
237    }
238
239    unsafe fn get_table_size(&self, t: *mut ss_plugin_table_t) -> Result<u64, Self::Error> {
240        unsafe { Ok((self.get_table_size)(t)) }
241    }
242
243    unsafe fn get_table_entry(
244        &self,
245        t: *mut ss_plugin_table_t,
246        key: *const ss_plugin_state_data,
247    ) -> Result<*mut ss_plugin_table_entry_t, Self::Error> {
248        unsafe { Ok((self.get_table_entry)(t, key)) }
249    }
250
251    unsafe fn read_entry_field(
252        &self,
253        t: *mut ss_plugin_table_t,
254        e: *mut ss_plugin_table_entry_t,
255        f: *const ss_plugin_table_field_t,
256        out: *mut ss_plugin_state_data,
257    ) -> Result<ss_plugin_rc, Self::Error> {
258        unsafe { Ok((self.read_entry_field)(t, e, f, out)) }
259    }
260
261    fn release_table_entry_fn(
262        &self,
263    ) -> Option<unsafe extern "C" fn(*mut ss_plugin_table_t, *mut ss_plugin_table_entry_t)> {
264        Some(self.release_table_entry)
265    }
266
267    fn iterate_entries_fn(
268        &self,
269    ) -> Result<
270        unsafe extern "C" fn(
271            *mut ss_plugin_table_t,
272            ss_plugin_table_iterator_func_t,
273            *mut ss_plugin_table_iterator_state_t,
274        ) -> ss_plugin_bool,
275        Self::Error,
276    > {
277        Ok(self.iterate_entries)
278    }
279
280    fn last_error(&self) -> &LastError {
281        &self.last_error
282    }
283}