falco_plugin/plugin/tables/vtable/
mod.rs

1use crate::plugin::error::as_result::{AsResult, WithLastError};
2use crate::plugin::error::last_error::LastError;
3use crate::plugin::exported_tables::entry::table_metadata::traits::TableMetadata;
4use crate::plugin::exported_tables::entry::traits::Entry;
5use crate::plugin::exported_tables::table::Table;
6use crate::plugin::exported_tables::wrappers::{fields_vtable, reader_vtable, writer_vtable};
7use crate::plugin::tables::data::Key;
8use crate::plugin::tables::table::raw::RawTable;
9use crate::plugin::tables::traits::{TableAccess, TableMetadata as ImportedTableMetadata};
10use falco_plugin_api::{
11    ss_plugin_init_input, ss_plugin_owner_t, ss_plugin_rc, ss_plugin_state_type,
12    ss_plugin_table_fields_vtable, ss_plugin_table_info, ss_plugin_table_input,
13    ss_plugin_table_reader_vtable, ss_plugin_table_t, ss_plugin_table_writer_vtable,
14};
15use std::borrow::Borrow;
16use std::ffi::CStr;
17use thiserror::Error;
18
19pub mod fields;
20pub mod reader;
21pub mod writer;
22
23use crate::tables::LazyTableReader;
24use fields::TableFields;
25use writer::LazyTableWriter;
26
27#[derive(Error, Debug)]
28pub enum TableError {
29    #[error("Missing entry {0} in table operations vtable")]
30    BadVtable(&'static str),
31}
32
33#[derive(Debug)]
34/// An object containing table-related vtables
35///
36/// It's used as a token to prove you're allowed to read/write tables
37/// or manage their fields
38pub struct TablesInput<'t> {
39    owner: *mut ss_plugin_owner_t,
40    pub(in crate::plugin::tables) last_error: LastError,
41    pub(in crate::plugin::tables) list_tables:
42        unsafe extern "C-unwind" fn(
43            o: *mut ss_plugin_owner_t,
44            ntables: *mut u32,
45        ) -> *mut ss_plugin_table_info,
46    pub(in crate::plugin::tables) get_table: unsafe extern "C-unwind" fn(
47        o: *mut ss_plugin_owner_t,
48        name: *const ::std::os::raw::c_char,
49        key_type: ss_plugin_state_type,
50    )
51        -> *mut ss_plugin_table_t,
52    pub(in crate::plugin::tables) add_table: unsafe extern "C-unwind" fn(
53        o: *mut ss_plugin_owner_t,
54        in_: *const ss_plugin_table_input,
55    ) -> ss_plugin_rc,
56
57    /// accessor object for reading tables
58    pub(in crate::plugin::tables) reader_ext: LazyTableReader<'t>,
59
60    /// accessor object for writing tables
61    pub(in crate::plugin::tables) writer_ext: LazyTableWriter<'t>,
62
63    /// accessor object for manipulating fields
64    pub(in crate::plugin::tables) fields_ext: TableFields<'t>,
65}
66
67impl TablesInput<'_> {
68    pub(crate) fn try_from(value: &ss_plugin_init_input) -> Result<Option<Self>, TableError> {
69        if let Some(table_init_input) = unsafe { value.tables.as_ref() } {
70            let reader_ext = unsafe {
71                table_init_input
72                    .reader_ext
73                    .as_ref()
74                    .ok_or(TableError::BadVtable("reader_ext"))?
75            };
76            let writer_ext = unsafe {
77                table_init_input
78                    .writer_ext
79                    .as_ref()
80                    .ok_or(TableError::BadVtable("writer_ext"))?
81            };
82            let fields_ext = unsafe {
83                table_init_input
84                    .fields_ext
85                    .as_ref()
86                    .ok_or(TableError::BadVtable("fields_ext"))?
87            };
88
89            let get_owner_last_error = value
90                .get_owner_last_error
91                .ok_or(TableError::BadVtable("get_owner_last_error"))?;
92            let last_error = unsafe { LastError::new(value.owner, get_owner_last_error) };
93
94            Ok(Some(TablesInput {
95                owner: value.owner,
96                last_error: last_error.clone(),
97                list_tables: table_init_input
98                    .list_tables
99                    .ok_or(TableError::BadVtable("list_tables"))?,
100                get_table: table_init_input
101                    .get_table
102                    .ok_or(TableError::BadVtable("get_table"))?,
103                add_table: table_init_input
104                    .add_table
105                    .ok_or(TableError::BadVtable("add_table"))?,
106                reader_ext: LazyTableReader::new(reader_ext, last_error.clone()),
107                writer_ext: LazyTableWriter::try_from(writer_ext, last_error)?,
108                fields_ext: TableFields::try_from(fields_ext)?,
109            }))
110        } else {
111            Ok(None)
112        }
113    }
114}
115
116impl TablesInput<'_> {
117    /// # List the available tables
118    ///
119    /// **Note**: this method is of limited utility in actual plugin code (you know the tables you
120    /// want to access), so it returns the unmodified structure from the plugin API, including
121    /// raw pointers to C-style strings. This may change later.
122    pub fn list_tables(&self) -> &[ss_plugin_table_info] {
123        let mut num_tables = 0u32;
124        let tables = unsafe { (self.list_tables)(self.owner, &mut num_tables as *mut _) };
125        if tables.is_null() {
126            &[]
127        } else {
128            unsafe { std::slice::from_raw_parts(tables, num_tables as usize) }
129        }
130    }
131
132    /// # Import a table from the Falco plugin API
133    ///
134    /// The key type is verified by the plugin API, so this method will return
135    /// an error on mismatch
136    pub fn get_table<T, K>(&self, name: &CStr) -> Result<T, anyhow::Error>
137    where
138        T: TableAccess<Key = K>,
139        K: Key,
140    {
141        let table = unsafe {
142            (self.get_table)(
143                self.owner,
144                name.as_ptr().cast(),
145                K::TYPE_ID as ss_plugin_state_type,
146            )
147        };
148        if table.is_null() {
149            Err(anyhow::anyhow!("Could not get table {:?}", name)).with_last_error(&self.last_error)
150        } else {
151            // Safety: we pass the data directly from FFI, the framework would never lie to us, right?
152            let table = RawTable { table };
153            let metadata = T::Metadata::new(&table, self)?;
154            Ok(T::new(table, metadata, false))
155        }
156    }
157
158    /// # Export a table to the Falco plugin API
159    ///
160    /// This method returns a Box, which you need to store in your plugin instance
161    /// even if you don't intend to use the table yourself (the table is destroyed when
162    /// going out of scope, which will lead to crashes in plugins using your table).
163    pub fn add_table<K, E>(&self, table: Table<K, E>) -> Result<Box<Table<K, E>>, anyhow::Error>
164    where
165        K: Key + Ord,
166        K: Borrow<<K as Key>::Borrowed>,
167        <K as Key>::Borrowed: Ord + ToOwned<Owned = K>,
168        E: Entry,
169        E::Metadata: TableMetadata,
170    {
171        let mut reader_vtable_ext = reader_vtable::<K, E>();
172        let mut writer_vtable_ext = writer_vtable::<K, E>();
173        let mut fields_vtable_ext = fields_vtable::<K, E>();
174
175        let mut table = Box::new(table);
176        let table_ptr = table.as_mut() as *mut Table<K, E>;
177
178        // Note: we lend the ss_plugin_table_input to the FFI api and do not need
179        // to hold on to it (everything is copied out), but the name field is copied
180        // as a pointer, so the name we receive must be a 'static ref
181        let table_input = ss_plugin_table_input {
182            name: table.name().as_ptr(),
183            key_type: K::TYPE_ID as ss_plugin_state_type,
184            table: table_ptr.cast(),
185            reader: ss_plugin_table_reader_vtable {
186                get_table_name: reader_vtable_ext.get_table_name,
187                get_table_size: reader_vtable_ext.get_table_size,
188                get_table_entry: reader_vtable_ext.get_table_entry,
189                read_entry_field: reader_vtable_ext.read_entry_field,
190            },
191            writer: ss_plugin_table_writer_vtable {
192                clear_table: writer_vtable_ext.clear_table,
193                erase_table_entry: writer_vtable_ext.erase_table_entry,
194                create_table_entry: writer_vtable_ext.create_table_entry,
195                destroy_table_entry: writer_vtable_ext.destroy_table_entry,
196                add_table_entry: writer_vtable_ext.add_table_entry,
197                write_entry_field: writer_vtable_ext.write_entry_field,
198            },
199            fields: ss_plugin_table_fields_vtable {
200                list_table_fields: fields_vtable_ext.list_table_fields,
201                get_table_field: fields_vtable_ext.get_table_field,
202                add_table_field: fields_vtable_ext.add_table_field,
203            },
204            reader_ext: &mut reader_vtable_ext as *mut _,
205            writer_ext: &mut writer_vtable_ext as *mut _,
206            fields_ext: &mut fields_vtable_ext as *mut _,
207        };
208
209        unsafe { (self.add_table)(self.owner, &table_input as *const _) }
210            .as_result()
211            .with_last_error(&self.last_error)?;
212        Ok(table)
213    }
214}