pluginop_common/
lib.rs

1//! Sub-crate of `protocol-operation` containing structures needed for operations of both the
2//! host instance and the plugin ones.
3
4use std::{hash::Hash, net::SocketAddr, num::ParseIntError, time::Duration};
5
6use serde::{Deserialize, Serialize};
7use unix_time::Instant as UnixInstant;
8
9pub type PluginInputType = u32;
10pub type PluginOutputType = i64;
11pub type WASMPtr = u32;
12pub type WASMLen = u32;
13pub type APIResult = i64;
14
15/// The different conversion errors that may arise with plugin-processable structures.
16#[derive(Clone, Debug)]
17pub enum ConversionError {
18    InvalidBool,
19    InvalidI32,
20    InvalidI64,
21    InvalidU32,
22    InvalidU64,
23    InvalidF32,
24    InvalidF64,
25    InvalidUsize,
26    InvalidBytes,
27    InvalidDuration,
28    InvalidInstant,
29    InvalidFrame,
30    InvalidFrameParam,
31    InvalidHeader,
32    InvalidSentPacket,
33    InvalidSocketAddr,
34    InvalidQVal,
35}
36
37/// The actual plugin operations.
38///
39/// FIXME: move these protoops in their respective protocols.
40#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, PartialOrd)]
41pub enum PluginOp {
42    /// Operation that will always be called once the plugin got loaded.
43    Init,
44
45    /// Operation with no particular meaning, only for testing purposes.
46    Test,
47
48    /// Plugin control operation, unspecified protocol operations called by the application.
49    PluginControl(u64),
50
51    /// Specific protocol operation when a plugin triggers some timers.
52    OnPluginTimeout(u64),
53
54    /// Decode from the wire the QUIC transport parameter having the specified type.
55    DecodeTransportParameter(u64),
56    /// Write to the wire the QUIC transport parameter having the specified type.
57    WriteTransportParameter(u64),
58
59    /// Provide some textual logging of the frame with specified type.
60    LogFrame(u64),
61    /// Report whether the frame was successfully acknowledged or lost.
62    NotifyFrame(u64),
63    /// Callback event when the frame has been confirmed scheduling for the current packet.
64    OnFrameReserved(u64),
65    /// Converts a wire-format frame into a plugin-processable structure.
66    ParseFrame(u64),
67    /// Generate a plugin-processable structure representing the next frame to send.
68    PrepareFrame(u64),
69    /// Process, at receiver side, the plugin-processable structure.
70    ProcessFrame(u64),
71    /// Return whether a frame of the corresponding type should be scheduled for sending in
72    /// the next packet.
73    ShouldSendFrame(u64),
74    /// Return the length of the complete frame on the wire.
75    WireLen(u64),
76    /// From a plugin-processable structure, write the frame on the wire.
77    WriteFrame(u64),
78
79    #[doc(hidden)]
80    UpdateRtt,
81
82    /// For experimentation purposes.
83    Other([u8; 32]),
84}
85
86/// The different anchors where plugin bytecodes can be attached.
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88pub enum Anchor {
89    /// Execute just before calling the operation. Cannot modify the running context.
90    Before,
91    /// Execute in place of the operation. Can modify the running context.
92    Define,
93    /// Execute just after returning from the operation. Cannot modify the running context.
94    After,
95}
96
97impl Anchor {
98    /// Returns an index value for the Anchor.
99    pub fn index(&self) -> usize {
100        match self {
101            Anchor::Before => 0,
102            Anchor::Define => 1,
103            Anchor::After => 2,
104        }
105    }
106}
107
108fn extract_po_param(name: &str) -> Result<u64, ParseIntError> {
109    let end_num = name.rfind('_').map(|i| &name[i + 1..]).unwrap_or("");
110    u64::from_str_radix(end_num, 16)
111}
112
113impl PluginOp {
114    /// Convert a string into the corresponding protocol operation and anchor.
115    ///
116    /// FIXME find a more idiomatic way
117    pub fn from_name(name: &str) -> (PluginOp, Anchor) {
118        let (name, anchor) = if let Some(po_name) = name.strip_prefix("pre_") {
119            (po_name, Anchor::Before)
120        } else if let Some(po_name) = name.strip_prefix("before_") {
121            (po_name, Anchor::Before)
122        } else if let Some(po_name) = name.strip_prefix("post_") {
123            (po_name, Anchor::After)
124        } else if let Some(po_name) = name.strip_prefix("after_") {
125            (po_name, Anchor::After)
126        } else {
127            (name, Anchor::Define)
128        };
129
130        if name == "init" {
131            (PluginOp::Init, anchor)
132        } else if name.starts_with("decode_transport_parameter_") {
133            match extract_po_param(name) {
134                Ok(frame_type) => (PluginOp::DecodeTransportParameter(frame_type), anchor),
135                Err(_) => panic!("Invalid protocol operation name"),
136            }
137        } else if name.starts_with("write_transport_parameter_") {
138            match extract_po_param(name) {
139                Ok(frame_type) => (PluginOp::WriteTransportParameter(frame_type), anchor),
140                Err(_) => panic!("Invalid protocol operation name"),
141            }
142        } else if name.starts_with("log_frame_") {
143            match extract_po_param(name) {
144                Ok(frame_type) => (PluginOp::LogFrame(frame_type), anchor),
145                Err(_) => panic!("Invalid protocol operation name"),
146            }
147        } else if name.starts_with("notify_frame_") {
148            match extract_po_param(name) {
149                Ok(frame_type) => (PluginOp::NotifyFrame(frame_type), anchor),
150                Err(_) => panic!("Invalid protocol operation name"),
151            }
152        } else if name.starts_with("on_frame_reserved_") {
153            match extract_po_param(name) {
154                Ok(frame_type) => (PluginOp::OnFrameReserved(frame_type), anchor),
155                Err(_) => panic!("Invalid protocol operation name"),
156            }
157        } else if name.starts_with("parse_frame_") {
158            match extract_po_param(name) {
159                Ok(frame_type) => (PluginOp::ParseFrame(frame_type), anchor),
160                Err(_) => panic!("Invalid protocol operation name"),
161            }
162        } else if name.starts_with("prepare_frame_") {
163            match extract_po_param(name) {
164                Ok(frame_type) => (PluginOp::PrepareFrame(frame_type), anchor),
165                Err(_) => panic!("Invalid protocol operation name"),
166            }
167        } else if name.starts_with("process_frame_") {
168            match extract_po_param(name) {
169                Ok(frame_type) => (PluginOp::ProcessFrame(frame_type), anchor),
170                Err(_) => panic!("Invalid protocol operation name"),
171            }
172        } else if name.starts_with("should_send_frame_") {
173            match extract_po_param(name) {
174                Ok(frame_type) => (PluginOp::ShouldSendFrame(frame_type), anchor),
175                Err(_) => panic!("Invalid protocol operation name"),
176            }
177        } else if name.starts_with("wire_len_") {
178            match extract_po_param(name) {
179                Ok(frame_type) => (PluginOp::WireLen(frame_type), anchor),
180                Err(e) => panic!("Invalid protocol operation name: {e}"),
181            }
182        } else if name.starts_with("write_frame_") {
183            match extract_po_param(name) {
184                Ok(frame_type) => (PluginOp::WriteFrame(frame_type), anchor),
185                Err(e) => panic!("Invalid protocol operation name: {e}"),
186            }
187        } else if name.starts_with("plugin_control_") {
188            match extract_po_param(name) {
189                Ok(val) => (PluginOp::PluginControl(val), anchor),
190                Err(e) => panic!("Invalid protocol operation name: {e}"),
191            }
192        } else if name.starts_with("on_plugin_timeout_") {
193            match extract_po_param(name) {
194                Ok(val) => (PluginOp::OnPluginTimeout(val), anchor),
195                Err(e) => panic!("Invalid protocol operation name: {e}"),
196            }
197        } else if name == "update_rtt" {
198            (PluginOp::UpdateRtt, anchor)
199        } else {
200            let mut name_array = [0; 32];
201            name_array[..name.len()].copy_from_slice(name.as_bytes());
202            (PluginOp::Other(name_array), anchor)
203        }
204    }
205
206    /// Returns whether the plugin operation can be called, even if it is not fully
207    /// loaded.
208    pub fn always_enabled(&self) -> bool {
209        matches!(
210            self,
211            PluginOp::Init
212                | PluginOp::DecodeTransportParameter(_)
213                | PluginOp::WriteTransportParameter(_)
214        )
215    }
216}
217
218/// A type, implemented as an access token, providing a capability-based access to raw
219/// bytes between the host implementation and the plugin bytecode.
220#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, PartialOrd, Eq, Ord)]
221pub struct Bytes {
222    /// The tag to use to retrieve the associated data.
223    pub tag: u64,
224    /// The maximum number of bytes that can be fetched.
225    pub max_read_len: u64,
226    /// The maximum number of bytes that can be written.
227    pub max_write_len: u64,
228}
229
230/// Values used to communicate with underlying plugins, either as inputs or
231/// outputs.
232#[allow(clippy::large_enum_variant)]
233#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, PartialOrd)]
234pub enum PluginVal {
235    /// A boolean value.
236    Bool(bool),
237    /// A i32.
238    I32(i32),
239    /// A i64.
240    I64(i64),
241    /// A u32.
242    U32(u32),
243    /// A u64.
244    U64(u64),
245    /// A f32.
246    F32(f32),
247    /// A f64.
248    F64(f64),
249    /// A Usize, but encoded as a u64.
250    Usize(u64),
251    /// An access token to some raw bytes.
252    Bytes(Bytes),
253    /// A duration.
254    Duration(Duration),
255    /// A UNIX-based instant.
256    UNIXInstant(UnixInstant),
257    /// A socket address.
258    SocketAddr(SocketAddr),
259    /// QUIC specific inputs.
260    QUIC(quic::QVal),
261}
262
263macro_rules! impl_from_try_from {
264    ($e:ident, $v:ident, $t:ty, $err:ident, $verr:ident) => {
265        impl From<$t> for $e {
266            fn from(v: $t) -> Self {
267                $e::$v(v)
268            }
269        }
270
271        impl TryFrom<$e> for $t {
272            type Error = $err;
273
274            fn try_from(v: $e) -> Result<Self, Self::Error> {
275                match v {
276                    $e::$v(v) => Ok(v),
277                    _ => Err($err::$verr),
278                }
279            }
280        }
281    };
282}
283
284impl From<usize> for PluginVal {
285    fn from(value: usize) -> Self {
286        PluginVal::Usize(value as u64)
287    }
288}
289
290impl TryFrom<PluginVal> for usize {
291    type Error = ConversionError;
292
293    fn try_from(value: PluginVal) -> Result<Self, Self::Error> {
294        match value {
295            PluginVal::Usize(v) => Ok(v as Self),
296            _ => Err(ConversionError::InvalidUsize),
297        }
298    }
299}
300
301impl TryFrom<PluginVal> for () {
302    type Error = ConversionError;
303
304    fn try_from(_: PluginVal) -> Result<Self, Self::Error> {
305        Ok(())
306    }
307}
308
309impl_from_try_from!(PluginVal, Bool, bool, ConversionError, InvalidBool);
310impl_from_try_from!(PluginVal, I32, i32, ConversionError, InvalidI32);
311impl_from_try_from!(PluginVal, I64, i64, ConversionError, InvalidI64);
312impl_from_try_from!(PluginVal, U32, u32, ConversionError, InvalidU32);
313impl_from_try_from!(PluginVal, U64, u64, ConversionError, InvalidU64);
314impl_from_try_from!(PluginVal, F32, f32, ConversionError, InvalidF32);
315impl_from_try_from!(PluginVal, F64, f64, ConversionError, InvalidF64);
316impl_from_try_from!(PluginVal, Bytes, Bytes, ConversionError, InvalidBytes);
317impl_from_try_from!(
318    PluginVal,
319    Duration,
320    Duration,
321    ConversionError,
322    InvalidDuration
323);
324impl_from_try_from!(
325    PluginVal,
326    UNIXInstant,
327    UnixInstant,
328    ConversionError,
329    InvalidInstant
330);
331impl_from_try_from!(
332    PluginVal,
333    SocketAddr,
334    SocketAddr,
335    ConversionError,
336    InvalidSocketAddr
337);
338impl_from_try_from!(PluginVal, QUIC, quic::QVal, ConversionError, InvalidQVal);
339
340pub mod quic;