wirefilter

Форк
0
/
lib.rs 
517 строк · 13.7 Кб
1
pub mod transfer_types;
2

3
use crate::transfer_types::{
4
    ExternallyAllocatedByteArr, ExternallyAllocatedStr, RustAllocatedString, RustBox,
5
    StaticRustAllocatedString,
6
};
7
use fnv::FnvHasher;
8
use std::{
9
    hash::Hasher,
10
    io::{self, Write},
11
    net::IpAddr,
12
};
13
use wirefilter::{ExecutionContext, Filter, FilterAst, ParseError, Scheme, Type};
14

15
const VERSION: &str = env!("CARGO_PKG_VERSION");
16

17
#[repr(u8)]
18
pub enum ParsingResult<'s> {
19
    Err(RustAllocatedString),
20
    Ok(RustBox<FilterAst<'s>>),
21
}
22

23
impl<'s> From<FilterAst<'s>> for ParsingResult<'s> {
24
    fn from(filter_ast: FilterAst<'s>) -> Self {
25
        ParsingResult::Ok(filter_ast.into())
26
    }
27
}
28

29
impl<'s, 'a> From<ParseError<'a>> for ParsingResult<'s> {
30
    fn from(err: ParseError<'a>) -> Self {
31
        ParsingResult::Err(RustAllocatedString::from(err.to_string()))
32
    }
33
}
34

35
impl<'s> ParsingResult<'s> {
36
    pub fn unwrap(self) -> RustBox<FilterAst<'s>> {
37
        match self {
38
            ParsingResult::Err(err) => panic!("{}", &err as &str),
39
            ParsingResult::Ok(filter) => filter,
40
        }
41
    }
42
}
43

44
#[no_mangle]
45
pub extern "C" fn wirefilter_create_scheme() -> RustBox<Scheme> {
46
    Default::default()
47
}
48

49
#[no_mangle]
50
pub extern "C" fn wirefilter_free_scheme(scheme: RustBox<Scheme>) {
51
    drop(scheme);
52
}
53

54
#[no_mangle]
55
pub extern "C" fn wirefilter_add_type_field_to_scheme(
56
    scheme: &mut Scheme,
57
    name: ExternallyAllocatedStr<'_>,
58
    ty: Type,
59
) {
60
    scheme.add_field(name.into_ref().to_owned(), ty).unwrap();
61
}
62

63
#[no_mangle]
64
pub extern "C" fn wirefilter_free_parsed_filter(filter_ast: RustBox<FilterAst<'_>>) {
65
    drop(filter_ast);
66
}
67

68
#[no_mangle]
69
pub extern "C" fn wirefilter_free_string(s: RustAllocatedString) {
70
    drop(s);
71
}
72

73
#[no_mangle]
74
pub extern "C" fn wirefilter_parse_filter<'s, 'i>(
75
    scheme: &'s Scheme,
76
    input: ExternallyAllocatedStr<'i>,
77
) -> ParsingResult<'s> {
78
    match scheme.parse(input.into_ref()) {
79
        Ok(filter) => ParsingResult::from(filter),
80
        Err(err) => ParsingResult::from(err),
81
    }
82
}
83

84
#[no_mangle]
85
pub extern "C" fn wirefilter_free_parsing_result(r: ParsingResult<'_>) {
86
    drop(r);
87
}
88

89
/// Wrapper for Hasher that allows using Write API (e.g. with serializer).
90
#[derive(Default)]
91
struct HasherWrite<H: Hasher>(H);
92

93
impl<H: Hasher> Write for HasherWrite<H> {
94
    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
95
        self.0.write(buf);
96
        Ok(())
97
    }
98

99
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
100
        self.write_all(buf)?;
101
        Ok(buf.len())
102
    }
103

104
    fn flush(&mut self) -> io::Result<()> {
105
        Ok(())
106
    }
107
}
108

109
fn unwrap_json_result<T>(filter_ast: &FilterAst<'_>, result: serde_json::Result<T>) -> T {
110
    // Filter serialisation must never fail.
111
    result.unwrap_or_else(|err| panic!("{} while serializing filter {:#?}", err, filter_ast))
112
}
113

114
#[no_mangle]
115
pub extern "C" fn wirefilter_get_filter_hash(filter_ast: &FilterAst<'_>) -> u64 {
116
    let mut hasher = FnvHasher::default();
117
    // Serialize JSON to our Write-compatible wrapper around FnvHasher,
118
    // effectively calculating a hash for our filter in a streaming fashion
119
    // that is as stable as the JSON representation itself
120
    // (instead of relying on #[derive(Hash)] which would be tied to impl details).
121
    let result = serde_json::to_writer(HasherWrite(&mut hasher), filter_ast);
122
    unwrap_json_result(filter_ast, result);
123
    hasher.finish()
124
}
125

