From 671feb59796c33206381ae9a1df485f27de3d956 Mon Sep 17 00:00:00 2001 From: marmeladema Date: Wed, 27 Mar 2019 10:58:09 +0000 Subject: [PATCH] FW-1087: provide wirefilter.h C header file and add ffi-ctests crate This new crate is here to provide a set of C based tests that will serve two purposes: * Provide some examples how to use the FFI bindings * Test that those bindings are actually working as intended Internally, it relies on ffi-ctests/ctests/tests.c file which contains tests written in C and that is compiled at cargo configuration time through the use of a build.rs file. This produces a wirefilter_ffi_ctests.so shared library that is later used in the ffi-ctests/ctests/src/lib.rs file to call the different tests functions. All of this is done in order to try to integrate somehow properly with cargo test. --- Cargo.lock | 17 ++ Cargo.toml | 1 + ffi-ctests/Cargo.toml | 12 ++ ffi-ctests/build.rs | 8 + ffi-ctests/ctests/CMakeLists.txt | 13 ++ ffi-ctests/ctests/tests.c | 270 +++++++++++++++++++++++++++++++ ffi-ctests/src/lib.rs | 30 ++++ ffi/include/wirefilter.h | 122 ++++++++++++++ ffi/src/lib.rs | 5 + 9 files changed, 478 insertions(+) create mode 100644 ffi-ctests/Cargo.toml create mode 100644 ffi-ctests/build.rs create mode 100644 ffi-ctests/ctests/CMakeLists.txt create mode 100644 ffi-ctests/ctests/tests.c create mode 100644 ffi-ctests/src/lib.rs create mode 100644 ffi/include/wirefilter.h diff --git a/Cargo.lock b/Cargo.lock index 6160d576..08e1038c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -110,6 +110,14 @@ dependencies = [ "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "cmake" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "criterion" version = "0.2.5" @@ -776,6 +784,14 @@ dependencies = [ "wirefilter-engine 0.6.1", ] +[[package]] +name = "wirefilter-ffi-ctests" +version = "0.1.0" +dependencies = [ + "cmake 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "wirefilter-ffi 0.6.1", +] + [[package]] name = "wirefilter-wasm" version = "0.6.1" @@ -800,6 +816,7 @@ dependencies = [ "checksum chrono 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e48d85528df61dc964aa43c5f6ca681a19cfa74939b2348d204bd08a981f2fb0" "checksum cidr 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2da1cf0f275bb8dc1867a7f40cdb3b746951db73a183048e6e37fa89ed81bd01" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" +"checksum cmake 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "6ec65ee4f9c9d16f335091d23693457ed4928657ba4982289d7fafee03bc614a" "checksum criterion 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c47d2b548c5647e1a436dc0cb78d4ebf51b6bf7ab101ed76662828bdd4d3a24a" "checksum criterion-plot 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6e649d6aacdbbdb94ec659561a309a71336fc5655ed408f3afd28df2fc0c4f4f" "checksum criterion-stats 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ff43cac80562f91ead0b617c1be74edf350adfaa195809d355de98dfc8f9237d" diff --git a/Cargo.toml b/Cargo.toml index 469c4de3..9fbc4c56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "engine", "ffi", + "ffi-ctests", "wasm", ] diff --git a/ffi-ctests/Cargo.toml b/ffi-ctests/Cargo.toml new file mode 100644 index 00000000..5c702901 --- /dev/null +++ b/ffi-ctests/Cargo.toml @@ -0,0 +1,12 @@ +[package] +authors = ["Elie ROUDNINSKI "] +name = "wirefilter-ffi-ctests" +version = "0.1.0" +description = "C based tests for FFI bindings of the Wirefilter engine" +publish = false + +[dependencies] + +[build-dependencies] +cmake = "0.1" +wirefilter-ffi = {"path"= "../ffi"} diff --git a/ffi-ctests/build.rs b/ffi-ctests/build.rs new file mode 100644 index 00000000..b97e4626 --- /dev/null +++ b/ffi-ctests/build.rs @@ -0,0 +1,8 @@ +extern crate cmake; + +fn main() { + let dst = cmake::build("ctests"); + + println!("cargo:rustc-link-search=native={}/lib", dst.display()); + println!("cargo:rustc-link-lib=dylib=wirefilter_ffi_ctests"); +} diff --git a/ffi-ctests/ctests/CMakeLists.txt b/ffi-ctests/ctests/CMakeLists.txt new file mode 100644 index 00000000..c88428ee --- /dev/null +++ b/ffi-ctests/ctests/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.2) +project(wirefilter-ffi-ctests) + +include_directories($ENV{CARGO_MANIFEST_DIR}/../ffi/include) +link_directories($ENV{OUT_DIR}/../../../deps) + +add_library(wirefilter_ffi_ctests SHARED tests.c) +target_link_libraries(wirefilter_ffi_ctests wirefilter_ffi) + +install( + TARGETS wirefilter_ffi_ctests + LIBRARY DESTINATION lib +) diff --git a/ffi-ctests/ctests/tests.c b/ffi-ctests/ctests/tests.c new file mode 100644 index 00000000..405bf6b8 --- /dev/null +++ b/ffi-ctests/ctests/tests.c @@ -0,0 +1,270 @@ +#include +#include +#include + +#include + +#define WIREFILTER_STRING(v, s) {(v).data = (const unsigned char *)(s); (v).length = strlen(s);} + +bool test_wirefilter_ffi_01() { + wirefilter_Scheme *scheme = wirefilter_create_scheme(); + if(!scheme) { + return false; + } + wirefilter_free_scheme(scheme); + return true; +} + +bool test_wirefilter_ffi_02() { + wirefilter_Scheme *scheme = wirefilter_create_scheme(); + if(!scheme) { + return false; + } + + wirefilter_ExternallyAllocatedStr field; + + WIREFILTER_STRING(field, "http.host"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Bytes); + WIREFILTER_STRING(field, "ip.addr"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Ip); + WIREFILTER_STRING(field, "ssl"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Bool); + WIREFILTER_STRING(field, "tcp.port"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Int); + + wirefilter_free_scheme(scheme); + return true; +} + +bool test_wirefilter_ffi_03() { + wirefilter_Scheme *scheme = wirefilter_create_scheme(); + if(!scheme) { + return false; + } + + wirefilter_ExternallyAllocatedStr field, filter_str; + + WIREFILTER_STRING(field, "http.host"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Bytes); + WIREFILTER_STRING(field, "ip.addr"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Ip); + WIREFILTER_STRING(field, "ssl"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Bool); + WIREFILTER_STRING(field, "tcp.port"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Int); + + WIREFILTER_STRING(filter_str, "tcp.port == 80"); + wirefilter_ParsingResult result = wirefilter_parse_filter(scheme, filter_str); + + if(!result.success) { + return false; + } + + if(!result.ok.ast) { + return false; + } + + wirefilter_free_parsing_result(result); + + wirefilter_free_scheme(scheme); + return true; +} + +bool test_wirefilter_ffi_04() { + wirefilter_Scheme *scheme = wirefilter_create_scheme(); + if(!scheme) { + return false; + } + + wirefilter_ExternallyAllocatedStr field, filter_str; + + WIREFILTER_STRING(field, "http.host"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Bytes); + WIREFILTER_STRING(field, "ip.addr"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Ip); + WIREFILTER_STRING(field, "ssl"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Bool); + WIREFILTER_STRING(field, "tcp.port"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Int); + + WIREFILTER_STRING(filter_str, "tcp.port == \"wirefilter\""); + wirefilter_ParsingResult result = wirefilter_parse_filter(scheme, filter_str); + + if(result.success) { + return false; + } + + if(!result.err.msg.data || result.err.msg.length <= 0) { + return false; + } + + wirefilter_free_parsing_result(result); + + wirefilter_free_scheme(scheme); + return true; +} + +bool test_wirefilter_ffi_05() { + wirefilter_Scheme *scheme = wirefilter_create_scheme(); + if(!scheme) { + return false; + } + + wirefilter_ExternallyAllocatedStr field, filter_str; + + WIREFILTER_STRING(field, "http.host"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Bytes); + WIREFILTER_STRING(field, "ip.addr"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Ip); + WIREFILTER_STRING(field, "ssl"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Bool); + WIREFILTER_STRING(field, "tcp.port"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Int); + + WIREFILTER_STRING(filter_str, "tcp.port == 80"); + wirefilter_ParsingResult result = wirefilter_parse_filter(scheme, filter_str); + + if(!result.success) { + return false; + } + + if(!result.ok.ast) { + return false; + } + + wirefilter_Filter *filter = wirefilter_compile_filter(result.ok.ast); + if(!filter) { + return false; + } + + wirefilter_free_compiled_filter(filter); + + wirefilter_free_scheme(scheme); + return true; +} + +bool test_wirefilter_ffi_06() { + wirefilter_Scheme *scheme = wirefilter_create_scheme(); + if(!scheme) { + return false; + } + + wirefilter_ExecutionContext *exec_ctx = wirefilter_create_execution_context(scheme); + if(!exec_ctx) { + return false; + } + + wirefilter_free_execution_context(exec_ctx); + + wirefilter_free_scheme(scheme); + return true; +} + +bool test_wirefilter_ffi_07() { + wirefilter_Scheme *scheme = wirefilter_create_scheme(); + if(!scheme) { + return false; + } + + wirefilter_ExternallyAllocatedStr field; + + WIREFILTER_STRING(field, "http.host"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Bytes); + WIREFILTER_STRING(field, "ip.addr"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Ip); + WIREFILTER_STRING(field, "ssl"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Bool); + WIREFILTER_STRING(field, "tcp.port"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Int); + + wirefilter_ExecutionContext *exec_ctx = wirefilter_create_execution_context(scheme); + if(!exec_ctx) { + return false; + } + + WIREFILTER_STRING(field, "http.host"); + wirefilter_ExternallyAllocatedByteArr http_host; + WIREFILTER_STRING(http_host, "www.cloudflare.com") + wirefilter_add_bytes_value_to_execution_context(exec_ctx, field, http_host); + + WIREFILTER_STRING(field, "ip.addr"); + uint8_t ip_addr[4] = {192, 168, 0, 1}; + wirefilter_add_ipv4_value_to_execution_context(exec_ctx, field, ip_addr); + + WIREFILTER_STRING(field, "ssl"); + wirefilter_add_bool_value_to_execution_context(exec_ctx, field, false); + + WIREFILTER_STRING(field, "tcp.port"); + wirefilter_add_int_value_to_execution_context(exec_ctx, field, 80); + + wirefilter_free_execution_context(exec_ctx); + + wirefilter_free_scheme(scheme); + return true; +} + +bool test_wirefilter_ffi_08() { + wirefilter_Scheme *scheme = wirefilter_create_scheme(); + if(!scheme) { + return false; + } + + wirefilter_ExternallyAllocatedStr field; + + WIREFILTER_STRING(field, "http.host"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Bytes); + WIREFILTER_STRING(field, "ip.addr"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Ip); + WIREFILTER_STRING(field, "ssl"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Bool); + WIREFILTER_STRING(field, "tcp.port"); + wirefilter_add_type_field_to_scheme(scheme, field, wirefilter_Int); + + wirefilter_ExternallyAllocatedStr filter_str; + WIREFILTER_STRING(filter_str, "tcp.port == 80"); + wirefilter_ParsingResult result = wirefilter_parse_filter(scheme, filter_str); + + if(!result.success) { + return false; + } + + if(!result.ok.ast) { + return false; + } + + wirefilter_Filter *filter = wirefilter_compile_filter(result.ok.ast); + if(!filter) { + return false; + } + + wirefilter_ExecutionContext *exec_ctx = wirefilter_create_execution_context(scheme); + if(!exec_ctx) { + return false; + } + + WIREFILTER_STRING(field, "http.host"); + wirefilter_ExternallyAllocatedByteArr http_host; + WIREFILTER_STRING(http_host, "www.cloudflare.com") + wirefilter_add_bytes_value_to_execution_context(exec_ctx, field, http_host); + + WIREFILTER_STRING(field, "ip.addr"); + uint8_t ip_addr[4] = {192, 168, 0, 1}; + wirefilter_add_ipv4_value_to_execution_context(exec_ctx, field, ip_addr); + + WIREFILTER_STRING(field, "ssl"); + wirefilter_add_bool_value_to_execution_context(exec_ctx, field, false); + + WIREFILTER_STRING(field, "tcp.port"); + wirefilter_add_int_value_to_execution_context(exec_ctx, field, 80); + + if(!wirefilter_match(filter, exec_ctx)) { + return false; + } + + wirefilter_free_execution_context(exec_ctx); + + wirefilter_free_compiled_filter(filter); + + wirefilter_free_scheme(scheme); + return true; +} \ No newline at end of file diff --git a/ffi-ctests/src/lib.rs b/ffi-ctests/src/lib.rs new file mode 100644 index 00000000..ba42d190 --- /dev/null +++ b/ffi-ctests/src/lib.rs @@ -0,0 +1,30 @@ +#[test] +fn test() { + #[link(name = "wirefilter_ffi_ctests")] + extern "C" { + fn test_wirefilter_ffi_01() -> bool; + fn test_wirefilter_ffi_02() -> bool; + fn test_wirefilter_ffi_03() -> bool; + fn test_wirefilter_ffi_04() -> bool; + fn test_wirefilter_ffi_05() -> bool; + fn test_wirefilter_ffi_06() -> bool; + fn test_wirefilter_ffi_07() -> bool; + fn test_wirefilter_ffi_08() -> bool; + } + + assert!(unsafe { test_wirefilter_ffi_01() }); + + assert!(unsafe { test_wirefilter_ffi_02() }); + + assert!(unsafe { test_wirefilter_ffi_03() }); + + assert!(unsafe { test_wirefilter_ffi_04() }); + + assert!(unsafe { test_wirefilter_ffi_05() }); + + assert!(unsafe { test_wirefilter_ffi_06() }); + + assert!(unsafe { test_wirefilter_ffi_07() }); + + assert!(unsafe { test_wirefilter_ffi_08() }); +} diff --git a/ffi/include/wirefilter.h b/ffi/include/wirefilter.h new file mode 100644 index 00000000..49c5e477 --- /dev/null +++ b/ffi/include/wirefilter.h @@ -0,0 +1,122 @@ +#ifndef _WIREFILTER_H_ +#define _WIREFILTER_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct wirefilter_Scheme wirefilter_Scheme; +typedef struct wirefilter_ExecutionContext wirefilter_ExecutionContext; +typedef struct wirefilter_FilterAst wirefilter_FilterAst; +typedef struct wirefilter_Filter wirefilter_Filter; + +typedef struct { + const unsigned char *data; + size_t length; +} wirefilter_RustAllocatedStr; + +typedef struct { + const unsigned char *data; + size_t length; +} wirefilter_StaticRustAllocatedString; + +typedef struct { + const unsigned char *data; + size_t length; +} wirefilter_ExternallyAllocatedStr; + +typedef struct { + const unsigned char *data; + size_t length; +} wirefilter_ExternallyAllocatedByteArr; + +typedef union { + union { + uint8_t success; + struct { + uint8_t _res1; + wirefilter_RustAllocatedStr msg; + } err; + struct { + uint8_t _res2; + wirefilter_FilterAst *ast; + } ok; + }; +} wirefilter_ParsingResult; + +typedef enum { + wirefilter_Ip, + wirefilter_Bytes, + wirefilter_Int, + wirefilter_Bool +} wirefilter_Type; + +wirefilter_Scheme *wirefilter_create_scheme(); +void wirefilter_free_scheme(wirefilter_Scheme *scheme); + +void wirefilter_add_type_field_to_scheme( + wirefilter_Scheme *scheme, + wirefilter_ExternallyAllocatedStr name, + wirefilter_Type type +); + +wirefilter_ParsingResult wirefilter_parse_filter( + wirefilter_Scheme *scheme, + wirefilter_ExternallyAllocatedStr input +); + +void wirefilter_free_parsing_result(wirefilter_ParsingResult result); + +wirefilter_Filter *wirefilter_compile_filter(wirefilter_FilterAst *ast); +void wirefilter_free_compiled_filter(wirefilter_Filter *filter); + +wirefilter_ExecutionContext *wirefilter_create_execution_context(wirefilter_Scheme *scheme); +void wirefilter_free_execution_context(wirefilter_ExecutionContext *exec_ctx); + +void wirefilter_add_int_value_to_execution_context( + wirefilter_ExecutionContext *exec_ctx, + wirefilter_ExternallyAllocatedStr name, + int32_t value +); + +void wirefilter_add_bytes_value_to_execution_context( + wirefilter_ExecutionContext *exec_ctx, + wirefilter_ExternallyAllocatedStr name, + wirefilter_ExternallyAllocatedByteArr value +); + +void wirefilter_add_ipv6_value_to_execution_context( + wirefilter_ExecutionContext *exec_ctx, + wirefilter_ExternallyAllocatedStr name, + uint8_t value[16] +); + +void wirefilter_add_ipv4_value_to_execution_context( + wirefilter_ExecutionContext *exec_ctx, + wirefilter_ExternallyAllocatedStr name, + uint8_t value[4] +); + +void wirefilter_add_bool_value_to_execution_context( + wirefilter_ExecutionContext *exec_ctx, + wirefilter_ExternallyAllocatedStr name, + bool value +); + +bool wirefilter_match(wirefilter_Filter *filter, wirefilter_ExecutionContext *exec_ctx); + +bool wirefilter_filter_uses( + wirefilter_Filter *filter, + wirefilter_ExternallyAllocatedByteArr field_name +); + +wirefilter_StaticRustAllocatedString wirefilter_get_version(); + +#ifdef __cplusplus +} +#endif + +#endif // _WIREFILTER_H_ diff --git a/ffi/src/lib.rs b/ffi/src/lib.rs index e4f9fa33..1686fe54 100644 --- a/ffi/src/lib.rs +++ b/ffi/src/lib.rs @@ -92,6 +92,11 @@ pub extern "C" fn wirefilter_parse_filter<'s, 'i>( } } +#[no_mangle] +pub extern "C" fn wirefilter_free_parsing_result(r: ParsingResult<'_>) { + drop(r); +} + /// Wrapper for Hasher that allows using Write API (e.g. with serializer). #[derive(Default)] struct HasherWrite(H);