diff --git a/Cargo.lock b/Cargo.lock index 6160d576..0469951f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -774,6 +774,15 @@ dependencies = [ "regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "wirefilter-engine 0.6.1", + "wirefilter-ffi-ctests 0.1.0", +] + +[[package]] +name = "wirefilter-ffi-ctests" +version = "0.1.0" +dependencies = [ + "cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "wirefilter-ffi 0.6.1", ] [[package]] diff --git a/ffi/Cargo.toml b/ffi/Cargo.toml index 5a2f6956..5feb8212 100644 --- a/ffi/Cargo.toml +++ b/ffi/Cargo.toml @@ -25,3 +25,4 @@ path = "../engine" [dev-dependencies] regex = "1.0.1" indoc = "0.3.0" +wirefilter-ffi-ctests = {"path" = "tests/ctests"} diff --git a/ffi/include/wirefilter.h b/ffi/include/wirefilter.h new file mode 100644 index 00000000..28a26fe2 --- /dev/null +++ b/ffi/include/wirefilter.h @@ -0,0 +1,129 @@ +#ifndef _WIREFILTER_H_ +#define _WIREFILTER_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct wirefilter_scheme wirefilter_scheme_t; +typedef struct wirefilter_execution_context wirefilter_execution_context_t; +typedef struct wirefilter_filter_ast wirefilter_filter_ast_t; +typedef struct wirefilter_filter wirefilter_filter_t; + +typedef struct { + const unsigned char *data; + size_t length; +} wirefilter_rust_allocated_str_t; + +typedef struct { + const unsigned char *data; + size_t length; +} wirefilter_static_rust_allocated_str_t; + +typedef struct { + const unsigned char *data; + size_t length; +} wirefilter_externally_allocated_str_t; + +typedef struct { + const unsigned char *data; + size_t length; +} wirefilter_externally_allocated_byte_arr_t; + +typedef union { + union { + uint8_t success; + struct { + uint8_t _res1; + wirefilter_rust_allocated_str_t msg; + } err; + struct { + uint8_t _res2; + wirefilter_filter_ast_t *ast; + } ok; + }; +} wirefilter_parsing_result_t; + +typedef enum { + WIREFILTER_TYPE_IP, + WIREFILTER_TYPE_BYTES, + WIREFILTER_TYPE_INT, + WIREFILTER_TYPE_BOOL, +} wirefilter_type_t; + +wirefilter_scheme_t *wirefilter_create_scheme(); +void wirefilter_free_scheme(wirefilter_scheme_t *scheme); + +void wirefilter_add_type_field_to_scheme( + wirefilter_scheme_t *scheme, + wirefilter_externally_allocated_str_t name, + wirefilter_type_t type +); + +wirefilter_parsing_result_t wirefilter_parse_filter( + wirefilter_scheme_t *scheme, + wirefilter_externally_allocated_str_t input +); + +void wirefilter_free_parsing_result(wirefilter_parsing_result_t result); + +wirefilter_filter_t *wirefilter_compile_filter(wirefilter_filter_ast_t *ast); +void wirefilter_free_compiled_filter(wirefilter_filter_t *filter); + +wirefilter_execution_context_t *wirefilter_create_execution_context( + wirefilter_scheme_t *scheme +); +void wirefilter_free_execution_context( + wirefilter_execution_context_t *exec_ctx +); + +void wirefilter_add_int_value_to_execution_context( + wirefilter_execution_context_t *exec_ctx, + wirefilter_externally_allocated_str_t name, + int32_t value +); + +void wirefilter_add_bytes_value_to_execution_context( + wirefilter_execution_context_t *exec_ctx, + wirefilter_externally_allocated_str_t name, + wirefilter_externally_allocated_byte_arr_t value +); + +void wirefilter_add_ipv6_value_to_execution_context( + wirefilter_execution_context_t *exec_ctx, + wirefilter_externally_allocated_str_t name, + uint8_t value[16] +); + +void wirefilter_add_ipv4_value_to_execution_context( + wirefilter_execution_context_t *exec_ctx, + wirefilter_externally_allocated_str_t name, + uint8_t value[4] +); + +void wirefilter_add_bool_value_to_execution_context( + wirefilter_execution_context_t *exec_ctx, + wirefilter_externally_allocated_str_t name, + bool value +); + +bool wirefilter_match( + wirefilter_filter_t *filter, + wirefilter_execution_context_t *exec_ctx +); + +bool wirefilter_filter_uses( + wirefilter_filter_t *filter, + wirefilter_externally_allocated_str_t field_name +); + +wirefilter_static_rust_allocated_str_t 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); diff --git a/ffi/tests/ctests/Cargo.toml b/ffi/tests/ctests/Cargo.toml new file mode 100644 index 00000000..1b8f84b0 --- /dev/null +++ b/ffi/tests/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] +wirefilter-ffi = {"path" = "../.."} + +[build-dependencies] +cc = "1.0" diff --git a/ffi/tests/ctests/build.rs b/ffi/tests/ctests/build.rs new file mode 100644 index 00000000..72ae5e32 --- /dev/null +++ b/ffi/tests/ctests/build.rs @@ -0,0 +1,8 @@ +extern crate cc; + +fn main() { + cc::Build::new() + .include("../../include") + .file("src/tests.c") + .compile("wirefilter_ffi_ctests"); +} diff --git a/ffi/tests/ctests/src/lib.rs b/ffi/tests/ctests/src/lib.rs new file mode 100644 index 00000000..a8ed4db5 --- /dev/null +++ b/ffi/tests/ctests/src/lib.rs @@ -0,0 +1,30 @@ +#[test] +fn test() { + #[link(name = "wirefilter_ffi")] + extern "C" { + fn test_wirefilter_ffi_01(); + fn test_wirefilter_ffi_02(); + fn test_wirefilter_ffi_03(); + fn test_wirefilter_ffi_04(); + fn test_wirefilter_ffi_05(); + fn test_wirefilter_ffi_06(); + fn test_wirefilter_ffi_07(); + fn test_wirefilter_ffi_08(); + } + + unsafe { test_wirefilter_ffi_01() }; + + unsafe { test_wirefilter_ffi_02() }; + + unsafe { test_wirefilter_ffi_03() }; + + unsafe { test_wirefilter_ffi_04() }; + + unsafe { test_wirefilter_ffi_05() }; + + unsafe { test_wirefilter_ffi_06() }; + + unsafe { test_wirefilter_ffi_07() }; + + unsafe { test_wirefilter_ffi_08() }; +} diff --git a/ffi/tests/ctests/src/tests.c b/ffi/tests/ctests/src/tests.c new file mode 100644 index 00000000..bb78500f --- /dev/null +++ b/ffi/tests/ctests/src/tests.c @@ -0,0 +1,221 @@ +#ifdef NDEBUG +#undef NDEBUG +#endif + +#include +#include +#include +#include + +#include + +#define WIREFILTER_STRING(v, s) {(v).data = (const unsigned char *)(s); (v).length = strlen(s);} + +wirefilter_externally_allocated_str_t wirefilter_init_string(const char *s) { + wirefilter_externally_allocated_str_t str = {(const unsigned char *)s, strlen(s)}; + return str; +} + +void test_wirefilter_ffi_01() { + wirefilter_scheme_t *scheme = wirefilter_create_scheme(); + assert(scheme != NULL); + wirefilter_free_scheme(scheme); +} + +void test_wirefilter_ffi_02() { + wirefilter_scheme_t *scheme = wirefilter_create_scheme(); + assert(scheme != NULL); + + wirefilter_externally_allocated_str_t field; + + field = wirefilter_init_string("http.host"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_BYTES); + field = wirefilter_init_string("ip.addr"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_IP); + field = wirefilter_init_string("ssl"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_BOOL); + field = wirefilter_init_string("tcp.port"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_INT); + + wirefilter_free_scheme(scheme); +} + +void test_wirefilter_ffi_03() { + wirefilter_scheme_t *scheme = wirefilter_create_scheme(); + assert(scheme != NULL); + + wirefilter_externally_allocated_str_t field, filter_str; + + field = wirefilter_init_string("http.host"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_BYTES); + field = wirefilter_init_string("ip.addr"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_IP); + field = wirefilter_init_string("ssl"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_BOOL); + field = wirefilter_init_string("tcp.port"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_INT); + + WIREFILTER_STRING(filter_str, "tcp.port == 80"); + wirefilter_parsing_result_t result = wirefilter_parse_filter(scheme, filter_str); + assert(result.success == true); + assert(result.ok.ast != NULL); + + wirefilter_free_parsing_result(result); + + wirefilter_free_scheme(scheme); +} + +void test_wirefilter_ffi_04() { + wirefilter_scheme_t *scheme = wirefilter_create_scheme(); + assert(scheme != NULL); + + wirefilter_externally_allocated_str_t field, filter_str; + + field = wirefilter_init_string("http.host"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_BYTES); + field = wirefilter_init_string("ip.addr"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_IP); + field = wirefilter_init_string("ssl"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_BOOL); + field = wirefilter_init_string("tcp.port"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_INT); + + WIREFILTER_STRING(filter_str, "tcp.port == \"wirefilter\""); + wirefilter_parsing_result_t result = wirefilter_parse_filter(scheme, filter_str); + assert(result.success == false); + assert(result.err.msg.data && result.err.msg.length > 0); + + wirefilter_free_parsing_result(result); + + wirefilter_free_scheme(scheme); +} + +void test_wirefilter_ffi_05() { + wirefilter_scheme_t *scheme = wirefilter_create_scheme(); + assert(scheme != NULL); + + wirefilter_externally_allocated_str_t field, filter_str; + + field = wirefilter_init_string("http.host"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_BYTES); + field = wirefilter_init_string("ip.addr"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_IP); + field = wirefilter_init_string("ssl"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_BOOL); + field = wirefilter_init_string("tcp.port"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_INT); + + WIREFILTER_STRING(filter_str, "tcp.port == 80"); + wirefilter_parsing_result_t result = wirefilter_parse_filter(scheme, filter_str); + assert(result.success == true); + assert(result.ok.ast != NULL); + + wirefilter_filter_t *filter = wirefilter_compile_filter(result.ok.ast); + assert(filter != NULL); + + wirefilter_free_compiled_filter(filter); + + wirefilter_free_scheme(scheme); +} + +void test_wirefilter_ffi_06() { + wirefilter_scheme_t *scheme = wirefilter_create_scheme(); + assert(scheme != NULL); + + wirefilter_execution_context_t *exec_ctx = wirefilter_create_execution_context(scheme); + assert(exec_ctx != NULL); + + wirefilter_free_execution_context(exec_ctx); + + wirefilter_free_scheme(scheme); +} + +void test_wirefilter_ffi_07() { + wirefilter_scheme_t *scheme = wirefilter_create_scheme(); + assert(scheme != NULL); + + wirefilter_externally_allocated_str_t field; + + field = wirefilter_init_string("http.host"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_BYTES); + field = wirefilter_init_string("ip.addr"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_IP); + field = wirefilter_init_string("ssl"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_BOOL); + field = wirefilter_init_string("tcp.port"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_INT); + + wirefilter_execution_context_t *exec_ctx = wirefilter_create_execution_context(scheme); + assert(exec_ctx != NULL); + + field = wirefilter_init_string("http.host"); + wirefilter_externally_allocated_byte_arr_t http_host; + WIREFILTER_STRING(http_host, "www.cloudflare.com") + wirefilter_add_bytes_value_to_execution_context(exec_ctx, field, http_host); + + field = wirefilter_init_string("ip.addr"); + uint8_t ip_addr[4] = {192, 168, 0, 1}; + wirefilter_add_ipv4_value_to_execution_context(exec_ctx, field, ip_addr); + + field = wirefilter_init_string("ssl"); + wirefilter_add_bool_value_to_execution_context(exec_ctx, field, false); + + field = wirefilter_init_string("tcp.port"); + wirefilter_add_int_value_to_execution_context(exec_ctx, field, 80); + + wirefilter_free_execution_context(exec_ctx); + + wirefilter_free_scheme(scheme); +} + +void test_wirefilter_ffi_08() { + wirefilter_scheme_t *scheme = wirefilter_create_scheme(); + assert(scheme != NULL); + + wirefilter_externally_allocated_str_t field; + + field = wirefilter_init_string("http.host"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_BYTES); + field = wirefilter_init_string("ip.addr"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_IP); + field = wirefilter_init_string("ssl"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_BOOL); + field = wirefilter_init_string("tcp.port"); + wirefilter_add_type_field_to_scheme(scheme, field, WIREFILTER_TYPE_INT); + + wirefilter_externally_allocated_str_t filter_str; + WIREFILTER_STRING(filter_str, "tcp.port == 80"); + wirefilter_parsing_result_t result = wirefilter_parse_filter(scheme, filter_str); + assert(result.success == true); + assert(result.ok.ast != NULL); + + wirefilter_filter_t *filter = wirefilter_compile_filter(result.ok.ast); + assert(filter != NULL); + + wirefilter_execution_context_t *exec_ctx = wirefilter_create_execution_context(scheme); + assert(exec_ctx != NULL); + + field = wirefilter_init_string("http.host"); + wirefilter_externally_allocated_byte_arr_t http_host; + http_host.data = (unsigned char *)"www.cloudflare.com"; + http_host.length = strlen((char *)http_host.data); + wirefilter_add_bytes_value_to_execution_context(exec_ctx, field, http_host); + + field = wirefilter_init_string("ip.addr"); + uint8_t ip_addr[4] = {192, 168, 0, 1}; + wirefilter_add_ipv4_value_to_execution_context(exec_ctx, field, ip_addr); + + field = wirefilter_init_string("ssl"); + wirefilter_add_bool_value_to_execution_context(exec_ctx, field, false); + + field = wirefilter_init_string("tcp.port"); + wirefilter_add_int_value_to_execution_context(exec_ctx, field, 80); + + assert(wirefilter_match(filter, exec_ctx) == true); + + wirefilter_free_execution_context(exec_ctx); + + wirefilter_free_compiled_filter(filter); + + wirefilter_free_scheme(scheme); +}