126
#[no_mangle]
127
pub extern "C" fn wirefilter_serialize_filter_to_json(
128
    filter_ast: &FilterAst<'_>,
129
) -> RustAllocatedString {
130
    let result = serde_json::to_string(filter_ast);
131
    unwrap_json_result(filter_ast, result).into()
132
}
133

134
#[no_mangle]
135
pub extern "C" fn wirefilter_create_execution_context<'e, 's: 'e>(
136
    scheme: &'s Scheme,
137
) -> RustBox<ExecutionContext<'e>> {
138
    ExecutionContext::new(scheme).into()
139
}
140

141
#[no_mangle]
142
pub extern "C" fn wirefilter_free_execution_context(exec_context: RustBox<ExecutionContext<'_>>) {
143
    drop(exec_context);
144
}
145

146
#[no_mangle]
147
pub extern "C" fn wirefilter_add_int_value_to_execution_context<'a>(
148
    exec_context: &mut ExecutionContext<'a>,
149
    name: ExternallyAllocatedStr<'_>,
150
    value: i32,
151
) {
152
    exec_context
153
        .set_field_value(name.into_ref(), value)
154
        .unwrap();
155
}
156

157
#[no_mangle]
158
pub extern "C" fn wirefilter_add_bytes_value_to_execution_context<'a>(
159
    exec_context: &mut ExecutionContext<'a>,
160
    name: ExternallyAllocatedStr<'_>,
161
    value: ExternallyAllocatedByteArr<'a>,
162
) {
163
    let slice: &[u8] = value.into_ref();
164
    exec_context
165
        .set_field_value(name.into_ref(), slice)
166
        .unwrap();
167
}
168

169
#[no_mangle]
170
pub extern "C" fn wirefilter_add_ipv6_value_to_execution_context(
171
    exec_context: &mut ExecutionContext<'_>,
172
    name: ExternallyAllocatedStr<'_>,
173
    value: &[u8; 16],
174
) {
175
    exec_context
176
        .set_field_value(name.into_ref(), IpAddr::from(*value))
177
        .unwrap();
178
}
179

180
#[no_mangle]
181
pub extern "C" fn wirefilter_add_ipv4_value_to_execution_context(
182
    exec_context: &mut ExecutionContext<'_>,
183
    name: ExternallyAllocatedStr<'_>,
184
    value: &[u8; 4],
185
) {
186
    exec_context
187
        .set_field_value(name.into_ref(), IpAddr::from(*value))
188
        .unwrap();
189
}
190

191
#[no_mangle]
192
pub extern "C" fn wirefilter_add_bool_value_to_execution_context(
193
    exec_context: &mut ExecutionContext<'_>,
194
    name: ExternallyAllocatedStr<'_>,
195
    value: bool,
196
) {
197
    exec_context
198
        .set_field_value(name.into_ref(), value)
199
        .unwrap();
200
}
201

202
#[no_mangle]
203
pub extern "C" fn wirefilter_compile_filter<'s>(
204
    filter_ast: RustBox<FilterAst<'s>>,
205
) -> RustBox<Filter<'s>> {
206
    let filter_ast = filter_ast.into_real_box();
207
    filter_ast.compile().into()
208
}
209

210
#[no_mangle]
211
pub extern "C" fn wirefilter_match<'s>(
212
    filter: &Filter<'s>,
213
    exec_context: &ExecutionContext<'s>,
214
) -> bool {
215
    filter.execute(exec_context).unwrap()
216
}
217

218
#[no_mangle]
219
pub extern "C" fn wirefilter_free_compiled_filter(filter: RustBox<Filter<'_>>) {
220
    drop(filter);
221
}
222

223
#[no_mangle]
224
pub extern "C" fn wirefilter_filter_uses(
225
    filter_ast: &FilterAst<'_>,
226
    field_name: ExternallyAllocatedStr<'_>,
227
) -> bool {
228
    filter_ast.uses(field_name.into_ref()).unwrap()
229
}
230

231
#[no_mangle]
232
pub extern "C" fn wirefilter_get_version() -> StaticRustAllocatedString {
233
    StaticRustAllocatedString::from(VERSION)
234
}
235

