1use core::{mem::MaybeUninit, str::FromStr, time::Duration};
2use std::time::Instant;
3
4use maybenot::{Framework, Machine, MachineId, TriggerEvent};
5
6mod error;
7pub use error::MaybenotResult;
8
9mod ffi;
10pub use ffi::*;
11use rand::rngs::{OsRng, ReseedingRng};
12
13pub struct MaybenotFramework {
19 framework: Framework<Vec<Machine>, Rng>,
20
21 events_buf: Vec<TriggerEvent>,
23}
24
25type Rng = ReseedingRng<rand_chacha::ChaCha12Core, OsRng>;
33const RNG_RESEED_THRESHOLD: u64 = 1024 * 64; #[repr(C)]
36#[derive(Debug, Clone, Copy)]
37pub struct MaybenotEvent {
38 pub event_type: MaybenotEventType,
39
40 pub machine: usize,
42}
43
44#[repr(C)]
45#[derive(Debug, Clone, Copy)]
46pub struct MaybenotDuration {
47 pub secs: u64,
49
50 pub nanos: u32,
52}
53
54#[repr(u32)]
55#[derive(Debug, Clone, Copy)]
56#[allow(dead_code)]
57pub enum MaybenotEventType {
58 NormalRecv = 0,
59 PaddingRecv = 1,
60 TunnelRecv = 2,
61
62 NormalSent = 3,
63 PaddingSent = 4,
64 TunnelSent = 5,
65
66 BlockingBegin = 6,
67 BlockingEnd = 7,
68
69 TimerBegin = 8,
70 TimerEnd = 9,
71}
72
73#[repr(C, u32)]
75#[derive(Debug, Clone, Copy)]
76pub enum MaybenotAction {
77 Cancel {
79 machine: usize,
81
82 timer: MaybenotTimer,
83 } = 0,
84
85 SendPadding {
87 machine: usize,
89
90 timeout: MaybenotDuration,
92
93 replace: bool,
94 bypass: bool,
95 } = 1,
96
97 BlockOutgoing {
99 machine: usize,
101
102 timeout: MaybenotDuration,
104
105 replace: bool,
106 bypass: bool,
107
108 duration: MaybenotDuration,
110 } = 2,
111
112 UpdateTimer {
114 machine: usize,
115
116 duration: MaybenotDuration,
117
118 replace: bool,
119 } = 3,
120}
121
122#[repr(u32)]
124#[derive(Debug, Clone, Copy)]
125#[allow(dead_code)]
126pub enum MaybenotTimer {
127 Action = 0,
129
130 Internal = 1,
132
133 All = 2,
135}
136
137impl MaybenotFramework {
138 fn start(
139 machines_str: &str,
140 max_padding_frac: f64,
141 max_blocking_frac: f64,
142 ) -> Result<Self, MaybenotResult> {
143 let machines: Vec<_> = machines_str
144 .lines()
145 .map(Machine::from_str)
146 .collect::<Result<_, _>>()
147 .map_err(|_e| MaybenotResult::InvalidMachineString)?;
148
149 let machines_count = machines.len();
150
151 let rng = Rng::new(RNG_RESEED_THRESHOLD, OsRng).unwrap();
152
153 let framework = Framework::new(
154 machines,
155 max_padding_frac,
156 max_blocking_frac,
157 Instant::now(),
158 rng,
159 )
160 .map_err(|_e| MaybenotResult::StartFramework)?;
161
162 Ok(MaybenotFramework {
163 framework,
164 events_buf: Vec::with_capacity(machines_count),
165 })
166 }
167
168 fn on_events(
169 &mut self,
170 events: &[MaybenotEvent],
171 actions: &mut [MaybeUninit<MaybenotAction>],
172 ) -> usize {
173 let now = Instant::now();
174
175 self.events_buf.clear();
177 for &event in events {
178 self.events_buf.push(convert_event(event));
179 }
180
181 self.framework
182 .trigger_events(&self.events_buf, now)
183 .map(convert_action)
185 .zip(actions.iter_mut())
188 .map(|(action, out)| out.write(action))
189 .count()
190 }
191}
192
193fn convert_action(action: &maybenot::TriggerAction) -> MaybenotAction {
195 match *action {
196 maybenot::TriggerAction::Cancel { machine, timer } => MaybenotAction::Cancel {
197 machine: machine.into_raw(),
198 timer: timer.into(),
199 },
200 maybenot::TriggerAction::SendPadding {
201 timeout,
202 bypass,
203 replace,
204 machine,
205 } => MaybenotAction::SendPadding {
206 timeout: timeout.into(),
207 replace,
208 bypass,
209 machine: machine.into_raw(),
210 },
211 maybenot::TriggerAction::BlockOutgoing {
212 timeout,
213 duration,
214 bypass,
215 replace,
216 machine,
217 } => MaybenotAction::BlockOutgoing {
218 timeout: timeout.into(),
219 duration: duration.into(),
220 replace,
221 bypass,
222 machine: machine.into_raw(),
223 },
224 maybenot::TriggerAction::UpdateTimer {
225 duration,
226 replace,
227 machine,
228 } => MaybenotAction::UpdateTimer {
229 duration: duration.into(),
230 replace,
231 machine: machine.into_raw(),
232 },
233 }
234}
235
236fn convert_event(event: MaybenotEvent) -> TriggerEvent {
237 let machine = MachineId::from_raw(event.machine);
238
239 match event.event_type {
240 MaybenotEventType::NormalRecv => TriggerEvent::NormalRecv,
241 MaybenotEventType::PaddingRecv => TriggerEvent::PaddingRecv,
242 MaybenotEventType::TunnelRecv => TriggerEvent::TunnelRecv,
243
244 MaybenotEventType::NormalSent => TriggerEvent::NormalSent,
245 MaybenotEventType::PaddingSent => TriggerEvent::PaddingSent { machine },
246 MaybenotEventType::TunnelSent => TriggerEvent::TunnelSent,
247
248 MaybenotEventType::BlockingBegin => TriggerEvent::BlockingBegin { machine },
249 MaybenotEventType::BlockingEnd => TriggerEvent::BlockingEnd,
250
251 MaybenotEventType::TimerBegin => TriggerEvent::TimerBegin { machine },
252 MaybenotEventType::TimerEnd => TriggerEvent::TimerEnd { machine },
253 }
254}
255
256impl From<Duration> for MaybenotDuration {
257 #[inline]
258 fn from(duration: Duration) -> Self {
259 MaybenotDuration {
260 secs: duration.as_secs(),
261 nanos: duration.subsec_nanos(),
262 }
263 }
264}
265
266impl From<maybenot::Timer> for MaybenotTimer {
267 fn from(timer: maybenot::Timer) -> Self {
268 match timer {
269 maybenot::Timer::Action => MaybenotTimer::Action,
270 maybenot::Timer::Internal => MaybenotTimer::Internal,
271 maybenot::Timer::All => MaybenotTimer::All,
272 }
273 }
274}