1use serde::{Serialize, Deserialize};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7pub enum CensorshipMethod {
8 IpBlocking,
10 PortBlocking,
12 Dpi,
14 Throttling,
16 DnsPoisoning,
18 TcpReset,
20 ActiveProbing,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct BlockingRule {
27 pub method: CensorshipMethod,
28 pub pattern: String,
30 pub description: String,
32 pub active: bool,
34}
35
36impl BlockingRule {
37 pub fn ip_block(ip_prefix: &str) -> Self {
38 Self {
39 method: CensorshipMethod::IpBlocking,
40 pattern: ip_prefix.to_string(),
41 description: format!("Block IP prefix {}", ip_prefix),
42 active: true,
43 }
44 }
45
46 pub fn port_block(port: u16) -> Self {
47 Self {
48 method: CensorshipMethod::PortBlocking,
49 pattern: port.to_string(),
50 description: format!("Block port {}", port),
51 active: true,
52 }
53 }
54
55 pub fn dpi_rule(signature: &str) -> Self {
56 Self {
57 method: CensorshipMethod::Dpi,
58 pattern: signature.to_string(),
59 description: format!("DPI detect: {}", signature),
60 active: true,
61 }
62 }
63
64 pub fn throttle_rule(pattern: &str) -> Self {
65 Self {
66 method: CensorshipMethod::Throttling,
67 pattern: pattern.to_string(),
68 description: format!("Throttle: {}", pattern),
69 active: true,
70 }
71 }
72}
73
74#[derive(Debug, Clone)]
76pub struct SimulatedPacket {
77 pub source_ip: String,
78 pub dest_ip: String,
79 pub source_port: u16,
80 pub dest_port: u16,
81 pub payload: Vec<u8>,
82 pub protocol: String,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq)]
87pub enum CensorResult {
88 Allowed,
90 Blocked(String),
92 Throttled(String),
94 Reset(String),
96}
97
98pub struct CensorshipSimulator {
100 rules: Vec<BlockingRule>,
101 packets_seen: u64,
103 packets_blocked: u64,
104 packets_throttled: u64,
105}
106
107impl CensorshipSimulator {
108 pub fn new() -> Self {
109 Self {
110 rules: Vec::new(),
111 packets_seen: 0,
112 packets_blocked: 0,
113 packets_throttled: 0,
114 }
115 }
116
117 pub fn with_standard_rules() -> Self {
119 let mut sim = Self::new();
120 sim.add_rule(BlockingRule::port_block(9001)); sim.add_rule(BlockingRule::port_block(443)); sim.add_rule(BlockingRule::dpi_rule("TLS_OBFS"));
125 sim.add_rule(BlockingRule::dpi_rule("SHADOW"));
126 sim.add_rule(BlockingRule::throttle_rule("VPN_PATTERN"));
128 sim
129 }
130
131 pub fn add_rule(&mut self, rule: BlockingRule) {
133 self.rules.push(rule);
134 }
135
136 pub fn clear_rules(&mut self) {
138 self.rules.clear();
139 }
140
141 pub fn check_packet(&mut self, packet: &SimulatedPacket) -> CensorResult {
143 self.packets_seen += 1;
144
145 for rule in &self.rules {
146 if !rule.active {
147 continue;
148 }
149
150 match rule.method {
151 CensorshipMethod::IpBlocking => {
152 if packet.dest_ip.starts_with(&rule.pattern)
153 || packet.source_ip.starts_with(&rule.pattern)
154 {
155 self.packets_blocked += 1;
156 return CensorResult::Blocked(rule.description.clone());
157 }
158 }
159 CensorshipMethod::PortBlocking => {
160 if let Ok(port) = rule.pattern.parse::<u16>() {
161 if packet.dest_port == port || packet.source_port == port {
162 self.packets_blocked += 1;
163 return CensorResult::Blocked(rule.description.clone());
164 }
165 }
166 }
167 CensorshipMethod::Dpi => {
168 let sig = rule.pattern.as_bytes();
170 if packet.payload.windows(sig.len()).any(|w| w == sig) {
171 self.packets_blocked += 1;
172 return CensorResult::Blocked(rule.description.clone());
173 }
174 if packet.protocol.contains(&rule.pattern) {
176 self.packets_blocked += 1;
177 return CensorResult::Blocked(rule.description.clone());
178 }
179 }
180 CensorshipMethod::Throttling => {
181 if packet.protocol.contains(&rule.pattern) {
182 self.packets_throttled += 1;
183 return CensorResult::Throttled(rule.description.clone());
184 }
185 }
186 CensorshipMethod::TcpReset => {
187 if packet.dest_ip.starts_with(&rule.pattern) {
188 self.packets_blocked += 1;
189 return CensorResult::Reset(rule.description.clone());
190 }
191 }
192 _ => {}
193 }
194 }
195
196 CensorResult::Allowed
197 }
198
199 pub fn batch_test(&mut self, packets: &[SimulatedPacket]) -> (usize, usize, usize) {
201 let mut allowed = 0;
202 let mut blocked = 0;
203 let mut throttled = 0;
204
205 for pkt in packets {
206 match self.check_packet(pkt) {
207 CensorResult::Allowed => allowed += 1,
208 CensorResult::Blocked(_) => blocked += 1,
209 CensorResult::Throttled(_) => throttled += 1,
210 CensorResult::Reset(_) => blocked += 1,
211 }
212 }
213
214 (allowed, blocked, throttled)
215 }
216
217 pub fn stats(&self) -> CensorStats {
219 CensorStats {
220 packets_seen: self.packets_seen,
221 packets_blocked: self.packets_blocked,
222 packets_throttled: self.packets_throttled,
223 rules_active: self.rules.iter().filter(|r| r.active).count(),
224 }
225 }
226
227 pub fn rule_count(&self) -> usize {
229 self.rules.len()
230 }
231}
232
233impl Default for CensorshipSimulator {
234 fn default() -> Self {
235 Self::new()
236 }
237}
238
239#[derive(Debug, Clone)]
241pub struct CensorStats {
242 pub packets_seen: u64,
243 pub packets_blocked: u64,
244 pub packets_throttled: u64,
245 pub rules_active: usize,
246}
247
248impl CensorStats {
249 pub fn block_rate(&self) -> f64 {
250 if self.packets_seen == 0 {
251 0.0
252 } else {
253 self.packets_blocked as f64 / self.packets_seen as f64
254 }
255 }
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 fn make_packet(dest_ip: &str, dest_port: u16, payload: &[u8], protocol: &str) -> SimulatedPacket {
263 SimulatedPacket {
264 source_ip: "10.0.0.1".into(),
265 dest_ip: dest_ip.into(),
266 source_port: 12345,
267 dest_port,
268 payload: payload.to_vec(),
269 protocol: protocol.into(),
270 }
271 }
272
273 #[test]
274 fn test_ip_blocking() {
275 let mut sim = CensorshipSimulator::new();
276 sim.add_rule(BlockingRule::ip_block("192.168.1"));
277
278 let blocked = make_packet("192.168.1.5", 80, b"hello", "TCP");
279 let allowed = make_packet("10.0.0.5", 80, b"hello", "TCP");
280
281 assert!(matches!(sim.check_packet(&blocked), CensorResult::Blocked(_)));
282 assert_eq!(sim.check_packet(&allowed), CensorResult::Allowed);
283 }
284
285 #[test]
286 fn test_port_blocking() {
287 let mut sim = CensorshipSimulator::new();
288 sim.add_rule(BlockingRule::port_block(9001));
289
290 let blocked = make_packet("10.0.0.5", 9001, b"data", "TCP");
291 let allowed = make_packet("10.0.0.5", 80, b"data", "TCP");
292
293 assert!(matches!(sim.check_packet(&blocked), CensorResult::Blocked(_)));
294 assert_eq!(sim.check_packet(&allowed), CensorResult::Allowed);
295 }
296
297 #[test]
298 fn test_dpi_detection() {
299 let mut sim = CensorshipSimulator::new();
300 sim.add_rule(BlockingRule::dpi_rule("SHADOW"));
301
302 let detected = make_packet("10.0.0.5", 443, b"xyzSHADOWabc", "TLS");
304 let clean = make_packet("10.0.0.5", 443, b"normal traffic", "TLS");
305
306 assert!(matches!(sim.check_packet(&detected), CensorResult::Blocked(_)));
307 assert_eq!(sim.check_packet(&clean), CensorResult::Allowed);
308 }
309
310 #[test]
311 fn test_throttling() {
312 let mut sim = CensorshipSimulator::new();
313 sim.add_rule(BlockingRule::throttle_rule("VPN"));
314
315 let vpn = make_packet("10.0.0.5", 1194, b"data", "VPN_TUNNEL");
316 let normal = make_packet("10.0.0.5", 80, b"data", "HTTP");
317
318 assert!(matches!(sim.check_packet(&vpn), CensorResult::Throttled(_)));
319 assert_eq!(sim.check_packet(&normal), CensorResult::Allowed);
320 }
321
322 #[test]
323 fn test_batch_statistics() {
324 let mut sim = CensorshipSimulator::with_standard_rules();
325
326 let packets = vec![
327 make_packet("10.0.0.5", 80, b"normal", "HTTP"),
328 make_packet("10.0.0.5", 9001, b"tor data", "TCP"),
329 make_packet("10.0.0.5", 8080, b"safe", "HTTP"),
330 ];
331
332 let (allowed, blocked, _throttled) = sim.batch_test(&packets);
333 assert_eq!(allowed, 2);
334 assert_eq!(blocked, 1);
335 }
336}