236
#[cfg(test)]
237
mod ffi_test {
238
    use super::*;
239
    use regex::Regex;
240

241
    fn create_scheme() -> RustBox<Scheme> {
242
        let mut scheme = wirefilter_create_scheme();
243

244
        wirefilter_add_type_field_to_scheme(
245
            &mut scheme,
246
            ExternallyAllocatedStr::from("ip1"),
247
            Type::Ip,
248
        );
249
        wirefilter_add_type_field_to_scheme(
250
            &mut scheme,
251
            ExternallyAllocatedStr::from("ip2"),
252
            Type::Ip,
253
        );
254

255
        wirefilter_add_type_field_to_scheme(
256
            &mut scheme,
257
            ExternallyAllocatedStr::from("str1"),
258
            Type::Bytes,
259
        );
260
        wirefilter_add_type_field_to_scheme(
261
            &mut scheme,
262
            ExternallyAllocatedStr::from("str2"),
263
            Type::Bytes,
264
        );
265

266
        wirefilter_add_type_field_to_scheme(
267
            &mut scheme,
268
            ExternallyAllocatedStr::from("num1"),
269
            Type::Int,
270
        );
271
        wirefilter_add_type_field_to_scheme(
272
            &mut scheme,
273
            ExternallyAllocatedStr::from("num2"),
274
            Type::Int,
275
        );
276

277
        scheme
278
    }
279

280
    fn create_execution_context<'e, 's: 'e>(scheme: &'s Scheme) -> RustBox<ExecutionContext<'e>> {
281
        let mut exec_context = wirefilter_create_execution_context(scheme);
282

283
        wirefilter_add_ipv4_value_to_execution_context(
284
            &mut exec_context,
285
            ExternallyAllocatedStr::from("ip1"),
286
            &[127, 0, 0, 1],
287
        );
288

289
        wirefilter_add_ipv6_value_to_execution_context(
290
            &mut exec_context,
291
            ExternallyAllocatedStr::from("ip2"),
292
            b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xC0\xA8\x00\x01",
293
        );
294

295
        wirefilter_add_bytes_value_to_execution_context(
296
            &mut exec_context,
297
            ExternallyAllocatedStr::from("str1"),
298
            ExternallyAllocatedByteArr::from("Hey"),
299
        );
300

301
        wirefilter_add_bytes_value_to_execution_context(
302
            &mut exec_context,
303
            ExternallyAllocatedStr::from("str2"),
304
            ExternallyAllocatedByteArr::from("yo123"),
305
        );
306

307
        wirefilter_add_int_value_to_execution_context(
308
            &mut exec_context,
309
            ExternallyAllocatedStr::from("num1"),
310
            42,
311
        );
312

313
        wirefilter_add_int_value_to_execution_context(
314
            &mut exec_context,
315
            ExternallyAllocatedStr::from("num2"),
316
            1337,
317
        );
318

319
        exec_context
320
    }
321

322
    fn parse_filter<'s>(scheme: &'s Scheme, input: &'static str) -> ParsingResult<'s> {
323
        wirefilter_parse_filter(scheme, ExternallyAllocatedStr::from(input))
324
    }
325

326
    fn match_filter(
327
        input: &'static str,
328
        scheme: &Scheme,
329
        exec_context: &ExecutionContext<'_>,
330
    ) -> bool {
331
        let filter = parse_filter(scheme, input).unwrap();
332
        let filter = wirefilter_compile_filter(filter);
333

334
        let result = wirefilter_match(&filter, exec_context);
335

336
        wirefilter_free_compiled_filter(filter);
337

338
        result
339
    }
340

341
    #[test]
342
    fn parse_error() {
343
        use indoc::indoc;
344

345
        let src = indoc!(
346
            r#"
347
            (
348
                num1 == 42
349
                or
350
                num1 == "abc"
351
            )
352
            "#
353
        );
354

355
        let scheme = create_scheme();
356

357
        {
358
            let result = parse_filter(&scheme, src);
359

360
            match result {
361
                ParsingResult::Ok(_) => panic!("Error expected"),
362
                ParsingResult::Err(err) => {
363
                    assert_eq!(
364
                        &err as &str,
365
                        indoc!(
366
                            r#"
367
                            Filter parsing error (4:13):
368
                                num1 == "abc"
369
                                        ^^^^^ expected digit
370
                            "#
371
                        )
372
                    );
373
                    wirefilter_free_string(err);
374
                }
375
            }
376
        }
377

378
        wirefilter_free_scheme(scheme);
379
    }
380

381
    #[test]
382
    fn filter_parsing() {
383
        let scheme = create_scheme();
384

385
        {
386
            let filter = parse_filter(&scheme, r#"num1 > 3 && str2 == "abc""#).unwrap();
387

388
            let json = wirefilter_serialize_filter_to_json(&filter);
389

390
            assert_eq!(
391
                &json as &str,
392
                r#"{"op":"And","items":[{"lhs":"num1","op":"GreaterThan","rhs":3},{"lhs":"str2","op":"Equal","rhs":"abc"}]}"#
393
            );
394

395
            wirefilter_free_string(json);
396

397
            wirefilter_free_parsed_filter(filter);
398
        }
399

400
        wirefilter_free_scheme(scheme);
401
    }
402

403
    #[test]
404
    fn filter_matching() {
405
        let scheme = create_scheme();
406

407
        {
408
            let exec_context = create_execution_context(&scheme);
409

410
            assert!(match_filter(
411
                r#"num1 > 41 && num2 == 1337 && ip1 != 192.168.0.1 && str2 ~ "yo\d+""#,
412
                &scheme,
413
                &exec_context
414
            ));
415

416
            assert!(match_filter(
417
                r#"ip2 == 0:0:0:0:0:ffff:c0a8:1 && (str1 == "Hey" || str2 == "ya")"#,
418
                &scheme,
419
                &exec_context
420
            ));
421

422
            assert!(!match_filter(
423
                "ip1 == 127.0.0.1 && ip2 == 0:0:0:0:0:ffff:c0a8:2",
424
                &scheme,
425
                &exec_context
426
            ));
427

428
            wirefilter_free_execution_context(exec_context);
429
        }
430

431
        wirefilter_free_scheme(scheme);
432
    }
433

434
    #[test]
435
    fn filter_hash() {
436
        let scheme = create_scheme();
437

438
        {
439
            let filter1 = parse_filter(
440
                &scheme,
441
                r#"num1 > 41 && num2 == 1337 && ip1 != 192.168.0.1 && str2 ~ "yo\d+""#,
442
            )
443
            .unwrap();
444

445
            let filter2 = parse_filter(
446
                &scheme,
447
                r#"num1 >     41 && num2 == 1337 &&    ip1 != 192.168.0.1 and str2 ~ "yo\d+""#,
448
            )
449
            .unwrap();
450

451
            let filter3 = parse_filter(&scheme, r#"num1 > 41 && num2 == 1337"#).unwrap();
452

453
            let hash1 = wirefilter_get_filter_hash(&filter1);
454
            let hash2 = wirefilter_get_filter_hash(&filter2);
455
            let hash3 = wirefilter_get_filter_hash(&filter3);
456

457
            assert_eq!(hash1, hash2);
458
            assert_ne!(hash2, hash3);
459

460
            wirefilter_free_parsed_filter(filter1);
461
            wirefilter_free_parsed_filter(filter2);
462
            wirefilter_free_parsed_filter(filter3);
463
        }
464

465
        wirefilter_free_scheme(scheme);
466
    }
467

468
    #[test]
469
    fn get_version() {
470
        let version = wirefilter_get_version();
471
        let re = Regex::new(r"(?-u)^\d+\.\d+\.\d+$").unwrap();
472

473
        assert!(re.is_match(version.into_ref()));
474
    }
475

476
    #[test]
477
    fn filter_uses() {
478
        let scheme = create_scheme();
479

480
        {
481
            let filter = parse_filter(
482
                &scheme,
483
                r#"num1 > 41 && num2 == 1337 && ip1 != 192.168.0.1 && str2 ~ "yo\d+""#,
484
            )
485
            .unwrap();
486

487
            assert!(wirefilter_filter_uses(
488
                &filter,
489
                ExternallyAllocatedStr::from("num1")
490
            ));
491

492
            assert!(wirefilter_filter_uses(
493
                &filter,
494
                ExternallyAllocatedStr::from("ip1")
495
            ));
496

497
            assert!(wirefilter_filter_uses(
498
                &filter,
499
                ExternallyAllocatedStr::from("str2")
500
            ));
501

502
            assert!(!wirefilter_filter_uses(
503
                &filter,
504
                ExternallyAllocatedStr::from("str1")
505
            ));
506

507
            assert!(!wirefilter_filter_uses(
508
                &filter,
509
                ExternallyAllocatedStr::from("ip2")
510
            ));
511

512
            wirefilter_free_parsed_filter(filter);
513
        }
514

515
        wirefilter_free_scheme(scheme);
516
    }
517
}
518

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.