From fb0cb5405d8236b23b5866952eda99e54d25aa5b Mon Sep 17 00:00:00 2001 From: liuwentan Date: Wed, 9 Aug 2023 19:22:09 +0800 Subject: [PATCH] rollback to v4.0.31 --- include/maat.h | 13 +- scanner/CMakeLists.txt | 5 +- .../adapter_hs/adapter_hs.cpp | 240 +-- scanner/adapter_hs/adapter_hs.h | 134 ++ scanner/expr_matcher/adapter_hs/adapter_hs.h | 75 - .../expr_matcher/adapter_rs/adapter_rs.cpp | 708 -------- scanner/expr_matcher/adapter_rs/adapter_rs.h | 78 - scanner/expr_matcher/expr_matcher.cpp | 235 --- scanner/expr_matcher/expr_matcher.h | 134 -- src/CMakeLists.txt | 4 +- src/inc_internal/maat_compile.h | 19 +- src/inc_internal/maat_expr.h | 11 +- src/inc_internal/maat_kv.h | 7 +- src/inc_internal/maat_rule.h | 3 +- src/inc_internal/maat_table.h | 24 +- src/json2iris.c | 4 +- src/maat_api.c | 106 +- src/maat_compile.c | 281 ++-- src/maat_expr.c | 133 +- src/maat_flag.c | 6 +- src/maat_interval.c | 6 +- src/maat_kv.c | 101 +- src/maat_rule.c | 54 +- src/maat_table.c | 318 ++-- test/CMakeLists.txt | 13 +- test/adapter_hs_gtest.cpp | 730 +++++++++ test/expr_matcher_gtest.cpp | 1330 --------------- test/json_update/corrupted.json | 2 +- test/json_update/new.json | 4 +- test/json_update/old.json | 4 +- test/literal_expr.conf | 30 +- test/maat_ex_data_gtest.cpp | 2 +- test/maat_framework_gtest.cpp | 1450 +++-------------- test/maat_framework_perf_gtest.cpp | 575 +------ test/maat_input_mode_gtest.cpp | 2 +- test/maat_json.json | 243 ++- test/table_info.conf | 243 +-- vendor/CMakeLists.txt | 28 - vendor/pcre-8.45.tar.gz | Bin 2287710 -> 0 bytes vendor/rulescan-3.0.1.tar.gz | Bin 1051651 -> 0 bytes 40 files changed, 1907 insertions(+), 5448 deletions(-) rename scanner/{expr_matcher => }/adapter_hs/adapter_hs.cpp (77%) create mode 100644 scanner/adapter_hs/adapter_hs.h delete mode 100644 scanner/expr_matcher/adapter_hs/adapter_hs.h delete mode 100644 scanner/expr_matcher/adapter_rs/adapter_rs.cpp delete mode 100644 scanner/expr_matcher/adapter_rs/adapter_rs.h delete mode 100644 scanner/expr_matcher/expr_matcher.cpp delete mode 100644 scanner/expr_matcher/expr_matcher.h create mode 100644 test/adapter_hs_gtest.cpp delete mode 100644 test/expr_matcher_gtest.cpp delete mode 100644 vendor/pcre-8.45.tar.gz delete mode 100644 vendor/rulescan-3.0.1.tar.gz diff --git a/include/maat.h b/include/maat.h index 5099e0a..f9afe61 100644 --- a/include/maat.h +++ b/include/maat.h @@ -37,7 +37,6 @@ struct maat_hit_path { }; struct maat_hit_group { - long long item_id; long long group_id; int vtable_id; }; @@ -60,11 +59,6 @@ enum maat_list_type { MAAT_LIST_TYPE_INC }; -enum maat_expr_engine { - MAAT_EXPR_ENGINE_HS = 0, //default engine(hyperscan) - MAAT_EXPR_ENGINE_RS //rulescan -}; - struct ip_addr { int ip_type; //4: IPv4, 6: IPv6 union { @@ -150,8 +144,6 @@ int maat_options_set_redis(struct maat_options *opts, const char *redis_ip, int maat_options_set_stat_file(struct maat_options *opts, const char *stat_filename); -int maat_options_set_expr_engine(struct maat_options *opts, enum maat_expr_engine engine); - /* maat_instance API */ struct maat *maat_new(struct maat_options *opts, const char *table_info_path); void maat_free(struct maat *instance); @@ -279,9 +271,6 @@ int maat_state_set_last_scan(struct maat_state *state); int maat_state_set_scan_compile_table(struct maat_state *state, int compile_table_id); -int maat_state_get_compile_table_ids(struct maat_state *state, long long *compile_ids, - size_t n_compile_ids, int *compile_table_ids); - int maat_state_get_hit_paths(struct maat_state *state, struct maat_hit_path *paths, size_t n_path); @@ -307,4 +296,4 @@ int maat_hit_group_compile_id(struct maat *instance, struct maat_hit_group *grou } #endif -#endif +#endif \ No newline at end of file diff --git a/scanner/CMakeLists.txt b/scanner/CMakeLists.txt index 7d45154..429755e 100644 --- a/scanner/CMakeLists.txt +++ b/scanner/CMakeLists.txt @@ -7,9 +7,8 @@ include_directories(${PROJECT_SOURCE_DIR}/src/inc_internal) add_subdirectory(ip_matcher/IntervalIndex) -add_library(adapter-static bool_matcher/bool_matcher.cpp expr_matcher/expr_matcher.cpp - expr_matcher/adapter_hs/adapter_hs.cpp expr_matcher/adapter_rs/adapter_rs.cpp +add_library(adapter-static adapter_hs/adapter_hs.cpp bool_matcher/bool_matcher.cpp fqdn_engine/fqdn_engine.cpp ip_matcher/ip_matcher.cpp ip_matcher/ipv4_match.cpp ip_matcher/ipv6_match.cpp flag_matcher/flag_matcher.cpp interval_matcher/cgranges.c interval_matcher/interval_matcher.cpp) -target_link_libraries(adapter-static hyperscan_static hyperscan_runtime_static rulescan_static interval_index_static) \ No newline at end of file +target_link_libraries(adapter-static hyperscan_static hyperscan_runtime_static interval_index_static) \ No newline at end of file diff --git a/scanner/expr_matcher/adapter_hs/adapter_hs.cpp b/scanner/adapter_hs/adapter_hs.cpp similarity index 77% rename from scanner/expr_matcher/adapter_hs/adapter_hs.cpp rename to scanner/adapter_hs/adapter_hs.cpp index 4d57dcf..d201e11 100644 --- a/scanner/expr_matcher/adapter_hs/adapter_hs.cpp +++ b/scanner/adapter_hs/adapter_hs.cpp @@ -1,10 +1,10 @@ /* ********************************************************************************************** -* File: adapter_hs.c -* Description: -* Authors: Liu wentan +* File: adapter_hs.cpp +* Description: +* Authors: Liu WenTan * Date: 2022-10-31 -* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. +* Copyright: (c) 2018-2022 Geedge Networks, Inc. All rights reserved. *********************************************************************************************** */ @@ -20,8 +20,9 @@ #include "uthash/utarray.h" #include "uthash/uthash.h" #include "maat_utils.h" -#include "../../bool_matcher/bool_matcher.h" +#include "../bool_matcher/bool_matcher.h" +#define MAX_OFFSET_NUM 1024 #define MAX_HIT_PATTERN_NUM 512 pid_t hs_gettid() @@ -40,7 +41,6 @@ static const char *hs_module_name_str(const char *name) #define MODULE_ADAPTER_HS hs_module_name_str("maat.adapter_hs") struct adpt_hs_compile_data { - enum expr_pattern_type pat_type; unsigned int *ids; unsigned int *flags; char **patterns; @@ -56,6 +56,7 @@ struct adapter_hs_scratch { struct adapter_hs_stream { int thread_id; + size_t n_expr; hs_stream_t *literal_stream; hs_stream_t *regex_stream; struct adapter_hs_runtime *ref_hs_rt; @@ -90,7 +91,7 @@ struct pattern_offset { struct pattern_attribute { long long pattern_id; - enum expr_match_mode match_mode; + enum hs_match_mode match_mode; struct pattern_offset offset; }; @@ -136,12 +137,12 @@ static int _hs_alloc_scratch(hs_database_t *db, hs_scratch_t **scratches, static int adpt_hs_alloc_scratch(struct adapter_hs_runtime *hs_rt, size_t n_worker_thread, - enum expr_pattern_type pattern_type, + enum hs_pattern_type pattern_type, struct log_handle *logger) { int ret = 0; - if (pattern_type == EXPR_PATTERN_TYPE_STR) { + if (pattern_type == HS_PATTERN_TYPE_STR) { hs_rt->scratch->literal_scratches = ALLOC(hs_scratch_t *, n_worker_thread); ret = _hs_alloc_scratch(hs_rt->literal_db, hs_rt->scratch->literal_scratches, n_worker_thread, logger); @@ -199,7 +200,7 @@ static int adpt_hs_build_database(struct adapter_hs_runtime *hs_rt, if (regex_cd != NULL) { err = hs_compile_multi((const char *const *)regex_cd->patterns, regex_cd->flags, regex_cd->ids, regex_cd->n_patterns, - HS_MODE_STREAM, + HS_MODE_STREAM | HS_MODE_SOM_HORIZON_SMALL, NULL, &hs_rt->regex_db, &compile_err); if (err != HS_SUCCESS) { if (compile_err) { @@ -214,11 +215,9 @@ static int adpt_hs_build_database(struct adapter_hs_runtime *hs_rt, return 0; } -static struct adpt_hs_compile_data * -adpt_hs_compile_data_new(enum expr_pattern_type pat_type, size_t n_patterns) +static struct adpt_hs_compile_data *adpt_hs_compile_data_new(size_t n_patterns) { struct adpt_hs_compile_data *hs_cd = ALLOC(struct adpt_hs_compile_data, 1); - hs_cd->pat_type = pat_type; hs_cd->patterns = ALLOC(char *, n_patterns); hs_cd->pattern_lens = ALLOC(size_t, n_patterns); hs_cd->n_patterns = n_patterns; @@ -264,11 +263,8 @@ static void populate_compile_data(struct adpt_hs_compile_data *compile_data, compile_data->ids[index] = pattern_id; /* set flags */ - if (compile_data->pat_type == EXPR_PATTERN_TYPE_STR) { - compile_data->flags[index] |= HS_FLAG_SOM_LEFTMOST; - } - - if (case_sensitive == EXPR_CASE_INSENSITIVE) { + compile_data->flags[index] |= HS_FLAG_SOM_LEFTMOST; + if (case_sensitive == HS_CASE_INSENSITIVE) { compile_data->flags[index] |= HS_FLAG_CASELESS; } @@ -299,14 +295,14 @@ static struct bool_expr *bool_exprs_new(struct expr_rule *rules, size_t n_rule, pattern_attr[pattern_index].pattern_id = pattern_index; pattern_attr[pattern_index].match_mode = rules[i].patterns[j].match_mode; - if (pattern_attr[pattern_index].match_mode == EXPR_MATCH_MODE_SUB || - pattern_attr[pattern_index].match_mode == EXPR_MATCH_MODE_EXACTLY) { + if (pattern_attr[pattern_index].match_mode == HS_MATCH_MODE_SUB || + pattern_attr[pattern_index].match_mode == HS_MATCH_MODE_EXACTLY) { pattern_attr[pattern_index].offset.start = rules[i].patterns[j].start_offset; pattern_attr[pattern_index].offset.end = rules[i].patterns[j].end_offset; } /* literal pattern */ - if (rules[i].patterns[j].type == EXPR_PATTERN_TYPE_STR) { + if (rules[i].patterns[j].pattern_type == HS_PATTERN_TYPE_STR) { populate_compile_data(literal_cd, literal_index, pattern_index, rules[i].patterns[j].pat, rules[i].patterns[j].pat_len, rules[i].patterns[j].case_sensitive); @@ -325,7 +321,7 @@ static struct bool_expr *bool_exprs_new(struct expr_rule *rules, size_t n_rule, bool_exprs[i].expr_id = rules[i].expr_id; bool_exprs[i].item_num = rules[i].n_patterns; - bool_exprs[i].user_tag = rules[i].tag; + bool_exprs[i].user_tag = rules[i].user_tag; } *n_pattern = pattern_index; @@ -349,43 +345,81 @@ static int verify_regex_expression(const char *regex_str, struct log_handle *log FREE(info); hs_free_compile_error(error); - return 0; + return -1; } if (info != NULL) { FREE(info); } - return 1; + return 0; } int adapter_hs_verify_regex_expression(const char *regex_expr, struct log_handle *logger) { if (NULL == regex_expr) { - return 0; + return -1; } return verify_regex_expression(regex_expr, logger); } -void *adapter_hs_new(struct expr_rule *rules, size_t n_rule, - size_t n_literal_pattern, size_t n_regex_pattern, - size_t n_worker_thread, struct log_handle *logger) +struct adapter_hs *adapter_hs_new(struct expr_rule *rules, size_t n_rule, + size_t n_worker_thread, struct log_handle *logger) { + if (0 == n_worker_thread || NULL == rules || 0 == n_rule) { + log_error(logger, MODULE_ADAPTER_HS, + "[%s:%d] input parameters illegal!", __FUNCTION__, __LINE__); + return NULL; + } + /* get the sum of pattern */ - size_t i = 0; + size_t i = 0, j = 0; + size_t literal_pattern_num = 0; + size_t regex_pattern_num = 0; + for (i = 0; i < n_rule; i++) { + if (rules[i].n_patterns > MAX_EXPR_PATTERN_NUM) { + log_error(logger, MODULE_ADAPTER_HS, + "[%s:%d] the number of patterns in one expression " + "should less than %d", __FUNCTION__, __LINE__, + MAX_EXPR_PATTERN_NUM); + return NULL; + } + + for (j = 0; j < rules[i].n_patterns; j++) { + /* pat_len should not 0 */ + if (0 == rules[i].patterns[j].pat_len) { + log_error(logger, MODULE_ADAPTER_HS, + "[%s:%d] expr pattern length should not 0", + __FUNCTION__, __LINE__); + return NULL; + } + + if (rules[i].patterns[j].pattern_type == HS_PATTERN_TYPE_STR) { + literal_pattern_num++; + } else { + regex_pattern_num++; + } + } + } + + if (0 == literal_pattern_num && 0 == regex_pattern_num) { + log_error(logger, MODULE_ADAPTER_HS, + "[%s:%d] exprs has no valid pattern", __FUNCTION__, __LINE__); + return NULL; + } + struct adpt_hs_compile_data *literal_cd = NULL; struct adpt_hs_compile_data *regex_cd = NULL; - - if (n_literal_pattern > 0) { - literal_cd = adpt_hs_compile_data_new(EXPR_PATTERN_TYPE_STR, n_literal_pattern); + if (literal_pattern_num > 0) { + literal_cd = adpt_hs_compile_data_new(literal_pattern_num); } - if (n_regex_pattern > 0) { - regex_cd = adpt_hs_compile_data_new(EXPR_PATTERN_TYPE_REG, n_regex_pattern); + if (regex_pattern_num > 0) { + regex_cd = adpt_hs_compile_data_new(regex_pattern_num); } - size_t pattern_cnt = n_literal_pattern + n_regex_pattern; + size_t pattern_cnt = literal_pattern_num + regex_pattern_num; struct adapter_hs *hs_inst = ALLOC(struct adapter_hs, 1); hs_inst->hs_attr = ALLOC(struct pattern_attribute, pattern_cnt); hs_inst->logger = logger; @@ -444,21 +478,21 @@ void *adapter_hs_new(struct expr_rule *rules, size_t n_rule, n_worker_thread); for (i = 0; i < n_worker_thread; i++) { hs_inst->hs_rt->scratch->bool_match_buffs[i] = ALLOC(struct bool_expr_match, - MAX_HIT_EXPR_NUM); + hs_inst->n_expr); } /* literal and regex scratch can't reuse */ - if (n_literal_pattern > 0) { + if (literal_pattern_num > 0) { ret = adpt_hs_alloc_scratch(hs_inst->hs_rt, n_worker_thread, - EXPR_PATTERN_TYPE_STR, logger); + HS_PATTERN_TYPE_STR, logger); if (ret < 0) { goto error; } } - if (n_regex_pattern > 0) { + if (regex_pattern_num > 0) { ret = adpt_hs_alloc_scratch(hs_inst->hs_rt, n_worker_thread, - EXPR_PATTERN_TYPE_REG, logger); + HS_PATTERN_TYPE_REG, logger); if (ret < 0) { goto error; } @@ -466,7 +500,7 @@ void *adapter_hs_new(struct expr_rule *rules, size_t n_rule, hs_inst->hs_rt->streams = ALLOC(struct adapter_hs_stream *, n_worker_thread); for (i = 0; i < n_worker_thread; i++) { - hs_inst->hs_rt->streams[i] = (struct adapter_hs_stream *)adapter_hs_stream_open(hs_inst, i); + hs_inst->hs_rt->streams[i] = adapter_hs_stream_open(hs_inst, i); } return hs_inst; @@ -475,15 +509,13 @@ error: return NULL; } -void adapter_hs_free(void *hs_instance) +void adapter_hs_free(struct adapter_hs *hs_inst) { - if (NULL == hs_instance) { + if (NULL == hs_inst) { return; } - struct adapter_hs *hs_inst = (struct adapter_hs *)hs_instance; size_t i = 0; - if (hs_inst->hs_rt != NULL) { if (hs_inst->hs_rt->literal_db != NULL) { hs_free_database(hs_inst->hs_rt->literal_db); @@ -593,12 +625,12 @@ static int matched_event_cb(unsigned int id, unsigned long long from, int ret = 0; struct pattern_attribute pat_attr = matched_pat->ref_hs_attr[id]; switch (pat_attr.match_mode) { - case EXPR_MATCH_MODE_EXACTLY: + case HS_MATCH_MODE_EXACTLY: if (0 == from && matched_pat->scan_data_len == to) { ret = 1; } break; - case EXPR_MATCH_MODE_SUB: + case HS_MATCH_MODE_SUB: if (pat_attr.offset.start == -1 && pat_attr.offset.end == -1) { ret = 1; @@ -624,12 +656,12 @@ static int matched_event_cb(unsigned int id, unsigned long long from, ret = 1; } break; - case EXPR_MATCH_MODE_PREFIX: + case HS_MATCH_MODE_PREFIX: if (0 == from) { ret = 1; } break; - case EXPR_MATCH_MODE_SUFFIX: + case HS_MATCH_MODE_SUFFIX: if (to == matched_pat->scan_data_len) { ret = 1; } @@ -646,42 +678,43 @@ static int matched_event_cb(unsigned int id, unsigned long long from, return 0; } -UT_icd ut_hs_pattern_id_icd = {sizeof(unsigned long long), NULL, NULL, NULL}; -void *adapter_hs_stream_open(void *hs_instance, int thread_id) +UT_icd ut_pattern_id_icd = {sizeof(unsigned long long), NULL, NULL, NULL}; +struct adapter_hs_stream * +adapter_hs_stream_open(struct adapter_hs *hs_instance, int thread_id) { if (NULL == hs_instance || thread_id < 0) { return NULL; } - struct adapter_hs *hs_inst = (struct adapter_hs *)hs_instance; struct adapter_hs_stream *hs_stream = ALLOC(struct adapter_hs_stream, 1); hs_error_t err; - hs_stream->logger = hs_inst->logger; + hs_stream->logger = hs_instance->logger; hs_stream->thread_id = thread_id; - hs_stream->ref_hs_rt = hs_inst->hs_rt; + hs_stream->n_expr = hs_instance->n_expr; + hs_stream->ref_hs_rt = hs_instance->hs_rt; hs_stream->matched_pat = ALLOC(struct matched_pattern, 1); - hs_stream->matched_pat->ref_hs_attr = hs_inst->hs_attr; - hs_stream->matched_pat->n_patterns = hs_inst->n_patterns; - utarray_new(hs_stream->matched_pat->pattern_ids, &ut_hs_pattern_id_icd); + hs_stream->matched_pat->ref_hs_attr = hs_instance->hs_attr; + hs_stream->matched_pat->n_patterns = hs_instance->n_patterns; + utarray_new(hs_stream->matched_pat->pattern_ids, &ut_pattern_id_icd); utarray_reserve(hs_stream->matched_pat->pattern_ids, MAX_HIT_PATTERN_NUM); int err_count = 0; - if (hs_inst->hs_rt->literal_db != NULL) { - err = hs_open_stream(hs_inst->hs_rt->literal_db, 0, + if (hs_instance->hs_rt->literal_db != NULL) { + err = hs_open_stream(hs_instance->hs_rt->literal_db, 0, &hs_stream->literal_stream); if (err != HS_SUCCESS) { - log_error(hs_inst->logger, MODULE_ADAPTER_HS, + log_error(hs_instance->logger, MODULE_ADAPTER_HS, "hs_open_stream failed, hs err:%d", err); err_count++; } } - if (hs_inst->hs_rt->regex_db != NULL) { - err = hs_open_stream(hs_inst->hs_rt->regex_db, 0, + if (hs_instance->hs_rt->regex_db != NULL) { + err = hs_open_stream(hs_instance->hs_rt->regex_db, 0, &hs_stream->regex_stream); if (err != HS_SUCCESS) { - log_error(hs_inst->logger, MODULE_ADAPTER_HS, + log_error(hs_instance->logger, MODULE_ADAPTER_HS, "hs_open_stream failed, hs err:%d", err); err_count++; } @@ -707,37 +740,36 @@ error: return NULL; } -void adapter_hs_stream_close(void *hs_stream) +void adapter_hs_stream_close(struct adapter_hs_stream *hs_stream) { if (NULL == hs_stream) { return; } - struct adapter_hs_stream *stream = (struct adapter_hs_stream *)hs_stream; - if (stream->ref_hs_rt != NULL) { - if (stream->literal_stream != NULL) { - hs_close_stream(stream->literal_stream, NULL, NULL, NULL); - stream->literal_stream = NULL; + if (hs_stream->ref_hs_rt != NULL) { + if (hs_stream->literal_stream != NULL) { + hs_close_stream(hs_stream->literal_stream, NULL, NULL, NULL); + hs_stream->literal_stream = NULL; } - if (stream->regex_stream != NULL) { - hs_close_stream(stream->regex_stream, NULL, NULL, NULL); - stream->regex_stream = NULL; + if (hs_stream->regex_stream != NULL) { + hs_close_stream(hs_stream->regex_stream, NULL, NULL, NULL); + hs_stream->regex_stream = NULL; } } - /* stream->hs_rt point to hs_instance->hs_rt which will call free + /* hs_stream->hs_rt point to hs_instance->hs_rt which will call free same as hs_attr */ - stream->ref_hs_rt = NULL; - stream->matched_pat->ref_hs_attr = NULL; + hs_stream->ref_hs_rt = NULL; + hs_stream->matched_pat->ref_hs_attr = NULL; - if (stream->matched_pat->pattern_ids != NULL) { - utarray_free(stream->matched_pat->pattern_ids); - stream->matched_pat->pattern_ids = NULL; + if (hs_stream->matched_pat->pattern_ids != NULL) { + utarray_free(hs_stream->matched_pat->pattern_ids); + hs_stream->matched_pat->pattern_ids = NULL; } - FREE(stream->matched_pat); - FREE(stream); + FREE(hs_stream->matched_pat); + FREE(hs_stream); } static void adapter_hs_stream_reset(struct adapter_hs_stream *hs_stream) @@ -762,9 +794,9 @@ static void adapter_hs_stream_reset(struct adapter_hs_stream *hs_stream) utarray_clear(hs_stream->matched_pat->pattern_ids); } -int adapter_hs_scan_stream(void *hs_stream, const char *data, size_t data_len, - struct expr_scan_result *results, size_t n_result, - size_t *n_hit_result) +int adapter_hs_scan_stream(struct adapter_hs_stream *hs_stream, const char *data, + size_t data_len, struct hs_scan_result *results, + size_t n_result, size_t *n_hit_result) { hs_error_t err; @@ -784,37 +816,36 @@ int adapter_hs_scan_stream(void *hs_stream, const char *data, size_t data_len, */ int err_count = 0; - struct adapter_hs_stream *stream = (struct adapter_hs_stream *)hs_stream; - int thread_id = stream->thread_id; - struct adapter_hs_scratch *scratch = stream->ref_hs_rt->scratch; - stream->matched_pat->scan_data_len = data_len; + int thread_id = hs_stream->thread_id; + struct adapter_hs_scratch *scratch = hs_stream->ref_hs_rt->scratch; + hs_stream->matched_pat->scan_data_len = data_len; int err_scratch_flag = 0; - if (stream->literal_stream != NULL) { + if (hs_stream->literal_stream != NULL) { if (scratch->literal_scratches != NULL) { - err = hs_scan_stream(stream->literal_stream, data, data_len, + err = hs_scan_stream(hs_stream->literal_stream, data, data_len, 0, scratch->literal_scratches[thread_id], - matched_event_cb, stream->matched_pat); + matched_event_cb, hs_stream->matched_pat); if (err != HS_SUCCESS) { err_count++; } } else { - log_error(stream->logger, MODULE_ADAPTER_HS, + log_error(hs_stream->logger, MODULE_ADAPTER_HS, "literal_scratches is null, thread_id:%d", thread_id); err_scratch_flag++; } } - if (stream->regex_stream != NULL) { + if (hs_stream->regex_stream != NULL) { if (scratch->regex_scratches != NULL) { - err = hs_scan_stream(stream->regex_stream, data, data_len, + err = hs_scan_stream(hs_stream->regex_stream, data, data_len, 0, scratch->regex_scratches[thread_id], - matched_event_cb, stream->matched_pat); + matched_event_cb, hs_stream->matched_pat); if (err != HS_SUCCESS) { err_count++; } } else { - log_error(stream->logger, MODULE_ADAPTER_HS, + log_error(hs_stream->logger, MODULE_ADAPTER_HS, "regex_scratches is null, thread_id:%d", thread_id); err_scratch_flag++; } @@ -828,7 +859,7 @@ int adapter_hs_scan_stream(void *hs_stream, const char *data, size_t data_len, return -1; } - size_t n_pattern_id = utarray_len(stream->matched_pat->pattern_ids); + size_t n_pattern_id = utarray_len(hs_stream->matched_pat->pattern_ids); if (0 == n_pattern_id) { *n_hit_result = 0; return 0; @@ -837,13 +868,13 @@ int adapter_hs_scan_stream(void *hs_stream, const char *data, size_t data_len, unsigned long long pattern_ids[n_pattern_id]; for (size_t i = 0; i < n_pattern_id; i++) { - pattern_ids[i] = *(unsigned long long *)utarray_eltptr(stream->matched_pat->pattern_ids, i); + pattern_ids[i] = *(unsigned long long *)utarray_eltptr(hs_stream->matched_pat->pattern_ids, i); } int ret = 0; struct bool_expr_match *bool_matcher_results = scratch->bool_match_buffs[thread_id]; - int bool_matcher_ret = bool_matcher_match(stream->ref_hs_rt->bm, pattern_ids, n_pattern_id, - bool_matcher_results, MAX_HIT_EXPR_NUM); + int bool_matcher_ret = bool_matcher_match(hs_stream->ref_hs_rt->bm, pattern_ids, n_pattern_id, + bool_matcher_results, hs_stream->n_expr); if (bool_matcher_ret < 0) { ret = -1; goto next; @@ -860,21 +891,22 @@ int adapter_hs_scan_stream(void *hs_stream, const char *data, size_t data_len, *n_hit_result = bool_matcher_ret; next: - utarray_clear(stream->matched_pat->pattern_ids); + utarray_clear(hs_stream->matched_pat->pattern_ids); return ret; } -int adapter_hs_scan(void *hs_instance, int thread_id, const char *data, size_t data_len, - struct expr_scan_result *results, size_t n_result, size_t *n_hit_result) +int adapter_hs_scan(struct adapter_hs *hs_instance, int thread_id, + const char *data, size_t data_len, + struct hs_scan_result *results, + size_t n_result, size_t *n_hit_result) { if (NULL == hs_instance || NULL == data || (0 == data_len) || NULL == results || 0 == n_result || NULL == n_hit_result) { return -1; } - struct adapter_hs *hs_inst = (struct adapter_hs *)hs_instance; - struct adapter_hs_stream *hs_stream = hs_inst->hs_rt->streams[thread_id]; + struct adapter_hs_stream *hs_stream = hs_instance->hs_rt->streams[thread_id]; assert(hs_stream != NULL); adapter_hs_stream_reset(hs_stream); diff --git a/scanner/adapter_hs/adapter_hs.h b/scanner/adapter_hs/adapter_hs.h new file mode 100644 index 0000000..672650a --- /dev/null +++ b/scanner/adapter_hs/adapter_hs.h @@ -0,0 +1,134 @@ +/* +********************************************************************************************** +* File: adapter_hs.h +* Description: wrapper for raw hyperscan +* Authors: Liu WenTan +* Date: 2022-10-31 +* Copyright: (c) 2018-2022 Geedge Networks, Inc. All rights reserved. +*********************************************************************************************** +*/ + +#ifndef _ADAPTER_HS_H_ +#define _ADAPTER_HS_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +#include "log/log.h" + +#define MAX_EXPR_PATTERN_NUM 8 + +struct adapter_hs; + +/* match method */ +enum hs_match_mode { + HS_MATCH_MODE_INVALID = -1, + HS_MATCH_MODE_EXACTLY = 1, /* scan data must match pattern exactly */ + HS_MATCH_MODE_PREFIX, /* pattern must in the head of scan_data */ + HS_MATCH_MODE_SUFFIX, /* pattern must in the end of scan_data */ + HS_MATCH_MODE_SUB /* pattern must in the range[l_offset, r_offset] of scan_data */ +}; + +enum hs_pattern_type { + HS_PATTERN_TYPE_STR = 0, /* pure literal string */ + HS_PATTERN_TYPE_REG /* regex expression */ +}; + +enum hs_case_sensitive { + HS_CASE_SENSITIVE = 0, + HS_CASE_INSENSITIVE +}; + +struct hs_scan_result { + long long rule_id; + void *user_tag; +}; + +struct hs_pattern { + enum hs_case_sensitive case_sensitive; + enum hs_match_mode match_mode; + enum hs_pattern_type pattern_type; + + int is_hexbin; /* 1(yes) 0(no) */ + + /* + * just match in scan_data's range of [start_offset, end_offset], -1 means no limits + * for example: + * [-1, end_offset] means the pattern must in scan_data's [0 ~ start_offset] + * [start_offset, -1] means the pattern must in scan_data's [start_offset ~ data_end] + */ + int start_offset; + int end_offset; + + /* start pointer of pattern */ + char *pat; + /* pattern length */ + size_t pat_len; +}; + +/* logic AND expression, such as (pattern1 & pattern2) */ +struct expr_rule { + long long expr_id; + size_t n_patterns; + struct hs_pattern patterns[MAX_EXPR_PATTERN_NUM]; + void *user_tag; +}; + +int adapter_hs_verify_regex_expression(const char *regex_expr, + struct log_handle *logger); +/** + * @brief new adapter_hs instance + * + * @param rules: logic AND expression's array + * @param n_rule: the number of logic AND expression's array + * @param nr_worker_threads: the number of scan threads which will call adapter_hs_scan() + * + * @retval the pointer to adapter_hs instance +*/ +struct adapter_hs *adapter_hs_new(struct expr_rule *rules, size_t n_rule, + size_t n_worker_thread, struct log_handle *logger); + +/** + * @brief scan input data to match logic AND expression, return all matched expr_id + * + * @param instance: adapter_hs instance obtained by adapter_hs_new() + * @param thread_id: the thread_id of caller + * @param data: data to be scanned + * @param data_len: the length of data to be scanned + * @param results: the array of expr_id + * @param n_results: number of elements in array of expr_id +*/ +int adapter_hs_scan(struct adapter_hs *hs_instance, int thread_id, + const char *data, size_t data_len, + struct hs_scan_result *results, + size_t n_result, size_t *n_hit_result); + +/** + * @brief destroy adapter_hs instance + * + * @param instance: adapter_hs instance obtained by adapter_hs_new() +*/ +void adapter_hs_free(struct adapter_hs *instance); + +struct adapter_hs_stream; +/** + * @brief open adapter_hs stream after adapter_hs instance initialized for stream scan + * +*/ +struct adapter_hs_stream *adapter_hs_stream_open(struct adapter_hs *hs_instance, int thread_id); + +int adapter_hs_scan_stream(struct adapter_hs_stream *stream, const char *data, size_t data_len, + struct hs_scan_result *results, size_t n_result, size_t *n_hit_result); + +void adapter_hs_stream_close(struct adapter_hs_stream *stream); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/scanner/expr_matcher/adapter_hs/adapter_hs.h b/scanner/expr_matcher/adapter_hs/adapter_hs.h deleted file mode 100644 index c9de7d2..0000000 --- a/scanner/expr_matcher/adapter_hs/adapter_hs.h +++ /dev/null @@ -1,75 +0,0 @@ -/* -********************************************************************************************** -* File: adapter_hs.h -* Description: -* Authors: Liu wentan -* Date: 2022-10-31 -* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. -*********************************************************************************************** -*/ - -#ifndef _ADAPTER_HS_H_ -#define _ADAPTER_HS_H_ - -#ifdef __cplusplus -extern "C" -{ -#endif - -#include -#include - -#include "log/log.h" -#include "../expr_matcher.h" - -int adapter_hs_verify_regex_expression(const char *regex_expr, struct log_handle *logger); -/** - * @brief new adapter_hs instance - * - * @param rules: logic AND expression's array - * @param n_rule: the number of logic AND expression's array - * @param nr_worker_threads: the number of scan threads which will call adapter_hs_scan() - * - * @retval the pointer to adapter_hs instance -*/ -void *adapter_hs_new(struct expr_rule *rules, size_t n_rule, - size_t n_literal_pattern, size_t n_regex_pattern, - size_t n_worker_thread, struct log_handle *logger); - -/** - * @brief scan input data to match logic AND expression, return all matched expr_id - * - * @param instance: adapter_hs instance obtained by adapter_hs_new() - * @param thread_id: the thread_id of caller - * @param data: data to be scanned - * @param data_len: the length of data to be scanned - * @param results: the array of expr_id - * @param n_results: number of elements in array of expr_id -*/ -int adapter_hs_scan(void *hs_instance, int thread_id, const char *data, size_t data_len, - struct expr_scan_result *results, size_t n_result, size_t *n_hit_result); - -/** - * @brief destroy adapter_hs instance - * - * @param instance: adapter_hs instance obtained by adapter_hs_new() -*/ -void adapter_hs_free(void *instance); - -/** - * @brief open adapter_hs stream after adapter_hs instance initialized for stream scan - * -*/ -void *adapter_hs_stream_open(void *hs_instance, int thread_id); - -int adapter_hs_scan_stream(void *stream, const char *data, size_t data_len, - struct expr_scan_result *results, size_t n_result, - size_t *n_hit_result); - -void adapter_hs_stream_close(void *stream); - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file diff --git a/scanner/expr_matcher/adapter_rs/adapter_rs.cpp b/scanner/expr_matcher/adapter_rs/adapter_rs.cpp deleted file mode 100644 index 1459ab1..0000000 --- a/scanner/expr_matcher/adapter_rs/adapter_rs.cpp +++ /dev/null @@ -1,708 +0,0 @@ -/* -********************************************************************************************** -* File: adapter_rs.cpp -* Description: -* Authors: Liu wentan -* Date: 2022-10-31 -* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. -*********************************************************************************************** -*/ - -#include -#include -#include -#include -#include -#include - -#include "rulescan.h" -#include "adapter_rs.h" -#include "uthash/utarray.h" -#include "uthash/uthash.h" -#include "maat_utils.h" -#include "../../bool_matcher/bool_matcher.h" - -#define MAX_HIT_PATTERN_NUM 512 - -pid_t rs_gettid() -{ - return syscall(SYS_gettid); -} - -static const char *rs_module_name_str(const char *name) -{ - static __thread char module[64]; - snprintf(module, sizeof(module), "%s(%d)", name, rs_gettid()); - - return module; -} - -#define MODULE_ADAPTER_RS rs_module_name_str("maat.adapter_rs") - -struct adpt_rs_compile_data { - struct scan_pattern *patterns; - size_t n_patterns; -}; - -struct adapter_rs_stream { - int thread_id; - size_t offset; /* current stream offset */ - rs_stream_t *literal_stream; - rs_stream_t *regex_stream; - struct adapter_rs_runtime *ref_rs_rt; - - struct log_handle *logger; -}; - -/* adapter_rs runtime */ -struct adapter_rs_runtime { - rs_database_t *literal_db; - rs_database_t *regex_db; - - struct bool_expr_match **bool_match_buffs; /* per thread */ - struct adapter_rs_stream **streams; /* per thread */ - struct matched_pattern **matched_pats; /* per thread */ - struct bool_matcher *bm; -}; - -/* adapter_rs instance */ -struct adapter_rs { - size_t n_worker_thread; - size_t n_expr; - size_t n_patterns; - struct adapter_rs_runtime *rs_rt; - struct pattern_attribute *rs_attr; - struct log_handle *logger; -}; - -struct pattern_offset { - long long start; - long long end; -}; - -struct pattern_attribute { - long long pattern_id; - enum expr_match_mode match_mode; - struct pattern_offset offset; - size_t pattern_len; -}; - -struct matched_pattern { - UT_array *pattern_ids; - size_t n_patterns; - struct pattern_attribute *ref_rs_attr; -}; - -int adapter_rs_verify_regex_expression(const char *regex_expr, - struct log_handle *logger) -{ - int ret = rs_verify_regex(regex_expr); - if (ret == 0) { - log_error(logger, MODULE_ADAPTER_RS, - "[%s:%d] illegal regex expression: \"%s\"", - __FUNCTION__, __LINE__, regex_expr); - } - - return ret; -} -/** - * @brief build rs database for literal string and regex expression respectively - * - * @retval 0(success) -1(failed) -*/ -static int adpt_rs_build_database(struct adapter_rs_runtime *rs_rt, - size_t n_worker_thread, - struct adpt_rs_compile_data *literal_cd, - struct adpt_rs_compile_data *regex_cd, - struct log_handle *logger) -{ - if (NULL == rs_rt) { - return -1; - } - - int ret = 0; - if (literal_cd != NULL) { - ret = rs_compile_lit(literal_cd->patterns, literal_cd->n_patterns, - &rs_rt->literal_db); - if (ret < 0) { - log_error(logger, MODULE_ADAPTER_RS, "[%s:%d] compile error", - __FUNCTION__, __LINE__); - return -1; - } - } - - if (regex_cd != NULL) { - size_t n_failed_pats = 0; - ret = rs_compile_regex(regex_cd->patterns, regex_cd->n_patterns, - n_worker_thread, &rs_rt->regex_db, &n_failed_pats); - if (ret < 0) { - log_error(logger, MODULE_ADAPTER_RS, "[%s:%d] compile error", - __FUNCTION__, __LINE__); - return -1; - } - } - - return 0; -} - -static struct adpt_rs_compile_data *adpt_rs_compile_data_new(size_t n_patterns) -{ - struct adpt_rs_compile_data *rs_cd = ALLOC(struct adpt_rs_compile_data, 1); - rs_cd->patterns = ALLOC(struct scan_pattern, n_patterns); - rs_cd->n_patterns = n_patterns; - - return rs_cd; -} - -static void adpt_rs_compile_data_free(struct adpt_rs_compile_data *rs_cd) -{ - if (NULL == rs_cd) { - return; - } - - if (rs_cd->patterns != NULL) { - for (size_t i = 0; i < rs_cd->n_patterns; i++) { - if (rs_cd->patterns[i].pattern != NULL) { - FREE(rs_cd->patterns[i].pattern); - } - } - - FREE(rs_cd->patterns); - } - - FREE(rs_cd); -} - -static void populate_compile_data(struct adpt_rs_compile_data *compile_data, - size_t index, long long pattern_id, char *pat, - size_t pat_len, int case_sensitive) -{ - compile_data->patterns[index].id = pattern_id; - compile_data->patterns[index].case_sensitive = case_sensitive; - compile_data->patterns[index].pattern = ALLOC(char, pat_len + 1); - memcpy(compile_data->patterns[index].pattern, pat, pat_len); - compile_data->patterns[index].pattern_len = pat_len; -} - -static struct bool_expr *bool_exprs_new(struct expr_rule *rules, size_t n_rule, - struct pattern_attribute *pattern_attr, - struct adpt_rs_compile_data *literal_cd, - struct adpt_rs_compile_data *regex_cd, - size_t *n_pattern) -{ - long long pattern_idx = 0; - size_t literal_idx = 0; - size_t regex_idx = 0; - - struct bool_expr *bool_exprs = ALLOC(struct bool_expr, n_rule); - - /* populate adpt_rs_compile_data and bool_expr */ - for (size_t i = 0; i < n_rule; i++) { - - for (size_t j = 0; j < rules[i].n_patterns; j++) { - pattern_attr[pattern_idx].pattern_id = pattern_idx; - pattern_attr[pattern_idx].match_mode = rules[i].patterns[j].match_mode; - pattern_attr[pattern_idx].pattern_len = rules[i].patterns[j].pat_len; - - if (pattern_attr[pattern_idx].match_mode == EXPR_MATCH_MODE_SUB || - pattern_attr[pattern_idx].match_mode == EXPR_MATCH_MODE_EXACTLY) { - pattern_attr[pattern_idx].offset.start = rules[i].patterns[j].start_offset; - pattern_attr[pattern_idx].offset.end = rules[i].patterns[j].end_offset; - } - - /* literal pattern */ - if (rules[i].patterns[j].type == EXPR_PATTERN_TYPE_STR) { - populate_compile_data(literal_cd, literal_idx, pattern_idx, - rules[i].patterns[j].pat, rules[i].patterns[j].pat_len, - rules[i].patterns[j].case_sensitive); - literal_idx++; - } else { - /* regex pattern */ - populate_compile_data(regex_cd, regex_idx, pattern_idx, - rules[i].patterns[j].pat, rules[i].patterns[j].pat_len, - rules[i].patterns[j].case_sensitive); - regex_idx++; - } - - bool_exprs[i].items[j].item_id = pattern_idx++; - bool_exprs[i].items[j].not_flag = 0; - } - - bool_exprs[i].expr_id = rules[i].expr_id; - bool_exprs[i].item_num = rules[i].n_patterns; - bool_exprs[i].user_tag = rules[i].tag; - } - - *n_pattern = pattern_idx; - - return bool_exprs; -} - -UT_icd ut_rs_pattern_id_icd = {sizeof(unsigned long long), NULL, NULL, NULL}; -void *adapter_rs_new(struct expr_rule *rules, size_t n_rule, - size_t n_literal_pattern, size_t n_regex_pattern, - size_t n_worker_thread, struct log_handle *logger) -{ - /* get the sum of pattern */ - size_t i = 0; - struct adpt_rs_compile_data *literal_cd = NULL; - struct adpt_rs_compile_data *regex_cd = NULL; - - if (n_literal_pattern > 0) { - literal_cd = adpt_rs_compile_data_new(n_literal_pattern); - } - - if (n_regex_pattern > 0) { - regex_cd = adpt_rs_compile_data_new(n_regex_pattern); - } - - size_t pattern_cnt = n_literal_pattern + n_regex_pattern; - struct adapter_rs *rs_inst = ALLOC(struct adapter_rs, 1); - rs_inst->rs_attr = ALLOC(struct pattern_attribute, pattern_cnt); - rs_inst->logger = logger; - rs_inst->n_worker_thread = n_worker_thread; - rs_inst->n_expr = n_rule; - - struct bool_expr *bool_exprs = bool_exprs_new(rules, n_rule, rs_inst->rs_attr, - literal_cd, regex_cd, &pattern_cnt); - if (NULL == bool_exprs) { - return NULL; - } - rs_inst->n_patterns = pattern_cnt; - - /* create bool matcher */ - size_t mem_size = 0; - int rs_ret = 0; - - rs_inst->rs_rt = ALLOC(struct adapter_rs_runtime, 1); - - //rs_rt->bm - rs_inst->rs_rt->bm = bool_matcher_new(bool_exprs, n_rule, &mem_size); - if (rs_inst->rs_rt->bm != NULL) { - log_info(logger, MODULE_ADAPTER_RS, - "Adapter_rs module: build bool matcher of %zu expressions" - " with %zu bytes memory", n_rule, mem_size); - } else { - log_error(logger, MODULE_ADAPTER_RS, - "[%s:%d] Adapter_rs module: build bool matcher failed", - __FUNCTION__, __LINE__); - - rs_ret = -1; - } - FREE(bool_exprs); - - /* build rs database rs_rt->literal_db & rs_rt->regex_db */ - int ret = adpt_rs_build_database(rs_inst->rs_rt, n_worker_thread, - literal_cd, regex_cd, logger); - if (ret < 0) { - rs_ret = -1; - } - - if (literal_cd != NULL) { - adpt_rs_compile_data_free(literal_cd); - } - - if (regex_cd != NULL) { - adpt_rs_compile_data_free(regex_cd); - } - - if (rs_ret < 0) { - goto error; - } - - /* alloc scratch */ - rs_inst->rs_rt->bool_match_buffs = ALLOC(struct bool_expr_match *, n_worker_thread); - for (i = 0; i < n_worker_thread; i++) { - rs_inst->rs_rt->bool_match_buffs[i] = ALLOC(struct bool_expr_match, MAX_HIT_EXPR_NUM); - } - - rs_inst->rs_rt->streams = ALLOC(struct adapter_rs_stream *, n_worker_thread); - for (i = 0; i < n_worker_thread; i++) { - rs_inst->rs_rt->streams[i] = (struct adapter_rs_stream *)adapter_rs_stream_open(rs_inst, i); - } - - rs_inst->rs_rt->matched_pats = ALLOC(struct matched_pattern *, n_worker_thread); - for (i = 0; i < n_worker_thread; i++) { - rs_inst->rs_rt->matched_pats[i] = ALLOC(struct matched_pattern, 1); - rs_inst->rs_rt->matched_pats[i]->ref_rs_attr = rs_inst->rs_attr; - rs_inst->rs_rt->matched_pats[i]->n_patterns = rs_inst->n_patterns; - utarray_new(rs_inst->rs_rt->matched_pats[i]->pattern_ids, &ut_rs_pattern_id_icd); - utarray_reserve(rs_inst->rs_rt->matched_pats[i]->pattern_ids, MAX_HIT_PATTERN_NUM); - } - - return rs_inst; -error: - adapter_rs_free(rs_inst); - return NULL; -} - -void adapter_rs_free(void *rs_instance) -{ - if (NULL == rs_instance) { - return; - } - - size_t i = 0; - struct adapter_rs *rs_inst = (struct adapter_rs *)rs_instance; - - if (rs_inst->rs_rt != NULL) { - if (rs_inst->rs_rt->literal_db != NULL) { - rs_free_database(rs_inst->rs_rt->literal_db); - rs_inst->rs_rt->literal_db = NULL; - } - - if (rs_inst->rs_rt->regex_db != NULL) { - rs_free_database(rs_inst->rs_rt->regex_db); - rs_inst->rs_rt->regex_db = NULL; - } - - if (rs_inst->rs_rt->bool_match_buffs != NULL) { - for (i = 0; i < rs_inst->n_worker_thread; i++) { - if (rs_inst->rs_rt->bool_match_buffs[i] != NULL) { - FREE(rs_inst->rs_rt->bool_match_buffs[i]); - } - } - - FREE(rs_inst->rs_rt->bool_match_buffs); - } - - if (rs_inst->rs_rt->bm != NULL) { - bool_matcher_free(rs_inst->rs_rt->bm); - rs_inst->rs_rt->bm = NULL; - } - - if (rs_inst->rs_rt->streams != NULL) { - for (i = 0; i < rs_inst->n_worker_thread; i++) { - if (rs_inst->rs_rt->streams[i] != NULL) { - adapter_rs_stream_close(rs_inst->rs_rt->streams[i]); - rs_inst->rs_rt->streams[i] = NULL; - } - } - FREE(rs_inst->rs_rt->streams); - } - - if (rs_inst->rs_rt->matched_pats != NULL) { - for (i = 0; i < rs_inst->n_worker_thread; i++) { - if (rs_inst->rs_rt->matched_pats[i] != NULL) { - utarray_free(rs_inst->rs_rt->matched_pats[i]->pattern_ids); - FREE(rs_inst->rs_rt->matched_pats[i]); - } - } - FREE(rs_inst->rs_rt->matched_pats); - } - - FREE(rs_inst->rs_rt); - } - - if (rs_inst->rs_attr != NULL) { - FREE(rs_inst->rs_attr); - } - - FREE(rs_inst); -} - -static inline int compare_pattern_id(const void *a, const void *b) -{ - long long ret = *(const unsigned long long *)a - *(const unsigned long long *)b; - if (ret == 0) { - return 0; - } else if(ret < 0) { - return -1; - } else { - return 1; - } -} - -/** - * @param id: pattern id -*/ -static int matched_event_cb(unsigned int id, int pos_offset, int from, int to, - size_t data_len, void *ctx) -{ - // put id in set - unsigned long long pattern_id = id; - struct matched_pattern *matched_pat = (struct matched_pattern *)ctx; - - if (pattern_id > matched_pat->n_patterns || id < 0) { - return 0; - } - - if (utarray_len(matched_pat->pattern_ids) >= MAX_HIT_PATTERN_NUM) { - return 0; - } - - // duplicate pattern_id - if (utarray_find(matched_pat->pattern_ids, &pattern_id, compare_pattern_id)) { - return 0; - } - - int ret = 0; - struct pattern_attribute pat_attr = matched_pat->ref_rs_attr[id]; - - switch (pat_attr.match_mode) { - case EXPR_MATCH_MODE_EXACTLY: - if (0 == (from + pos_offset) && (int)data_len == (to + pos_offset)) { - ret = 1; - } - break; - case EXPR_MATCH_MODE_SUB: - if (pat_attr.offset.start == -1 && - pat_attr.offset.end == -1) { - ret = 1; - break; - } - - if (pat_attr.offset.start == -1) { - if ((long long)(to + pos_offset - 1) <= pat_attr.offset.end) { - ret = 1; - break; - } - } - - if (pat_attr.offset.end == -1) { - if ((long long)(from + pos_offset) >= pat_attr.offset.start) { - ret = 1; - break; - } - } - - if ((long long)(from + pos_offset) >= pat_attr.offset.start && - (long long)(to + pos_offset - 1) <= pat_attr.offset.end) { - ret = 1; - } - break; - case EXPR_MATCH_MODE_PREFIX: - if (0 == (from + pos_offset)) { - ret = 1; - } - break; - case EXPR_MATCH_MODE_SUFFIX: - if ((to + pos_offset) == (int)data_len) { - ret = 1; - } - break; - default: - break; - } - - if (1 == ret) { - utarray_push_back(matched_pat->pattern_ids, &pattern_id); - utarray_sort(matched_pat->pattern_ids, compare_pattern_id); - } - - return 0; -} - -void *adapter_rs_stream_open(void *rs_instance, int thread_id) -{ - if (NULL == rs_instance || thread_id < 0) { - return NULL; - } - - struct adapter_rs *rs_inst = (struct adapter_rs *)rs_instance; - struct adapter_rs_stream *rs_stream = ALLOC(struct adapter_rs_stream, 1); - - rs_stream->logger = rs_inst->logger; - rs_stream->thread_id = thread_id; - rs_stream->ref_rs_rt = rs_inst->rs_rt; - - int err_count = 0; - if (rs_inst->rs_rt->literal_db != NULL) { - rs_stream->literal_stream = rs_open_stream(rs_inst->rs_rt->literal_db, 0, 128); - if (NULL == rs_stream->literal_stream) { - log_error(rs_inst->logger, MODULE_ADAPTER_RS, "rs_open_stream failed"); - err_count++; - } - } - - if (rs_inst->rs_rt->regex_db != NULL) { - rs_stream->regex_stream = rs_open_stream(rs_inst->rs_rt->regex_db, 0, 128); - if (NULL == rs_stream->regex_stream) { - log_error(rs_inst->logger, MODULE_ADAPTER_RS, "rs_open_stream failed"); - err_count++; - } - } - - if (err_count > 0) { - goto error; - } - - return rs_stream; -error: - if (rs_stream->literal_stream != NULL) { - rs_close_stream(rs_stream->literal_stream); - rs_stream->literal_stream = NULL; - } - - if (rs_stream->regex_stream != NULL) { - rs_close_stream(rs_stream->regex_stream); - rs_stream->regex_stream = NULL; - } - - FREE(rs_stream); - return NULL; -} - -void adapter_rs_stream_close(void *rs_stream) -{ - if (NULL == rs_stream) { - return; - } - - struct adapter_rs_stream *stream = (struct adapter_rs_stream *)rs_stream; - if (stream->ref_rs_rt != NULL) { - if (stream->literal_stream != NULL) { - rs_close_stream(stream->literal_stream); - stream->literal_stream = NULL; - } - - if (stream->regex_stream != NULL) { - rs_close_stream(stream->regex_stream); - stream->regex_stream = NULL; - } - } - - /* rs_stream->rs_rt point to rs_instance->rs_rt which will call free - same as rs_attr */ - stream->ref_rs_rt = NULL; - FREE(stream); -} - -int adapter_rs_scan_stream(void *rs_stream, const char *data, size_t data_len, - struct expr_scan_result *results, size_t n_result, - size_t *n_hit_result) -{ - if (NULL == rs_stream || NULL == data || 0 == data_len || - NULL == results || 0 == n_result || NULL == n_hit_result) { - return -1; - } - - int ret = 0, err_count = 0; - struct adapter_rs_stream *stream = (struct adapter_rs_stream *)rs_stream; - int thread_id = stream->thread_id; - struct adapter_rs_runtime *rs_rt = stream->ref_rs_rt; - struct matched_pattern *matched_pat = rs_rt->matched_pats[thread_id]; - - if (stream->literal_stream != NULL) { - ret = rs_scan_stream(stream->literal_stream, data, data_len, - matched_event_cb, matched_pat); - if (ret < 0) { - err_count++; - } - } - - if (stream->regex_stream != NULL) { - ret = rs_scan_stream(stream->regex_stream, data, data_len, - matched_event_cb, matched_pat); - if (ret < 0) { - err_count++; - } - } - - if (err_count == 2) { - return -1; - } - - size_t n_pattern_id = utarray_len(matched_pat->pattern_ids); - if (0 == n_pattern_id) { - *n_hit_result = 0; - return 0; - } - - unsigned long long pattern_ids[n_pattern_id]; - - for (size_t i = 0; i < n_pattern_id; i++) { - pattern_ids[i] = *(unsigned long long *)utarray_eltptr(matched_pat->pattern_ids, i); - } - - struct bool_expr_match *bool_matcher_results = rs_rt->bool_match_buffs[thread_id]; - int bool_matcher_ret = bool_matcher_match(rs_rt->bm, pattern_ids, n_pattern_id, - bool_matcher_results, MAX_HIT_EXPR_NUM); - if (bool_matcher_ret < 0) { - ret = -1; - goto next; - } - - if (bool_matcher_ret > (int)n_result) { - bool_matcher_ret = n_result; - } - - for (int index = 0; index < bool_matcher_ret; index++) { - results[index].rule_id = bool_matcher_results[index].expr_id; - results[index].user_tag = bool_matcher_results[index].user_tag; - } - *n_hit_result = bool_matcher_ret; - -next: - utarray_clear(matched_pat->pattern_ids); - - return ret; -} - -int adapter_rs_scan(void *rs_instance, int thread_id, const char *data, size_t data_len, - struct expr_scan_result *results, size_t n_result, size_t *n_hit_result) -{ - if (NULL == rs_instance || NULL == data || (0 == data_len) || - NULL == results || 0 == n_result || NULL == n_hit_result) { - return -1; - } - - int ret = 0, err_count = 0; - struct adapter_rs *rs_inst = (struct adapter_rs *)rs_instance; - struct adapter_rs_runtime *rs_rt = rs_inst->rs_rt; - struct matched_pattern *matched_pat = rs_rt->matched_pats[thread_id]; - - if (rs_rt->literal_db != NULL) { - ret = rs_scan(rs_rt->literal_db, thread_id, data, data_len, - 0, matched_event_cb, matched_pat); - if (ret < 0) { - err_count++; - } - } - - if (rs_rt->regex_db != NULL) { - ret = rs_scan(rs_rt->regex_db, thread_id, data, data_len, - 0, matched_event_cb, matched_pat); - if (ret < 0) { - err_count++; - } - } - - if (err_count == 2) { - return -1; - } - - size_t n_pattern_id = utarray_len(matched_pat->pattern_ids); - if (0 == n_pattern_id) { - *n_hit_result = 0; - return 0; - } - - unsigned long long pattern_ids[n_pattern_id]; - for (size_t i = 0; i < n_pattern_id; i++) { - pattern_ids[i] = *(unsigned long long *)utarray_eltptr(matched_pat->pattern_ids, i); - } - - struct bool_expr_match *bool_matcher_results = rs_rt->bool_match_buffs[thread_id]; - int bool_matcher_ret = bool_matcher_match(rs_rt->bm, pattern_ids, n_pattern_id, - bool_matcher_results, MAX_HIT_EXPR_NUM); - if (bool_matcher_ret < 0) { - ret = -1; - goto next; - } - - if (bool_matcher_ret > (int)n_result) { - bool_matcher_ret = n_result; - } - - for (int index = 0; index < bool_matcher_ret; index++) { - results[index].rule_id = bool_matcher_results[index].expr_id; - results[index].user_tag = bool_matcher_results[index].user_tag; - } - *n_hit_result = bool_matcher_ret; - -next: - utarray_clear(matched_pat->pattern_ids); - - return ret; -} \ No newline at end of file diff --git a/scanner/expr_matcher/adapter_rs/adapter_rs.h b/scanner/expr_matcher/adapter_rs/adapter_rs.h deleted file mode 100644 index c43e553..0000000 --- a/scanner/expr_matcher/adapter_rs/adapter_rs.h +++ /dev/null @@ -1,78 +0,0 @@ -/* -********************************************************************************************** -* File: adapter_rs.h -* Description: -* Authors: Liu wentan -* Date: 2023-06-30 -* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. -*********************************************************************************************** -*/ - -#ifndef _ADAPTER_RS_H_ -#define _ADAPTER_RS_H_ - -#ifdef __cplusplus -extern "C" -{ -#endif - -#include - -#include "log/log.h" - -#include "../expr_matcher.h" - -int adapter_rs_verify_regex_expression(const char *regex_expr, - struct log_handle *logger); - -/** - * @brief new adapter_rs instance - * - * @param rules: logic AND expression's array - * @param n_rule: the number of logic AND expression's array - * @param n_worker_threads: the number of scan threads which will call adapter_rs_scan() - * - * @retval the pointer to adapter_rs instance -*/ -void *adapter_rs_new(struct expr_rule *rules, size_t n_rule, - size_t n_literal_pattern, size_t n_regex_pattern, - size_t n_worker_thread, struct log_handle *logger); - -void adapter_rs_free(void *rs_instance); - -/** - * @brief scan input data to match logic AND expression, return all matched expr_id - * - * @param rs_instance: adapter_rs instance obtained by adapter_rs_new() - * @param thread_id: the thread_id of caller - * @param scan_data: data to be scanned - * @param data_len: the length of data to be scanned - * @param result_array: the array to store hit expr_id which allocated by caller - * @param n_result_array: number of elements in array of expr_id -*/ -int adapter_rs_scan(void *rs_instance, int thread_id, - const char *scan_data, size_t data_len, - struct expr_scan_result *result_array, - size_t n_result_array, size_t *n_hit_results); - -/** - * @brief - */ -void *adapter_rs_stream_open(void *rs_instance, int thread_id); - -/** - * @brief - */ -int adapter_rs_scan_stream(void *rs_stream, const char *scan_data, - size_t data_len, struct expr_scan_result *result_array, - size_t n_result_array, size_t *n_hit_results); -/** - * @brief - */ -void adapter_rs_stream_close(void *rs_stream); - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file diff --git a/scanner/expr_matcher/expr_matcher.cpp b/scanner/expr_matcher/expr_matcher.cpp deleted file mode 100644 index 3a37383..0000000 --- a/scanner/expr_matcher/expr_matcher.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* -********************************************************************************************** -* File: expr_matcher.cpp -* Description: -* Authors: Liu wentan -* Date: 2023-06-30 -* Copyright: (c) Since 2023 Geedge Networks, Ltd. All rights reserved. -*********************************************************************************************** -*/ - -#include -#include -#include - -#include "log/log.h" -#include "expr_matcher.h" -#include "maat_utils.h" -#include "adapter_hs/adapter_hs.h" -#include "adapter_rs/adapter_rs.h" - -pid_t expr_matcher_gettid() -{ - return syscall(SYS_gettid); -} - -static const char *expr_matcher_module_name_str(const char *name) -{ - static __thread char module[64]; - snprintf(module, sizeof(module), "%s(%d)", name, expr_matcher_gettid()); - - return module; -} - -#define MODULE_EXPR_MATCHER expr_matcher_module_name_str("maat.expr_matcher") - -struct expr_matcher { - enum expr_engine_type engine_type; - void *engine; - struct log_handle *logger; -}; - -struct expr_matcher_stream { - enum expr_engine_type engine_type; - void *handle; -}; - -struct expr_engine_operations { - enum expr_engine_type type; - void *(*engine_new)(struct expr_rule *rules, size_t n_rule, - size_t n_literal_pattern, size_t n_regex_pattern, - size_t n_worker_thread, struct log_handle *logger); - void (*engine_free)(void *engine); - int (*engine_scan)(void *engine, int thread_id, const char *scan_data, - size_t data_len, struct expr_scan_result *result_array, - size_t n_result_array, size_t *n_hit_result); - void *(*engine_stream_open)(void *engine, int thread_id); - void (*engine_stream_close)(void *stream); - int (*engine_scan_stream)(void *stream, const char *scan_data, size_t data_len, - struct expr_scan_result *result_array, size_t n_result_array, - size_t *n_hit_result); -}; - -struct expr_engine_operations expr_engine_ops[EXPR_ENGINE_TYPE_MAX] = { - { - .type = EXPR_ENGINE_TYPE_HS, - .engine_new = adapter_hs_new, - .engine_free = adapter_hs_free, - .engine_scan = adapter_hs_scan, - .engine_stream_open = adapter_hs_stream_open, - .engine_stream_close = adapter_hs_stream_close, - .engine_scan_stream = adapter_hs_scan_stream - }, - { - .type = EXPR_ENGINE_TYPE_RS, - .engine_new = adapter_rs_new, - .engine_free = adapter_rs_free, - .engine_scan = adapter_rs_scan, - .engine_stream_open = adapter_rs_stream_open, - .engine_stream_close = adapter_rs_stream_close, - .engine_scan_stream = adapter_rs_scan_stream - } -}; - -int expr_matcher_verify_regex_expression(const char *regex_expr, - struct log_handle *logger) -{ - int ret = adapter_hs_verify_regex_expression(regex_expr, logger); - if (ret == 0) { - return 0; - } - - return adapter_rs_verify_regex_expression(regex_expr, logger); -} - -struct expr_matcher * -expr_matcher_new(struct expr_rule *rules, size_t n_rule, enum expr_engine_type engine_type, - size_t n_worker_thread, struct log_handle *logger) -{ - if (NULL == rules || 0 == n_rule || 0 == n_worker_thread || - (engine_type != EXPR_ENGINE_TYPE_HS && engine_type != EXPR_ENGINE_TYPE_RS)) { - log_error(logger, MODULE_EXPR_MATCHER, "[%s:%d]engine type:%d is illegal", - __FUNCTION__, __LINE__, engine_type); - return NULL; - } - - size_t i = 0, j = 0; - size_t literal_pat_num = 0; - size_t regex_pat_num = 0; - - for (i = 0; i < n_rule; i++) { - if (rules[i].n_patterns > MAX_EXPR_PATTERN_NUM) { - log_error(logger, MODULE_EXPR_MATCHER, - "[%s:%d] the number of patterns in one expression should less than" - " %d", __FUNCTION__, __LINE__, MAX_EXPR_PATTERN_NUM); - return NULL; - } - - for (j = 0; j < rules[i].n_patterns; j++) { - /* pat_len should not 0 */ - if (0 == rules[i].patterns[j].pat_len) { - log_error(logger, MODULE_EXPR_MATCHER, - "[%s:%d] expr pattern length should not 0", - __FUNCTION__, __LINE__); - return NULL; - } - - if (rules[i].patterns[j].type == EXPR_PATTERN_TYPE_STR) { - literal_pat_num++; - } else { - regex_pat_num++; - } - } - } - - if (0 == literal_pat_num && 0 == regex_pat_num) { - log_error(logger, MODULE_EXPR_MATCHER, - "[%s:%d] exprs has no valid pattern", __FUNCTION__, __LINE__); - return NULL; - } - - void *engine = expr_engine_ops[engine_type].engine_new(rules, n_rule, literal_pat_num, - regex_pat_num, n_worker_thread, - logger); - if (NULL == engine) { - log_error(logger, MODULE_EXPR_MATCHER, - "[%s:%d]expr_matcher engine_new failed.", __FUNCTION__, __LINE__); - return NULL; - } - - struct expr_matcher *matcher = ALLOC(struct expr_matcher, 1); - matcher->engine_type = engine_type; - matcher->engine = engine; - matcher->logger = logger; - - return matcher; -} - -void expr_matcher_free(struct expr_matcher *matcher) -{ - if (NULL == matcher) { - return; - } - - if (matcher->engine != NULL) { - expr_engine_ops[matcher->engine_type].engine_free(matcher->engine); - matcher->engine = NULL; - } - - FREE(matcher); -} - -int expr_matcher_match(struct expr_matcher *matcher, int thread_id, const char *scan_data, - size_t data_len, struct expr_scan_result *result_array, - size_t n_result_array, size_t *n_hit_results) -{ - if (NULL == matcher || thread_id < 0 || NULL == scan_data || 0 == data_len - || NULL == result_array || 0 == n_result_array || NULL == n_hit_results) { - return -1; - } - - return expr_engine_ops[matcher->engine_type].engine_scan(matcher->engine, thread_id, - scan_data, data_len, result_array, - n_result_array, n_hit_results); -} - -struct expr_matcher_stream * -expr_matcher_stream_open(struct expr_matcher *matcher, int thread_id) -{ - if (NULL == matcher || thread_id < 0) { - return NULL; - } - - void *s_handle = expr_engine_ops[matcher->engine_type].engine_stream_open(matcher->engine, - thread_id); - if (NULL == s_handle) { - log_error(matcher->logger, MODULE_EXPR_MATCHER, - "[%s:%d] expr_matcher engine_stream_open failed.", - __FUNCTION__, __LINE__); - return NULL; - } - - struct expr_matcher_stream *stream = ALLOC(struct expr_matcher_stream, 1); - stream->engine_type = matcher->engine_type; - stream->handle = s_handle; - - return stream; -} - -int expr_matcher_stream_match(struct expr_matcher_stream *stream, const char *scan_data, - size_t data_len, struct expr_scan_result *result_array, - size_t n_result_array, size_t *n_hit_results) -{ - if (NULL == stream || NULL == scan_data || 0 == data_len || NULL == result_array - || 0 == n_result_array || NULL == n_hit_results) { - return -1; - } - - return expr_engine_ops[stream->engine_type].engine_scan_stream(stream->handle, scan_data, - data_len, result_array, - n_result_array, n_hit_results); -} - -void expr_matcher_stream_close(struct expr_matcher_stream *stream) -{ - if (NULL == stream) { - return; - } - - if (stream->handle != NULL) { - expr_engine_ops[stream->engine_type].engine_stream_close(stream->handle); - stream->handle = NULL; - } - - FREE(stream); -} \ No newline at end of file diff --git a/scanner/expr_matcher/expr_matcher.h b/scanner/expr_matcher/expr_matcher.h deleted file mode 100644 index fb61854..0000000 --- a/scanner/expr_matcher/expr_matcher.h +++ /dev/null @@ -1,134 +0,0 @@ -/* -********************************************************************************************** -* File: expr_matcher.h -* Description: -* Authors: Liu wentan -* Date: 2023-06-30 -* Copyright: (c) Since 2023 Geedge Networks, Ltd. All rights reserved. -*********************************************************************************************** -*/ - -#ifndef _EXPR_MATCHER_H_ -#define _EXPR_MATCHER_H_ - -#ifdef __cplusplus -extern "C" -{ -#endif - -#include - -#include "log/log.h" - -#define MAX_EXPR_PATTERN_NUM 8 /* 每条与表达式最多由MAX_EXPR_ITEM_NUM个规则组成 */ -#define MAX_HIT_EXPR_NUM 1024 - -enum expr_engine_type { - EXPR_ENGINE_TYPE_HS = 0, /* default engine */ - EXPR_ENGINE_TYPE_RS, - EXPR_ENGINE_TYPE_MAX -}; - -enum expr_pattern_type { - EXPR_PATTERN_TYPE_STR = 0, /* pure literal string */ - EXPR_PATTERN_TYPE_REG = 1, /* regex expression */ -}; - -enum expr_case_sensitive { - EXPR_CASE_INSENSITIVE = 0, - EXPR_CASE_SENSITIVE -}; - -enum expr_match_mode { - EXPR_MATCH_MODE_INVALID = -1, - EXPR_MATCH_MODE_EXACTLY = 1, /* scan data must match pattern exactly */ - EXPR_MATCH_MODE_PREFIX, /* pattern must in the head of scan_data */ - EXPR_MATCH_MODE_SUFFIX, /* pattern must in the tail of scan_data */ - EXPR_MATCH_MODE_SUB /* pattern must in the range[l_offset, r_offset] of scan_data */ -}; - -struct expr_pattern { - enum expr_pattern_type type; - enum expr_match_mode match_mode; - enum expr_case_sensitive case_sensitive; - - /* - * just match in scan_data's range of [start_offset, end_offset], -1 means no limits - * for example: - * [-1, end_offset] means the pattern must in scan_data's [0 ~ start_offset] - * [start_offset, -1] means the pattern must in scan_data's [start_offset ~ data_end] - */ - int start_offset; - int end_offset; - - char *pat; - size_t pat_len; -}; - -struct expr_scan_result { - long long rule_id; - void *user_tag; -}; - -/* logic AND expression, such as (rule1 & rule2) */ -struct expr_rule { - long long expr_id; /* AND expression ID */ - size_t n_patterns; - struct expr_pattern patterns[MAX_EXPR_PATTERN_NUM]; - void *tag; /* user defined data, return with hit result */ -}; - -int expr_matcher_verify_regex_expression(const char *regex_expr, - struct log_handle *logger); - -/** - * @brief new expr matcher instance - * - * @param expr_array: logic AND expression's array - * @param n_expr_array: the number of logic AND expression's array - * @param n_worker_threads: the number of scan threads which will call adapter_rs_scan() - * -*/ -struct expr_matcher * -expr_matcher_new(struct expr_rule *rules, size_t n_rule, enum expr_engine_type type, - size_t n_worker_thread, struct log_handle *logger); - -void expr_matcher_free(struct expr_matcher *matcher); - -/** - * @brief scan input data to match logic AND expression, return all matched expr_id - * - * @param matcher: expr_matcher instance obtained by expr_matcher_new() - * @param thread_id: the thread_id of caller - * @param scan_data: data to be scanned - * @param data_len: the length of data to be scanned - * @param result_array: the array to store hit expr_id which allocated by caller - * @param n_result_array: number of elements in array of expr_id -*/ -int expr_matcher_match(struct expr_matcher *matcher, int thread_id, const char *scan_data, - size_t data_len, struct expr_scan_result *result_array, - size_t n_result_array, size_t *n_hit_results); - -/** - * @brief - */ -struct expr_matcher_stream * -expr_matcher_stream_open(struct expr_matcher *matcher, int thread_id); - -/** - * @brief - */ -int expr_matcher_stream_match(struct expr_matcher_stream *stream, const char *scan_data, - size_t data_len, struct expr_scan_result *result_array, - size_t n_result_array, size_t *n_hit_results); - -/** - * @brief - */ -void expr_matcher_stream_close(struct expr_matcher_stream *stream); - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4e21418..5b56b4a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,15 +23,13 @@ include_directories(/opt/MESA/include/MESA/) include_directories(${PROJECT_SOURCE_DIR}/include/) include_directories(${PROJECT_SOURCE_DIR}/deps/) include_directories(${PROJECT_SOURCE_DIR}/scanner) +include_directories(${PROJECT_SOURCE_DIR}/scanner/adapter_hs) include_directories(${PROJECT_SOURCE_DIR}/scanner/fqdn_engine) include_directories(${PROJECT_SOURCE_DIR}/scanner/bool_matcher) include_directories(${PROJECT_SOURCE_DIR}/scanner/ip_matcher) include_directories(${PROJECT_SOURCE_DIR}/scanner/flag_matcher) include_directories(${PROJECT_SOURCE_DIR}/scanner/interval_matcher) include_directories(${PROJECT_SOURCE_DIR}/src/inc_internal) -include_directories(${PROJECT_SOURCE_DIR}/scanner/expr_matcher) -include_directories(${PROJECT_SOURCE_DIR}/scanner/expr_matcher/adapter_hs) -include_directories(${PROJECT_SOURCE_DIR}/scanner/expr_matcher/adapter_rs) # Static Library Output add_library(maat_frame_static STATIC ${MAAT_SRC} ${LIB_SOURCE_FILES}) diff --git a/src/inc_internal/maat_compile.h b/src/inc_internal/maat_compile.h index 52395c4..e6e588e 100644 --- a/src/inc_internal/maat_compile.h +++ b/src/inc_internal/maat_compile.h @@ -37,6 +37,12 @@ void *group2compile_schema_new(cJSON *json, struct table_manager *tbl_mgr, void group2compile_schema_free(void *g2c_schema); int group2compile_associated_compile_table_id(void *g2c_schema); +int compile_table_set_ex_data_schema(struct compile_schema *compile_schema, int table_id, + maat_ex_new_func_t *new_func, + maat_ex_free_func_t *free_func, + maat_ex_dup_func_t *dup_func, + long argl, void *argp); + /* compile runtime API */ void *compile_runtime_new(void *compile_schema, size_t max_thread_num, struct maat_garbage_bin *garbage_bin, @@ -64,6 +70,14 @@ size_t compile_runtime_get_hit_paths(struct compile_runtime *compile_rt, int thr struct maat_hit_path *hit_path_array, size_t array_size, size_t n_internal_hit_path); +void *compile_runtime_get_ex_data(struct compile_runtime *compile_rt, + struct compile_schema *compile_schema, + long long compile_id); +void compile_runtime_ex_data_iterate(struct compile_runtime *compile_rt, + struct compile_schema *compile_schema); + +void compile_runtime_garbage_collect_routine(void *compile_runtime); + /* group2compile runtime API */ void *group2compile_runtime_new(void *g2c_schema, size_t max_thread_num, struct maat_garbage_bin *garbage_bin, @@ -105,13 +119,10 @@ size_t maat_compile_state_get_hit_groups(struct maat_compile_state *compile_stat struct maat_hit_group *hit_group_array, size_t array_size); -int maat_compile_state_get_compile_table_id(struct maat_compile_state *compile_state, - long long compile_id); - int maat_compile_state_has_NOT_clause(struct maat_compile_state *compile_state); #ifdef __cplusplus } #endif -#endif +#endif \ No newline at end of file diff --git a/src/inc_internal/maat_expr.h b/src/inc_internal/maat_expr.h index c6923f6..9f6c9c6 100644 --- a/src/inc_internal/maat_expr.h +++ b/src/inc_internal/maat_expr.h @@ -52,13 +52,14 @@ long long expr_runtime_get_version(void *expr_runtime); int expr_runtime_scan(struct expr_runtime *expr_rt, int thread_id, const char *data, size_t data_len, int vtable_id, struct maat_state *state); -struct expr_matcher_stream *expr_runtime_stream_open(struct expr_runtime *expr_rt, int thread_id); +struct adapter_hs_stream *expr_runtime_stream_open(struct expr_runtime *expr_rt, int thread_id); -int expr_runtime_stream_scan(struct expr_runtime *expr_rt, struct expr_matcher_stream *s_handle, - const char *data, size_t data_len, int vtable_id, struct maat_state *state); +int expr_runtime_stream_scan(struct expr_runtime *expr_rt, struct adapter_hs_stream *s_handle, + const char *data, size_t data_len, + int vtable_id, struct maat_state *state); void expr_runtime_stream_close(struct expr_runtime *expr_rt, int thread_id, - struct expr_matcher_stream *stream); + struct adapter_hs_stream *s_handle); int expr_runtime_set_scan_district(struct expr_runtime *expr_rt, const char *district, size_t district_len, long long *district_id); @@ -83,4 +84,4 @@ long long expr_runtime_scan_bytes(struct expr_runtime *expr_rt); } #endif -#endif +#endif \ No newline at end of file diff --git a/src/inc_internal/maat_kv.h b/src/inc_internal/maat_kv.h index a7ce735..896ec1d 100644 --- a/src/inc_internal/maat_kv.h +++ b/src/inc_internal/maat_kv.h @@ -26,15 +26,12 @@ void maat_kv_store_free(struct maat_kv_store *store); int maat_kv_register(struct maat_kv_store *store, const char *key, long long value); -int maat_kv_read(struct maat_kv_store *store, const char *key, long long *value_array, size_t n_array); +int maat_kv_read(struct maat_kv_store *store, const char *key, long long *value); -int maat_kv_read_unNull(struct maat_kv_store *store, const char *key, size_t key_sz, - long long *value_array, size_t n_array); +int maat_kv_read_unNull(struct maat_kv_store *store, const char *key, size_t key_sz, long long *value); int maat_kv_write(struct maat_kv_store *store, const char *key, long long value); -int maat_kv_append(struct maat_kv_store *store, const char *key, long long value); - struct maat_kv_store *maat_kv_store_duplicate(struct maat_kv_store *store); #ifdef __cplusplus diff --git a/src/inc_internal/maat_rule.h b/src/inc_internal/maat_rule.h index 2c9b9c5..90450ac 100644 --- a/src/inc_internal/maat_rule.h +++ b/src/inc_internal/maat_rule.h @@ -128,7 +128,6 @@ struct maat_options { int rule_effect_interval_ms; int rule_update_checking_interval_ms; - enum maat_expr_engine expr_engine; enum data_source input_mode; union { struct source_iris_ctx iris_ctx; @@ -226,4 +225,4 @@ void garbage_maat_kv_store_free(void *kv_store, void *arg); } #endif -#endif +#endif \ No newline at end of file diff --git a/src/inc_internal/maat_table.h b/src/inc_internal/maat_table.h index 7ade1bd..9efdb82 100644 --- a/src/inc_internal/maat_table.h +++ b/src/inc_internal/maat_table.h @@ -19,7 +19,6 @@ extern "C" #include #include -#include "maat.h" #include "log/log.h" #include "maat_garbage_collection.h" @@ -48,8 +47,7 @@ struct table_manager; struct table_manager * table_manager_create(const char *table_info_path, const char *accept_tags, - enum maat_expr_engine expr_engine, struct maat_garbage_bin *garbage_bin, - struct log_handle *logger); + struct maat_garbage_bin *garbage_bin, struct log_handle *logger); int table_manager_runtime_create(struct table_manager *tbl_mgr, size_t max_thread_num, struct maat_garbage_bin *garbage_bin); @@ -59,23 +57,7 @@ void table_manager_destroy(struct table_manager *tbl_mgr); size_t table_manager_table_size(struct table_manager *tbl_mgr); size_t table_manager_table_count(struct table_manager *tbl_mgr); -int table_manager_get_table_id(struct table_manager *tbl_mgr, const char *table_name); -/** - * @brief get table_name's all conjunction parents' table_id - * - * for example: "table_id": 1, - * "table_name":"COMPILE_CONJ" - * "db_tables":["COMPILE_DEFAULT", "COMPILE_ALIAS"] - * - * "table_id": 2, - * "table_name": "COMPILE_PLUGIN", - * "db_tables": ["COMPILE_DEFAULT"] - * - * so COMPILE_DEFAULT has two conjunction parents whose table_id is 1 and 2. -*/ -int table_manager_get_conj_parent_table_ids(struct table_manager *tbl_mgr, const char *table_name, - long long *table_ids_array, size_t n_table_ids_array); - +int table_manager_get_table_id(struct table_manager *tbl_mgr, const char *name); const char *table_manager_get_table_name(struct table_manager *tbl_mgr, int table_id); enum table_type table_manager_get_table_type(struct table_manager *tbl_mgr, int table_id); @@ -85,8 +67,6 @@ int table_manager_get_group2group_table_id(struct table_manager *tbl_mgr); int table_manager_get_valid_column(struct table_manager *tbl_mgr, int table_id); -enum maat_expr_engine table_manager_get_expr_engine(struct table_manager *tbl_mgr); - size_t table_manager_accept_tags_count(struct table_manager *tbl_mgr); int table_manager_accept_tags_match(struct table_manager *tbl_mgr, const char *tags); diff --git a/src/json2iris.c b/src/json2iris.c index de1ec7d..2b3127f 100644 --- a/src/json2iris.c +++ b/src/json2iris.c @@ -377,7 +377,7 @@ static int direct_write_rule(cJSON *json, struct maat_kv_store *str2int, if (1 == cmd[i].str2int_flag) { long long int_value = 0; char *p = item->valuestring; - ret = maat_kv_read(str2int, p, &int_value, 1); + ret = maat_kv_read(str2int, p, &int_value); if (ret < 0) { log_error(logger, MODULE_JSON2IRIS, "[%s:%d] %s's value %s is not valid format", @@ -636,7 +636,7 @@ static int write_region_rule(cJSON *region_json, int compile_id, int group_id, const char *table_type_str = item->valuestring; long long table_type_int; - int ret = maat_kv_read(p_iris->str2int_map, table_type_str, &(table_type_int), 1); + int ret = maat_kv_read(p_iris->str2int_map, table_type_str, &table_type_int); if (ret != 1) { log_error(logger, MODULE_JSON2IRIS, "[%s:%d] compile rule %d table name %s's table_type %s invalid", diff --git a/src/maat_api.c b/src/maat_api.c index 3a5cc5a..0b296a4 100644 --- a/src/maat_api.c +++ b/src/maat_api.c @@ -51,7 +51,7 @@ enum district_flag { struct maat_stream { struct maat *ref_maat_inst; - struct expr_matcher_stream *handle; //each physical table open one stream + struct adapter_hs_stream *handle; //each physical table open one stream long long last_full_version; long long expr_rt_version; struct log_handle *logger; @@ -70,7 +70,6 @@ struct maat_options* maat_options_new(void) options->rule_update_checking_interval_ms = 1 * 1000; options->gc_timeout_ms = 10 * 1000; options->input_mode = DATA_SOURCE_NONE; - options->expr_engine = MAAT_EXPR_ENGINE_HS; options->log_level = 0; return options; @@ -255,19 +254,6 @@ int maat_options_set_stat_file(struct maat_options *opts, const char *stat_filen return 0; } -int maat_options_set_expr_engine(struct maat_options *opts, - enum maat_expr_engine expr_engine) -{ - if (NULL == opts || - (expr_engine != MAAT_EXPR_ENGINE_HS && expr_engine != MAAT_EXPR_ENGINE_RS)) { - return -1; - } - - opts->expr_engine = expr_engine; - - return 0; -} - int maat_options_set_logger(struct maat_options *opts, const char *log_path, enum log_level level) { @@ -371,8 +357,7 @@ struct maat *maat_new(struct maat_options *opts, const char *table_info_path) pthread_mutex_init(&(maat_inst->background_update_mutex), NULL); maat_inst->tbl_mgr = table_manager_create(table_info_path, maat_inst->opts.accept_tags, - maat_inst->opts.expr_engine, maat_inst->garbage_bin, - maat_inst->logger); + maat_inst->garbage_bin, maat_inst->logger); if (NULL == maat_inst->tbl_mgr) { goto failed; } @@ -425,7 +410,12 @@ int maat_helper_verify_regex_expression(const char *regex_expr) return 0; } - return expr_matcher_verify_regex_expression(regex_expr, NULL); + int ret = adapter_hs_verify_regex_expression(regex_expr, NULL); + if (ret < 0) { + return 0; + } else { + return 1; + } } int maat_get_table_id(struct maat *maat_inst, const char *table_name) @@ -522,6 +512,32 @@ int maat_table_callback_register(struct maat *maat_inst, int table_id, return 0; } +static int compile_table_ex_schema_register(struct maat *maat_inst, int table_id, + maat_ex_new_func_t *new_func, + maat_ex_free_func_t *free_func, + maat_ex_dup_func_t *dup_func, + long argl, void *argp) +{ + void *schema = table_manager_get_schema(maat_inst->tbl_mgr, table_id); + assert(schema != NULL); + + int ret = compile_table_set_ex_data_schema((struct compile_schema *)schema, table_id, + new_func, free_func, dup_func, + argl, argp); + if (ret < 0) { + return -1; + } + + if (maat_inst->maat_rt != NULL) { + void *runtime = table_manager_get_runtime(maat_inst->tbl_mgr, table_id); + assert(runtime != NULL); + compile_runtime_ex_data_iterate((struct compile_runtime *)runtime, + (struct compile_schema *)schema); + } + + return 0; +} + static int generic_plugin_table_set_ex_schema(struct table_manager *tbl_mgr, int table_id, maat_ex_new_func_t *new_func, maat_ex_free_func_t *free_func, @@ -755,14 +771,16 @@ int maat_plugin_table_ex_schema_register(struct maat *maat_inst, int ret = -1; enum table_type table_type = table_manager_get_table_type(maat_inst->tbl_mgr, table_id); - if (table_type == TABLE_TYPE_PLUGIN || table_type == TABLE_TYPE_IP_PLUGIN || - table_type == TABLE_TYPE_BOOL_PLUGIN || table_type == TABLE_TYPE_FQDN_PLUGIN ) { + if (TABLE_TYPE_COMPILE == table_type) { + ret = compile_table_ex_schema_register(maat_inst, table_id, new_func, + free_func, dup_func, argl, argp); + } else { ret = generic_plugin_table_ex_schema_register(maat_inst, table_name, table_id, new_func, free_func, dup_func, argl, argp); } - pthread_mutex_unlock(&(maat_inst->background_update_mutex)); + pthread_mutex_unlock(&(maat_inst->background_update_mutex)); return ret; } @@ -793,7 +811,9 @@ void *maat_plugin_table_get_ex_data(struct maat *maat_inst, int table_id, void *ret = NULL; enum table_type table_type = table_manager_get_table_type(maat_inst->tbl_mgr, table_id); - if (TABLE_TYPE_PLUGIN == table_type) { + if (TABLE_TYPE_COMPILE == table_type) { + ret = compile_runtime_get_ex_data(runtime, schema, *(long long *)key); + } else if (TABLE_TYPE_PLUGIN == table_type) { ret = plugin_runtime_get_ex_data(runtime, schema, key, key_len); } @@ -1148,7 +1168,9 @@ int maat_scan_flag(struct maat *maat_inst, int table_id, return MAAT_SCAN_ERR; } + maat_runtime_ref_inc(maat_rt, state->thread_id); alignment_int64_array_add(maat_inst->stat->thread_call_cnt, state->thread_id, 1); + int hit_group_cnt = flag_scan(maat_inst->tbl_mgr, state->thread_id, flag, phy_table_id, vtable_id, state); if (hit_group_cnt < 0) { @@ -1156,8 +1178,6 @@ int maat_scan_flag(struct maat *maat_inst, int table_id, return MAAT_SCAN_ERR; } - maat_runtime_ref_inc(maat_rt, state->thread_id); - size_t sum_hit_compile_cnt = 0; if (hit_group_cnt > 0 || scan_status_should_compile_NOT(state)) { sum_hit_compile_cnt = group_to_compile(maat_inst, results, n_result, state); @@ -1237,7 +1257,9 @@ int maat_scan_integer(struct maat *maat_inst, int table_id, return MAAT_SCAN_ERR; } + maat_runtime_ref_inc(maat_rt, state->thread_id); alignment_int64_array_add(maat_inst->stat->thread_call_cnt, state->thread_id, 1); + int hit_group_cnt = interval_scan(maat_inst->tbl_mgr, state->thread_id, integer, phy_table_id, vtable_id, state); if (hit_group_cnt < 0) { @@ -1245,8 +1267,6 @@ int maat_scan_integer(struct maat *maat_inst, int table_id, return MAAT_SCAN_ERR; } - maat_runtime_ref_inc(maat_rt, state->thread_id); - size_t sum_hit_compile_cnt = 0; if (hit_group_cnt > 0 || scan_status_should_compile_NOT(state)) { sum_hit_compile_cnt = group_to_compile(maat_inst, results, n_result, state); @@ -1326,7 +1346,9 @@ int maat_scan_ipv4(struct maat *maat_inst, int table_id, uint32_t ip_addr, return MAAT_SCAN_ERR; } + maat_runtime_ref_inc(maat_rt, state->thread_id); alignment_int64_array_add(maat_inst->stat->thread_call_cnt, state->thread_id, 1); + int hit_group_cnt = ipv4_scan(maat_inst->tbl_mgr, state->thread_id, ip_addr, port, protocol, phy_table_id, vtable_id, state); if (hit_group_cnt < 0) { @@ -1334,8 +1356,6 @@ int maat_scan_ipv4(struct maat *maat_inst, int table_id, uint32_t ip_addr, return MAAT_SCAN_ERR; } - maat_runtime_ref_inc(maat_rt, state->thread_id); - size_t sum_hit_compile_cnt = 0; if (hit_group_cnt > 0 || scan_status_should_compile_NOT(state)) { sum_hit_compile_cnt = group_to_compile(maat_inst, results, n_result, state); @@ -1416,7 +1436,9 @@ int maat_scan_ipv6(struct maat *maat_inst, int table_id, return MAAT_SCAN_ERR; } + maat_runtime_ref_inc(maat_rt, state->thread_id); alignment_int64_array_add(maat_inst->stat->thread_call_cnt, state->thread_id, 1); + int hit_group_cnt = ipv6_scan(maat_inst->tbl_mgr, state->thread_id, ip_addr, port, protocol, phy_table_id, vtable_id, state); if (hit_group_cnt < 0) { @@ -1424,8 +1446,6 @@ int maat_scan_ipv6(struct maat *maat_inst, int table_id, return MAAT_SCAN_ERR; } - maat_runtime_ref_inc(maat_rt, state->thread_id); - size_t sum_hit_compile_cnt = 0; if (hit_group_cnt > 0 || scan_status_should_compile_NOT(state)) { sum_hit_compile_cnt = group_to_compile(maat_inst, results, n_result, state); @@ -1505,7 +1525,9 @@ int maat_scan_string(struct maat *maat_inst, int table_id, const char *data, return MAAT_SCAN_ERR; } + maat_runtime_ref_inc(maat_rt, state->thread_id); alignment_int64_array_add(maat_inst->stat->thread_call_cnt, state->thread_id, 1); + int hit_group_cnt = string_scan(maat_inst->tbl_mgr, state->thread_id, data, data_len, phy_table_id, vtable_id, state); if (hit_group_cnt < 0) { @@ -1513,8 +1535,6 @@ int maat_scan_string(struct maat *maat_inst, int table_id, const char *data, return MAAT_SCAN_ERR; } - maat_runtime_ref_inc(maat_rt, state->thread_id); - size_t sum_hit_compile_cnt = 0; if (hit_group_cnt > 0 || scan_status_should_compile_NOT(state)) { sum_hit_compile_cnt = group_to_compile(maat_inst, results, n_result, state); @@ -1589,8 +1609,8 @@ struct maat_stream *maat_stream_new(struct maat *maat_inst, int table_id, stream->expr_rt_version = expr_runtime_get_version(expr_rt); maat_runtime_ref_inc(maat_inst->maat_rt, state->thread_id); - struct expr_matcher_stream *handle = expr_runtime_stream_open((struct expr_runtime *)expr_rt, - state->thread_id); + struct adapter_hs_stream *handle = expr_runtime_stream_open((struct expr_runtime *)expr_rt, + state->thread_id); if (NULL == handle) { goto error; } @@ -1869,22 +1889,6 @@ int maat_state_set_scan_compile_table(struct maat_state *state, int compile_tabl return 0; } -int maat_state_get_compile_table_ids(struct maat_state *state, long long *compile_ids, - size_t n_compile_ids, int *compile_table_ids) -{ - if (NULL == state || NULL == compile_ids || 0 == n_compile_ids || - NULL == compile_table_ids) { - return -1; - } - - for (size_t i = 0; i < n_compile_ids; i++) { - compile_table_ids[i] = maat_compile_state_get_compile_table_id(state->compile_state, - compile_ids[i]); - } - - return n_compile_ids; -} - int maat_state_get_hit_paths(struct maat_state *state, struct maat_hit_path *paths, size_t n_path) { @@ -1956,4 +1960,4 @@ int maat_state_get_hit_groups(struct maat_state *state, enum maat_list_type type int maat_hit_group_compile_id(struct maat *instance, struct maat_hit_group *group) { return 0; -} +} \ No newline at end of file diff --git a/src/maat_compile.c b/src/maat_compile.c index 8c3e23b..4413dba 100644 --- a/src/maat_compile.c +++ b/src/maat_compile.c @@ -27,12 +27,16 @@ #include "alignment.h" #define MODULE_COMPILE module_name_str("maat.compile") +#define DEFAULT_GC_TIMEOUT_S 10 struct compile_schema { int compile_id_column; int rule_tag_column; int declared_clause_num_column; + int set_flag; + int gc_timeout_s; int table_id; //ugly + struct ex_data_schema ex_schema; struct table_manager *ref_tbl_mgr; struct log_handle *logger; }; @@ -53,6 +57,8 @@ struct compile_item { long long compile_id; char *table_line; size_t table_line_len; + struct compile_schema *ref_schema; + void **ex_data; char table_name[MAX_NAME_STR_LEN]; }; @@ -79,8 +85,7 @@ struct literal_clause { /* compile_runtime and group2compile_runtime share compile_hash_map */ struct compile_runtime { struct bool_matcher *bm; - struct rcu_hash_table *cfg_hash; // - struct rcu_hash_table *tbl_cfg_hash; // + struct rcu_hash_table *cfg_hash; // struct maat_runtime *ref_maat_rt; time_t version; struct literal_clause *literal2clause_hash; @@ -132,15 +137,9 @@ struct maat_internal_hit_path { int vtable_id; }; -struct maat_compile_table { - long long compile_id; - int table_id; -}; - struct maat_compile_state { uint8_t this_scan_hit_item_flag; uint8_t not_clause_hit_flag; - uint8_t inc_hit_path_flag; int Nth_scan; time_t compile_rt_version; @@ -148,14 +147,12 @@ struct maat_compile_state { UT_array *internal_inc_hit_paths; UT_array *all_hit_clauses; UT_array *this_scan_hit_clauses; - UT_array *hit_compile_tables; }; UT_icd ut_literal_id_icd = {sizeof(struct maat_literal_id), NULL, NULL, NULL}; UT_icd ut_clause_id_icd = {sizeof(long long), NULL, NULL, NULL}; UT_icd ut_hit_group_icd = {sizeof(struct maat_hit_group), NULL, NULL, NULL}; UT_icd ut_hit_path_icd = {sizeof(struct maat_internal_hit_path), NULL, NULL, NULL}; -UT_icd ut_hit_compile_table_icd = {sizeof(struct maat_compile_table)}; static struct maat_compile *maat_compile_new(long long compile_id) { @@ -188,6 +185,23 @@ static int maat_compile_set(struct maat_compile *compile, const char *table_name return 0; } +static void *rule_ex_data_new(const char *table_name, int table_id, + const char *table_line, + struct ex_data_schema *ex_schema) +{ + void *ex_data = NULL; + + ex_schema->new_func(table_name, table_id, NULL, table_line, &ex_data, + ex_schema->argl, ex_schema->argp); + return ex_data; +} + +static void rule_ex_data_free(int table_id, void **ex_data, + const struct ex_data_schema *ex_schema) +{ + ex_schema->free_func(table_id, ex_data, ex_schema->argl, ex_schema->argp); +} + static int compile_accept_tag_match(struct compile_schema *schema, const char *line, const char *table_name, struct log_handle *logger) { @@ -262,11 +276,18 @@ compile_item_new(const char *table_line, struct compile_schema *schema, } compile_item->declared_clause_num = atoi(table_line + column_offset); + compile_item->ref_schema = schema; + compile_item->ex_data = ALLOC(void *, 1); memcpy(compile_item->table_name, table_name, sizeof(compile_item->table_name)); compile_item->table_line_len = strlen(table_line) + 1; compile_item->table_line = ALLOC(char, compile_item->table_line_len); memcpy(compile_item->table_line, table_line, compile_item->table_line_len); + if (1 == schema->set_flag) { + *(compile_item->ex_data) = rule_ex_data_new(table_name, schema->table_id, + compile_item->table_line, + &(schema->ex_schema)); + } return compile_item; error: FREE(compile_item); @@ -275,6 +296,17 @@ error: static void compile_item_free(struct compile_item *item) { + struct compile_schema *schema = item->ref_schema; + + if (1 == schema->set_flag) { + rule_ex_data_free(schema->table_id, item->ex_data, &(schema->ex_schema)); + *item->ex_data = NULL; + } + + if (item->ex_data != NULL) { + FREE(item->ex_data); + } + item->declared_clause_num = -1; if (item->table_line != NULL) { @@ -315,9 +347,70 @@ static void rcu_compile_cfg_free(void *user_ctx, void *data) maat_compile_free(compile); } -static void rcu_compile_table_cfg_free(void *user_ctx, void *data) +int compile_table_set_ex_data_schema(struct compile_schema *compile_schema, int table_id, + maat_ex_new_func_t *new_func, + maat_ex_free_func_t *free_func, + maat_ex_dup_func_t *dup_func, + long argl, void *argp) { - FREE(data); + if (1 == compile_schema->set_flag) { + log_error(compile_schema->logger, MODULE_COMPILE, + "[%s:%d] compile table(table_id:%d)ex schema has been set already," + " can't set again", __FUNCTION__, __LINE__, table_id); + return -1; + } + + compile_schema->ex_schema.new_func = new_func; + compile_schema->ex_schema.free_func = free_func; + compile_schema->ex_schema.dup_func = dup_func; + compile_schema->ex_schema.argl = argl; + compile_schema->ex_schema.argp = argp; + compile_schema->set_flag = 1; + + return 0; +} + +static void *compile_runtime_get_user_data(struct compile_runtime *compile_rt, + long long compile_id) +{ + struct maat_compile *compile = rcu_hash_find(compile_rt->cfg_hash, + (char *)&compile_id, + sizeof(long long)); + void *ret = NULL; + if (compile != NULL) { + ret = compile->user_data; + } + + return ret; +} + +static void rule_ex_data_new_cb(void *user_data, void *param, + const char *table_name, int table_id) +{ + struct ex_data_schema *ex_schema = (struct ex_data_schema *)param; + struct compile_item *compile = (struct compile_item *)user_data; + + void *ad = rule_ex_data_new(table_name, table_id, compile->table_line, ex_schema); + *compile->ex_data = ad; +} + +static void compile_runtime_user_data_iterate(struct compile_runtime *compile_rt, + void (*callback)(void *user_data, void *param, + const char *table_name, int table_id), + void *param, int table_id) +{ + /* I'm in background_update_mutex, config update can't happen, so no need to lock cfg_hash */ + void **data_array = NULL; + size_t data_cnt = rcu_hash_list(compile_rt->cfg_hash, &data_array); + + for (size_t i = 0; i < data_cnt; i++) { + struct maat_compile *compile = (struct maat_compile *)data_array[i]; + if (compile->user_data) { + callback(compile->user_data, param, compile->table_name, table_id); + } + } + + FREE(data_array); } void *compile_schema_new(cJSON *json, struct table_manager *tbl_mgr, @@ -371,6 +464,12 @@ void *compile_schema_new(cJSON *json, struct table_manager *tbl_mgr, goto error; } + //gc_timeout_s is optional + custom_item = cJSON_GetObjectItem(item, "gc_timeout_s"); + if (custom_item != NULL && custom_item->type == cJSON_Number) { + schema->gc_timeout_s = custom_item->valueint; + } + schema->ref_tbl_mgr = tbl_mgr; return schema; error: @@ -494,13 +593,14 @@ void *compile_runtime_new(void *compile_schema, size_t max_thread_num, return NULL; } + struct compile_schema *schema = (struct compile_schema *)compile_schema; struct compile_runtime *compile_rt = ALLOC(struct compile_runtime, 1); compile_rt->expr_match_buff = ALLOC(struct bool_expr_match, max_thread_num * MAX_SCANNER_HIT_COMPILE_NUM); compile_rt->version = time(NULL); - compile_rt->cfg_hash = rcu_hash_new(rcu_compile_cfg_free, NULL, 0); - compile_rt->tbl_cfg_hash = rcu_hash_new(rcu_compile_table_cfg_free, NULL, 0); + compile_rt->cfg_hash = rcu_hash_new(rcu_compile_cfg_free, NULL, + schema->gc_timeout_s + DEFAULT_GC_TIMEOUT_S); compile_rt->literal2clause_hash = NULL; compile_rt->logger = logger; compile_rt->ref_garbage_bin = garbage_bin; @@ -547,11 +647,6 @@ void compile_runtime_free(void *compile_runtime) compile_rt->cfg_hash = NULL; } - if (compile_rt->tbl_cfg_hash != NULL) { - rcu_hash_free(compile_rt->tbl_cfg_hash); - compile_rt->tbl_cfg_hash = NULL; - } - if (compile_rt->literal2clause_hash != NULL) { literal2clause_hash_free(compile_rt->literal2clause_hash); compile_rt->literal2clause_hash = NULL; @@ -892,30 +987,14 @@ static inline int compare_hit_group(const void *pa, const void *pb) struct maat_hit_group *la=(struct maat_hit_group *)pa; struct maat_hit_group *lb=(struct maat_hit_group *)pb; - long long ret = la->item_id - lb->item_id; + long long ret = la->group_id - lb->group_id; if (ret == 0) { - ret = la->group_id - lb->group_id; - if (ret == 0) { - ret = la->vtable_id - lb->vtable_id; - } + ret = la->vtable_id - lb->vtable_id; } return ret; } -static inline int compare_compile_id(const void *a, const void *b) -{ - long long ret = *(const long long *)a - *(const long long *)b; - - if (0 == ret) { - return 0; - } else if (ret < 0) { - return -1; - } else { - return 1; - } -} - static struct literal_clause * maat_compile_build_literal2clause_hash(struct compile_runtime *compile_rt) { @@ -996,29 +1075,6 @@ static size_t compile_state_if_new_hit_compile(struct maat_compile_state *compil return r_in_c_cnt; } -static void compile_state_update_hit_compile_table_id(struct maat_compile_state *compile_state, - long long compile_id, int table_id) -{ - if (!utarray_find(compile_state->hit_compile_tables, &compile_id, compare_compile_id)) { - struct maat_compile_table compile_table = {compile_id, table_id}; - utarray_push_back(compile_state->hit_compile_tables, &compile_table); - utarray_sort(compile_state->hit_compile_tables, compare_compile_id); - } -} - -static int compile_runtime_get_compile_table_id(struct compile_runtime *compile_rt, - long long compile_id) -{ - if (NULL == compile_rt || compile_id < 0) { - return -1; - } - - int *table_id = rcu_hash_find(compile_rt->tbl_cfg_hash, (char *)&compile_id, - sizeof(long long)); - - return *table_id; -} - static size_t maat_compile_bool_matcher_match(struct compile_runtime *compile_rt, int is_last_scan, int thread_id, struct maat_compile_state *compile_state, @@ -1071,11 +1127,6 @@ static size_t maat_compile_bool_matcher_match(struct compile_runtime *compile_rt hit a compile that refer a NOT-logic group in previous scan */ user_data_array[ud_result_cnt] = compile->user_data; ud_result_cnt++; - - int table_id = compile_runtime_get_compile_table_id(compile_rt, compile->compile_id); - if (table_id >= 0) { - compile_state_update_hit_compile_table_id(compile_state, compile->compile_id, table_id); - } } } } @@ -1091,11 +1142,18 @@ static struct compile_item *compile_item_clone(struct compile_item *item) new_item->compile_id = item->compile_id; new_item->declared_clause_num = item->declared_clause_num; + new_item->ref_schema = item->ref_schema; + new_item->ex_data = ALLOC(void *, 1); memcpy(new_item->table_name, item->table_name, sizeof(new_item->table_name)); new_item->table_line_len = item->table_line_len; new_item->table_line = ALLOC(char, new_item->table_line_len); memcpy(new_item->table_line, item->table_line, new_item->table_line_len); + if (1 == item->ref_schema->set_flag) { + *(new_item->ex_data) = rule_ex_data_new(item->table_name, item->ref_schema->table_id, + item->table_line, &(item->ref_schema->ex_schema)); + } + return new_item; } @@ -1231,7 +1289,7 @@ static int maat_remove_group_from_compile(struct rcu_hash_table *hash_tbl, if (NULL == compile) { log_error(logger, MODULE_COMPILE, "[%s:%d] Remove group_id:%lld from compile_id:%lld failed, compile" - " is not existed.", __FUNCTION__, __LINE__, g2c_item->group_id, + " is not exisited.", __FUNCTION__, __LINE__, g2c_item->group_id, compile_id); return -1; } else { @@ -1287,7 +1345,7 @@ static int maat_remove_group_from_compile(struct rcu_hash_table *hash_tbl, } else { log_error(logger, MODULE_COMPILE, "[%s:%d] Remove group_id:%lld from compile_id:%lld failed, " - "compile is not existed.", __FUNCTION__, __LINE__, + "compile is not exisited.", __FUNCTION__, __LINE__, g2c_item->group_id, compile_id); return -1; } @@ -1304,7 +1362,6 @@ struct maat_compile_state *maat_compile_state_new(void) utarray_new(compile_state->internal_inc_hit_paths, &ut_hit_path_icd); utarray_new(compile_state->all_hit_clauses, &ut_clause_id_icd); utarray_new(compile_state->this_scan_hit_clauses, &ut_clause_id_icd); - utarray_new(compile_state->hit_compile_tables, &ut_hit_compile_table_icd); return compile_state; } @@ -1319,14 +1376,11 @@ void maat_compile_state_reset(struct maat_compile_state *compile_state) compile_state->compile_rt_version = 0; compile_state->this_scan_hit_item_flag = 0; compile_state->not_clause_hit_flag = 0; - compile_state->inc_hit_path_flag = 0; utarray_clear(compile_state->internal_hit_paths); utarray_clear(compile_state->internal_inc_hit_paths); utarray_clear(compile_state->all_hit_clauses); utarray_clear(compile_state->this_scan_hit_clauses); - utarray_clear(compile_state->hit_compile_tables); - } void maat_compile_state_free(struct maat_compile_state *compile_state, @@ -1361,12 +1415,6 @@ void maat_compile_state_free(struct maat_compile_state *compile_state, compile_state->this_scan_hit_clauses = NULL; } - if (compile_state->hit_compile_tables != NULL) { - free_bytes += utarray_len(compile_state->hit_compile_tables) * sizeof(struct maat_compile_table); - utarray_free(compile_state->hit_compile_tables); - compile_state->hit_compile_tables = NULL; - } - FREE(compile_state); free_bytes += sizeof(struct maat_compile_state); @@ -1493,14 +1541,10 @@ static void maat_compile_state_update_hit_path(struct maat_compile_state *compil if (compile_state->Nth_scan != Nth_scan) { assert(compile_state->this_scan_hit_item_flag == 0); compile_state->Nth_scan = Nth_scan; + utarray_clear(compile_state->internal_inc_hit_paths); utarray_clear(compile_state->this_scan_hit_clauses); } - if (1 == compile_state->inc_hit_path_flag) { - compile_state->inc_hit_path_flag = 0; - utarray_clear(compile_state->internal_inc_hit_paths); - } - maat_compile_hit_path_add(compile_state->internal_inc_hit_paths, item_id, group_id, vtable_id, Nth_scan, Nth_item_result); @@ -1550,25 +1594,46 @@ static void maat_compile_state_update_hit_clause(struct maat_compile_state *comp } } -int maat_compile_state_get_compile_table_id(struct maat_compile_state *compile_state, - long long compile_id) -{ - struct maat_compile_table *compile_table = NULL; - - compile_table = utarray_find(compile_state->hit_compile_tables, &compile_id, - compare_compile_id); - if (NULL == compile_table) { - return -1; - } - - return compile_table->table_id; -} - int maat_compile_state_has_NOT_clause(struct maat_compile_state *compile_state) { return compile_state->not_clause_hit_flag; } +void compile_runtime_ex_data_iterate(struct compile_runtime *compile_rt, + struct compile_schema *compile_schema) +{ + if (NULL == compile_rt || NULL == compile_schema || + (0 == compile_schema->set_flag)) { + return; + } + + compile_runtime_user_data_iterate(compile_rt, rule_ex_data_new_cb, + &(compile_schema->ex_schema), + compile_schema->table_id); +} + +void *compile_runtime_get_ex_data(struct compile_runtime *compile_rt, + struct compile_schema *schema, + long long compile_id) +{ + if (NULL == compile_rt || NULL == schema || compile_id < 0 + || (0 == schema->set_flag)) { + return NULL; + } + + struct compile_item *item = NULL; + item = (struct compile_item *)compile_runtime_get_user_data(compile_rt, + compile_id); + if (NULL == item) { + return NULL; + } + + void *ex_data = NULL; + schema->ex_schema.dup_func(schema->table_id, &ex_data, item->ex_data, + schema->ex_schema.argl, schema->ex_schema.argp); + return ex_data; +} + static int compile_runtime_add_compile(struct compile_runtime *compile_rt, struct compile_schema *schema, long long compile_id, const char *table_name, @@ -1737,7 +1802,6 @@ int compile_runtime_update(void *compile_runtime, void *compile_schema, if (0 == is_valid) { // delete compile_runtime_del_compile(compile_rt, compile_id); - rcu_hash_del(compile_rt->tbl_cfg_hash, (char *)&compile_id, sizeof(long long)); } else { // add int ret = compile_runtime_add_compile(compile_rt, schema, compile_id, @@ -1745,14 +1809,6 @@ int compile_runtime_update(void *compile_runtime, void *compile_schema, if (ret < 0) { compile_rt->update_err_cnt++; } - - int *table_id = ALLOC(int, 1); - *table_id = table_manager_get_table_id(schema->ref_tbl_mgr, table_name); - ret = rcu_hash_add(compile_rt->tbl_cfg_hash, (char *)&compile_id, - sizeof(long long), table_id); - if (ret < 0) { - FREE(table_id); - } } return 0; @@ -1905,7 +1961,6 @@ int compile_runtime_commit(void *compile_runtime, const char *table_name, compile_rt->bm = new_bool_matcher; compile_rt->literal2clause_hash = new_literal2clause; rcu_hash_commit(compile_rt->cfg_hash); - rcu_hash_commit(compile_rt->tbl_cfg_hash); maat_garbage_bagging(compile_rt->ref_garbage_bin, old_bool_matcher, NULL, garbage_bool_matcher_free); @@ -2070,7 +2125,6 @@ size_t maat_compile_state_get_hit_groups(struct maat_compile_state *compile_stat tmp_hit_path = compile_state->internal_hit_paths; } else if (type == MAAT_LIST_TYPE_INC) { tmp_hit_path = compile_state->internal_inc_hit_paths; - compile_state->inc_hit_path_flag = 1; } for (i = 0; i < utarray_len(tmp_hit_path); i++) { @@ -2085,7 +2139,6 @@ size_t maat_compile_state_get_hit_groups(struct maat_compile_state *compile_stat for (size_t idx = 0; idx < super_group_cnt; idx++) { struct maat_hit_group hit_group; - hit_group.item_id = internal_path->item_id; hit_group.group_id = super_group_ids[idx]; hit_group.vtable_id = internal_path->vtable_id; if (utarray_find(all_hit_groups, &hit_group, compare_hit_group)) { @@ -2167,4 +2220,16 @@ size_t maat_compile_state_get_internal_hit_paths(struct maat_compile_state *comp } return hit_path_cnt; +} + +void compile_runtime_garbage_collect_routine(void *compile_runtime) +{ + if (NULL == compile_runtime) { + return; + } + + struct compile_runtime *compile_rt = (struct compile_runtime *)compile_runtime; + if (compile_rt->cfg_hash != NULL) { + rcu_hash_garbage_collect_routine(compile_rt->cfg_hash); + } } \ No newline at end of file diff --git a/src/maat_expr.c b/src/maat_expr.c index 8b7e084..3fb7e8a 100644 --- a/src/maat_expr.c +++ b/src/maat_expr.c @@ -60,7 +60,7 @@ struct expr_item { long long group_id; char keywords[MAX_KEYWORDS_STR]; enum expr_type expr_type; - enum expr_match_mode match_mode; + enum hs_match_mode match_mode; int is_hexbin; int is_case_sensitive; void *user_data; @@ -68,7 +68,7 @@ struct expr_item { }; struct expr_runtime { - struct expr_matcher *matcher; + struct adapter_hs *hs; struct rcu_hash_table *item_hash; // long long version; //expr_rt version @@ -79,7 +79,6 @@ struct expr_runtime { struct log_handle *logger; struct maat_garbage_bin *ref_garbage_bin; - enum maat_expr_engine expr_engine; int district_num; struct maat_kv_store *district_map; struct maat_kv_store *tmp_district_map; @@ -115,22 +114,22 @@ static enum expr_type int_to_expr_type(int expr_type) return type; } -static enum expr_match_mode int_to_match_mode(int match_method) +static enum hs_match_mode int_to_match_mode(int match_method) { - enum expr_match_mode mode = EXPR_MATCH_MODE_INVALID; + enum hs_match_mode mode = HS_MATCH_MODE_INVALID; switch (match_method) { case 0: - mode = EXPR_MATCH_MODE_SUB; + mode = HS_MATCH_MODE_SUB; break; case 1: - mode = EXPR_MATCH_MODE_SUFFIX; + mode = HS_MATCH_MODE_SUFFIX; break; case 2: - mode = EXPR_MATCH_MODE_PREFIX; + mode = HS_MATCH_MODE_PREFIX; break; case 3: - mode = EXPR_MATCH_MODE_EXACTLY; + mode = HS_MATCH_MODE_EXACTLY; break; default: break; @@ -144,13 +143,13 @@ static int expr_runtime_get_district_id(struct expr_runtime *expr_rt, { long long district_id = DISTRICT_ANY; - int map_ret = maat_kv_read(expr_rt->district_map, district, &district_id, 1); + int map_ret = maat_kv_read(expr_rt->district_map, district, &district_id); if (map_ret < 0) { if (NULL == expr_rt->tmp_district_map) { expr_rt->tmp_district_map = maat_kv_store_duplicate(expr_rt->district_map); } - map_ret = maat_kv_read(expr_rt->tmp_district_map, district, &district_id, 1); + map_ret = maat_kv_read(expr_rt->tmp_district_map, district, &district_id); if (map_ret < 0) { district_id = expr_rt->district_num; maat_kv_register(expr_rt->tmp_district_map, district, district_id); @@ -168,7 +167,7 @@ int expr_runtime_set_scan_district(struct expr_runtime *expr_rt, const char *dis return -1; } - return maat_kv_read_unNull(expr_rt->district_map, district, district_len, district_id, 1); + return maat_kv_read_unNull(expr_rt->district_map, district, district_len, district_id); } static struct expr_item * @@ -235,8 +234,8 @@ expr_item_new(struct expr_schema *expr_schema, const char *table_name, __FUNCTION__, __LINE__, table_name, line); goto error; } else if (expr_item->expr_type == EXPR_TYPE_REGEX) { - ret = expr_matcher_verify_regex_expression(expr_item->keywords, expr_rt->logger); - if (0 == ret) { + ret = adapter_hs_verify_regex_expression(expr_item->keywords, expr_rt->logger); + if (ret < 0) { log_error(expr_rt->logger, MODULE_EXPR, "[%s:%d] expr table:<%s> regex expression(item_id:%lld):%s illegal," " will be dropped", __FUNCTION__, __LINE__, table_name, @@ -278,7 +277,7 @@ expr_item_new(struct expr_schema *expr_schema, const char *table_name, match_method_type = atoi(line + column_offset); expr_item->match_mode = int_to_match_mode(match_method_type); - if (expr_item->match_mode == EXPR_MATCH_MODE_INVALID) { + if (expr_item->match_mode == HS_MATCH_MODE_INVALID) { log_error(expr_rt->logger, MODULE_EXPR, "[%s:%d] expr table:<%s> has invalid match_method in line:%s", __FUNCTION__, __LINE__, table_name, line); @@ -473,14 +472,12 @@ void *expr_runtime_new(void *expr_schema, size_t max_thread_num, return NULL; } - struct expr_schema *schema = (struct expr_schema *)expr_schema; struct expr_runtime *expr_rt = ALLOC(struct expr_runtime, 1); expr_rt->item_hash = rcu_hash_new(expr_item_free_cb, NULL, 0); expr_rt->n_worker_thread = max_thread_num; expr_rt->ref_garbage_bin = garbage_bin; expr_rt->logger = logger; - expr_rt->expr_engine = table_manager_get_expr_engine(schema->ref_tbl_mgr); expr_rt->district_map = maat_kv_store_new(); expr_rt->hit_cnt = alignment_int64_array_alloc(max_thread_num); @@ -498,9 +495,9 @@ void expr_runtime_free(void *expr_runtime) } struct expr_runtime *expr_rt = (struct expr_runtime *)expr_runtime; - if (expr_rt->matcher != NULL) { - expr_matcher_free(expr_rt->matcher); - expr_rt->matcher = NULL; + if (expr_rt->hs != NULL) { + adapter_hs_free(expr_rt->hs); + expr_rt->hs = NULL; } if (expr_rt->item_hash != NULL) { @@ -561,18 +558,18 @@ static int expr_runtime_update_row(struct expr_runtime *expr_rt, char *key, return 0; } -static enum expr_pattern_type expr_type2pattern_type(enum expr_type expr_type) +static enum hs_pattern_type expr_type2pattern_type(enum expr_type expr_type) { - enum expr_pattern_type pattern_type = EXPR_PATTERN_TYPE_STR; + enum hs_pattern_type pattern_type; switch (expr_type) { case EXPR_TYPE_STRING: case EXPR_TYPE_AND: case EXPR_TYPE_OFFSET: - pattern_type = EXPR_PATTERN_TYPE_STR; + pattern_type = HS_PATTERN_TYPE_STR; break; case EXPR_TYPE_REGEX: - pattern_type = EXPR_PATTERN_TYPE_REG; + pattern_type = HS_PATTERN_TYPE_REG; break; default: break; @@ -689,12 +686,12 @@ static int expr_item_to_expr_rule(struct expr_item *expr_item, } sub_expr_cnt = i; break; - case EXPR_TYPE_STRING: //AND/OFFSET/STRING type expression use \b to represent blank(' ') + case EXPR_TYPE_STRING: sub_expr_cnt = 1; sub_key_array[0] = expr_item->keywords; sub_key_array[0] = str_unescape(sub_key_array[0]); break; - case EXPR_TYPE_REGEX: //only regex type expression use \s to represent blank(' ') + case EXPR_TYPE_REGEX: sub_expr_cnt = 1; sub_key_array[0] = expr_item->keywords; break; @@ -713,15 +710,15 @@ static int expr_item_to_expr_rule(struct expr_item *expr_item, if (TRUE == expr_item->is_case_sensitive) { // insensitive - expr_rule->patterns[i].case_sensitive = EXPR_CASE_SENSITIVE; + expr_rule->patterns[i].case_sensitive = HS_CASE_SENSITIVE; } else { - expr_rule->patterns[i].case_sensitive = EXPR_CASE_INSENSITIVE; + expr_rule->patterns[i].case_sensitive = HS_CASE_INSENSITIVE; } - expr_rule->patterns[i].type = expr_type2pattern_type(expr_item->expr_type); + expr_rule->patterns[i].pattern_type = expr_type2pattern_type(expr_item->expr_type); if (TRUE == expr_item->is_hexbin && - expr_rule->patterns[i].type != EXPR_PATTERN_TYPE_REG) { + expr_rule->patterns[i].pattern_type != HS_PATTERN_TYPE_REG) { region_str_len = strlen(sub_key_array[i]) * 8 + 1; region_string = ALLOC(char, region_str_len); region_str_len = hex2bin(sub_key_array[i], strlen(sub_key_array[i]), @@ -741,13 +738,13 @@ static int expr_item_to_expr_rule(struct expr_item *expr_item, } expr_rule->patterns[i].match_mode = expr_item->match_mode; - if (expr_rule->patterns[i].match_mode == EXPR_MATCH_MODE_SUB) { + if (expr_rule->patterns[i].match_mode == HS_MATCH_MODE_SUB) { expr_rule->patterns[i].start_offset = key_left_offset[i]; expr_rule->patterns[i].end_offset = key_right_offset[i]; } } expr_rule->expr_id = expr_item->item_id; - expr_rule->tag = expr_item->user_data; + expr_rule->user_tag = expr_item->user_data; expr_rule->n_patterns = sub_expr_cnt; return 0; @@ -813,10 +810,10 @@ int expr_runtime_update(void *expr_runtime, void *expr_schema, return 0; } -static void garbage_expr_matcher_free(void *expr_matcher, void *arg) +static void garbage_adapter_hs_free(void *adapter_hs, void *arg) { - struct expr_matcher *matcher = (struct expr_matcher *)expr_matcher; - expr_matcher_free(matcher); + struct adapter_hs *hs = (struct adapter_hs *)adapter_hs; + adapter_hs_free(hs); } int expr_runtime_commit(void *expr_runtime, const char *table_name, @@ -867,42 +864,38 @@ int expr_runtime_commit(void *expr_runtime, const char *table_name, } } - struct expr_matcher *new_matcher = NULL; - struct expr_matcher *old_matcher = NULL; + struct adapter_hs *new_adapter_hs = NULL; + struct adapter_hs *old_adapter_hs = NULL; if (rule_cnt > 0) { - enum expr_engine_type engine_type = EXPR_ENGINE_TYPE_HS; - if (expr_rt->expr_engine == MAAT_EXPR_ENGINE_RS) { - engine_type = EXPR_ENGINE_TYPE_RS; - } - - new_matcher = expr_matcher_new(rules, real_rule_cnt, engine_type, - expr_rt->n_worker_thread, expr_rt->logger); - if (NULL == new_matcher) { + new_adapter_hs = adapter_hs_new(rules, real_rule_cnt, expr_rt->n_worker_thread, + expr_rt->logger); + if (NULL == new_adapter_hs) { log_error(expr_rt->logger, MODULE_EXPR, - "[%s:%d] table[%s] rebuild expr_matcher failed when update" + "[%s:%d] table[%s] rebuild adapter_hs engine failed when update" " %zu expr rules", __FUNCTION__, __LINE__, table_name, real_rule_cnt); ret = -1; - } else { - log_info(expr_rt->logger, MODULE_EXPR, - "table[%s] has %zu rules, commit %zu expr rules(regex rules:%zu) " - "and rebuild adapter_hs completed, version:%lld", table_name, rule_cnt, - real_rule_cnt, real_regex_rule_cnt, maat_rt_version); } } - old_matcher = expr_rt->matcher; - expr_rt->matcher = new_matcher; + old_adapter_hs = expr_rt->hs; + expr_rt->hs = new_adapter_hs; rcu_hash_commit(expr_rt->item_hash); - if (old_matcher != NULL) { - maat_garbage_bagging(expr_rt->ref_garbage_bin, old_matcher, NULL, garbage_expr_matcher_free); + if (old_adapter_hs != NULL) { + maat_garbage_bagging(expr_rt->ref_garbage_bin, old_adapter_hs, NULL, + garbage_adapter_hs_free); } expr_rt->rule_num = real_rule_cnt; expr_rt->regex_rule_num = real_regex_rule_cnt; expr_rt->version = maat_rt_version; + log_info(expr_rt->logger, MODULE_EXPR, + "table[%s] has %zu rules, commit %zu expr rules(regex rules:%zu) " + "and rebuild adapter_hs completed, version:%lld", table_name, rule_cnt, + real_rule_cnt, real_regex_rule_cnt, expr_rt->version); + if (rules != NULL) { for (i = 0; i < rule_cnt; i++) { expr_rule_reset(&rules[i]); @@ -956,14 +949,15 @@ int expr_runtime_scan(struct expr_runtime *expr_rt, int thread_id, return 0; } - if (NULL == expr_rt->matcher) { + if (NULL == expr_rt->hs) { return 0; } size_t n_hit_item = 0; - struct expr_scan_result hit_results[MAX_SCANNER_HIT_ITEM_NUM]; - int ret = expr_matcher_match(expr_rt->matcher, thread_id, data, data_len, - hit_results, MAX_SCANNER_HIT_ITEM_NUM, &n_hit_item); + struct hs_scan_result hit_results[MAX_SCANNER_HIT_ITEM_NUM]; + int ret = adapter_hs_scan(expr_rt->hs, thread_id, data, data_len, + hit_results, MAX_SCANNER_HIT_ITEM_NUM, + &n_hit_item); if (ret < 0) { return -1; } @@ -1006,15 +1000,14 @@ int expr_runtime_scan(struct expr_runtime *expr_rt, int thread_id, return real_hit_item_cnt; } -struct expr_matcher_stream * +struct adapter_hs_stream * expr_runtime_stream_open(struct expr_runtime *expr_rt, int thread_id) { if (NULL == expr_rt || thread_id < 0) { return NULL; } - struct expr_matcher_stream *stream = expr_matcher_stream_open(expr_rt->matcher, - thread_id); + struct adapter_hs_stream *stream = adapter_hs_stream_open(expr_rt->hs, thread_id); if (NULL == stream) { return NULL; } @@ -1023,7 +1016,7 @@ expr_runtime_stream_open(struct expr_runtime *expr_rt, int thread_id) } int expr_runtime_stream_scan(struct expr_runtime *expr_rt, - struct expr_matcher_stream *s_handle, + struct adapter_hs_stream *s_handle, const char *data, size_t data_len, int vtable_id, struct maat_state *state) { @@ -1033,10 +1026,10 @@ int expr_runtime_stream_scan(struct expr_runtime *expr_rt, } size_t n_hit_item = 0; - struct expr_scan_result hit_results[MAX_SCANNER_HIT_ITEM_NUM]; + struct hs_scan_result hit_results[MAX_SCANNER_HIT_ITEM_NUM]; - int ret = expr_matcher_stream_match(s_handle, data, data_len, hit_results, - MAX_SCANNER_HIT_ITEM_NUM, &n_hit_item); + int ret = adapter_hs_scan_stream(s_handle, data, data_len, hit_results, + MAX_SCANNER_HIT_ITEM_NUM, &n_hit_item); if (ret < 0) { return -1; } @@ -1074,13 +1067,13 @@ int expr_runtime_stream_scan(struct expr_runtime *expr_rt, } void expr_runtime_stream_close(struct expr_runtime *expr_rt, int thread_id, - struct expr_matcher_stream *stream) + struct adapter_hs_stream *s_handle) { - if (NULL == expr_rt || thread_id < 0 || NULL == stream) { + if (NULL == expr_rt || thread_id < 0 || NULL == s_handle) { return; } - expr_matcher_stream_close(stream); + adapter_hs_stream_close(s_handle); } void expr_runtime_hit_inc(struct expr_runtime *expr_rt, int thread_id) @@ -1173,4 +1166,4 @@ long long expr_runtime_scan_bytes(struct expr_runtime *expr_rt) alignment_int64_array_reset(expr_rt->scan_bytes, expr_rt->n_worker_thread); return sum; -} +} \ No newline at end of file diff --git a/src/maat_flag.c b/src/maat_flag.c index ecc38dc..da0747f 100644 --- a/src/maat_flag.c +++ b/src/maat_flag.c @@ -269,13 +269,13 @@ static int flag_runtime_get_district_id(struct flag_runtime *flag_rt, { long long district_id = DISTRICT_ANY; - int map_ret = maat_kv_read(flag_rt->district_map, district, &district_id, 1); + int map_ret = maat_kv_read(flag_rt->district_map, district, &district_id); if (map_ret < 0) { if (NULL == flag_rt->tmp_district_map) { flag_rt->tmp_district_map = maat_kv_store_duplicate(flag_rt->district_map); } - map_ret = maat_kv_read(flag_rt->tmp_district_map, district, &district_id, 1); + map_ret = maat_kv_read(flag_rt->tmp_district_map, district, &district_id); if (map_ret < 0) { district_id = flag_rt->district_num; maat_kv_register(flag_rt->tmp_district_map, district, district_id); @@ -293,7 +293,7 @@ int flag_runtime_set_scan_district(struct flag_runtime *flag_rt, const char *dis return -1; } - return maat_kv_read_unNull(flag_rt->district_map, district, district_len, district_id, 1); + return maat_kv_read_unNull(flag_rt->district_map, district, district_len, district_id); } static struct flag_item * diff --git a/src/maat_interval.c b/src/maat_interval.c index 4e22659..e5a11a8 100644 --- a/src/maat_interval.c +++ b/src/maat_interval.c @@ -242,13 +242,13 @@ static int interval_runtime_get_district_id(struct interval_runtime *interval_rt { long long district_id = DISTRICT_ANY; - int map_ret = maat_kv_read(interval_rt->district_map, district, &district_id, 1); + int map_ret = maat_kv_read(interval_rt->district_map, district, &district_id); if (map_ret < 0) { if (NULL == interval_rt->tmp_district_map) { interval_rt->tmp_district_map = maat_kv_store_duplicate(interval_rt->district_map); } - map_ret = maat_kv_read(interval_rt->tmp_district_map, district, &district_id, 1); + map_ret = maat_kv_read(interval_rt->tmp_district_map, district, &district_id); if (map_ret < 0) { district_id = interval_rt->district_num; maat_kv_register(interval_rt->tmp_district_map, district, district_id); @@ -268,7 +268,7 @@ int interval_runtime_set_scan_district(struct interval_runtime *interval_rt, } return maat_kv_read_unNull(interval_rt->district_map, district, - district_len, district_id, 1); + district_len, district_id); } static struct interval_item * diff --git a/src/maat_kv.c b/src/maat_kv.c index df9938d..2ce1a73 100644 --- a/src/maat_kv.c +++ b/src/maat_kv.c @@ -17,16 +17,11 @@ #define MAAT_KV_MAX_KEY_LEN 512 #define MAX_ENTITY_ID_CNT 4 -struct kv_entity { - long long id[MAX_ENTITY_ID_CNT]; - int id_cnt; //0~4 -}; - struct maat_kv_pair { char* key; //must be lower case. size_t key_len; - struct kv_entity *val; + long long val; UT_hash_handle hh; }; @@ -43,32 +38,15 @@ static void strlowercase(const char* src, size_t src_len, char* dst, size_t dst_ } static struct maat_kv_pair * -maat_kv_pair_new(const char* key, size_t keylen, long long value) +maat_kv_pair_new(const char* key, size_t key_len, long long value) { struct maat_kv_pair *kv = ALLOC(struct maat_kv_pair, 1); - kv->key = ALLOC(char, keylen); - kv->key_len = keylen; - kv->val = ALLOC(struct kv_entity, 1); - - strlowercase(key, keylen, kv->key, kv->key_len); - kv->val->id[0] = value; - kv->val->id_cnt = 1; - - return kv; -} - -static struct maat_kv_pair * -maat_kv_pair_clone(const char *key, size_t key_len, struct kv_entity *entity) -{ - struct maat_kv_pair *kv = ALLOC(struct maat_kv_pair, 1); - kv->key = ALLOC(char, key_len); kv->key_len = key_len; - kv->val = ALLOC(struct kv_entity, 1); + kv->val = value; strlowercase(key, key_len, kv->key, kv->key_len); - *(kv->val) = *entity; return kv; } @@ -83,10 +61,6 @@ static void maat_kv_pair_free(struct maat_kv_pair *kv) FREE(kv->key); } - if (kv->val != NULL) { - FREE(kv->val); - } - FREE(kv); } @@ -114,23 +88,23 @@ void maat_kv_store_free(struct maat_kv_store *store) } static int maat_kv_register_unNull(struct maat_kv_store *store, const char *key, - size_t keylen, long long value) + size_t key_len, long long value) { - if (keylen > MAAT_KV_MAX_KEY_LEN) { + if (key_len > MAAT_KV_MAX_KEY_LEN) { return -1; } struct maat_kv_pair *kv = NULL; struct maat_kv_pair *tmp_kv = NULL; - kv = maat_kv_pair_new(key, keylen, value); - HASH_FIND(hh, store->hash, kv->key, keylen, tmp_kv); + kv = maat_kv_pair_new(key, key_len, value); + HASH_FIND(hh, store->hash, kv->key, key_len, tmp_kv); if (tmp_kv) { maat_kv_pair_free(kv); return -1; } - HASH_ADD_KEYPTR(hh, store->hash, kv->key, keylen, kv); + HASH_ADD_KEYPTR(hh, store->hash, kv->key, key_len, kv); return 1; } @@ -142,34 +116,31 @@ int maat_kv_register(struct maat_kv_store* store, const char *key, long long val return ret; } -int maat_kv_read_unNull(struct maat_kv_store *store, const char *key, size_t keylen, - long long *value_array, size_t n_array) +int maat_kv_read_unNull(struct maat_kv_store *store, const char *key, size_t key_len, + long long *value) { struct maat_kv_pair *kv = NULL; char key_lowercase[MAAT_KV_MAX_KEY_LEN] = {0}; - if (keylen > MAAT_KV_MAX_KEY_LEN) { + if (key_len > MAAT_KV_MAX_KEY_LEN) { return -1; } - strlowercase(key, keylen, key_lowercase, sizeof(key_lowercase)); - HASH_FIND(hh, store->hash, key_lowercase, keylen, kv); + strlowercase(key, key_len, key_lowercase, sizeof(key_lowercase)); + HASH_FIND(hh, store->hash, key_lowercase, key_len, kv); int i = 0; if (kv) { - for (i = 0; i < kv->val->id_cnt && i < (int)n_array; i++) { - value_array[i] = kv->val->id[i]; - } - return i; + *value = kv->val; + return 1; } else { return -1; } } -int maat_kv_read(struct maat_kv_store *store, const char * key, - long long *value_array, size_t n_array) +int maat_kv_read(struct maat_kv_store *store, const char * key, long long *value) { - return maat_kv_read_unNull(store, key, strlen(key), value_array, n_array); + return maat_kv_read_unNull(store, key, strlen(key), value); } int maat_kv_write_unNull(struct maat_kv_store* store, const char* key, @@ -185,8 +156,7 @@ int maat_kv_write_unNull(struct maat_kv_store* store, const char* key, strlowercase(key, key_len, key_lowercase, sizeof(key_lowercase)); HASH_FIND(hh, store->hash, key_lowercase, key_len, kv); if (kv) { - kv->val->id[0] = value; - kv->val->id_cnt = 1; + kv->val = value; } else { kv = maat_kv_pair_new(key, key_len, value); HASH_ADD_KEYPTR(hh, store->hash, kv->key, key_len, kv); @@ -200,46 +170,13 @@ int maat_kv_write(struct maat_kv_store *store, const char *key, long long value) return maat_kv_write_unNull(store, key, strlen(key), value); } -int maat_kv_append_unNull(struct maat_kv_store* store, const char* key, - size_t key_len, long long value) -{ - struct maat_kv_pair *kv = NULL; - char key_lowercase[MAAT_KV_MAX_KEY_LEN] = {0}; - - if (key_len > MAAT_KV_MAX_KEY_LEN) { - return -1; - } - - strlowercase(key, key_len, key_lowercase, sizeof(key_lowercase)); - HASH_FIND(hh, store->hash, key_lowercase, key_len, kv); - if (kv) { - size_t id_cnt = kv->val->id_cnt; - if (id_cnt >= MAX_ENTITY_ID_CNT) { - return -1; - } - - kv->val->id[id_cnt] = value; - kv->val->id_cnt++; - } else { - kv = maat_kv_pair_new(key, key_len, value); - HASH_ADD_KEYPTR(hh, store->hash, kv->key, key_len, kv); - } - - return 1; -} - -int maat_kv_append(struct maat_kv_store *store, const char *key, long long value) -{ - return maat_kv_append_unNull(store, key, strlen(key), value); -} - struct maat_kv_store *maat_kv_store_duplicate(struct maat_kv_store *origin_map) { struct maat_kv_store *target = maat_kv_store_new(); struct maat_kv_pair *kv = NULL, *tmp_kv = NULL, *copy_kv = NULL; HASH_ITER (hh, origin_map->hash, kv, tmp_kv) { - copy_kv = maat_kv_pair_clone(kv->key, kv->key_len, kv->val); + copy_kv = maat_kv_pair_new(kv->key, kv->key_len, kv->val); HASH_ADD_KEYPTR(hh, target->hash, copy_kv->key, copy_kv->key_len, copy_kv); } diff --git a/src/maat_rule.c b/src/maat_rule.c index 238b614..6223952 100644 --- a/src/maat_rule.c +++ b/src/maat_rule.c @@ -127,7 +127,8 @@ static int maat_update_cb(const char *table_name, const char *line, void *u_para return 0; } - struct maat *maat_inst =(struct maat *)u_param; + struct maat *maat_inst =(struct maat *)u_param; + struct maat_runtime* maat_rt = NULL; int table_id = table_manager_get_table_id(maat_inst->tbl_mgr, table_name); if (table_id < 0) { log_error(maat_inst->logger, MODULE_MAAT_RULE, @@ -136,36 +137,23 @@ static int maat_update_cb(const char *table_name, const char *line, void *u_para return -1; } - int update_type = MAAT_UPDATE_TYPE_INC; - if (maat_inst->creating_maat_rt != NULL) { // Full update + void *schema = table_manager_get_schema(maat_inst->tbl_mgr, table_id); + if (NULL == schema) { + log_error(maat_inst->logger, MODULE_MAAT_RULE, + "[%s:%d] update warning, table name %s doesn't have table schema", + __FUNCTION__, __LINE__, table_name); + return -1; + } + + int update_type = MAAT_UPDATE_TYPE_INC; + if (maat_inst->creating_maat_rt != NULL) { //Full update + maat_rt = maat_inst->creating_maat_rt; update_type = MAAT_UPDATE_TYPE_FULL; - } - - // find conjunction id for table_id - long long conj_parent_table_ids[4]; - int conj_parent_table_cnt = table_manager_get_conj_parent_table_ids(maat_inst->tbl_mgr, table_name, - conj_parent_table_ids, 4); - if (conj_parent_table_cnt > 0) { - for (int i = 0; i < conj_parent_table_cnt; i++) { - int ret = table_manager_update_runtime(maat_inst->tbl_mgr, table_name, - (int)conj_parent_table_ids[i], line, update_type); - if (ret < 0) { - log_error(maat_inst->logger, MODULE_MAAT_RULE, - "[%s:%d] table<%s> update runtime error for rule:%s", - __FUNCTION__, __LINE__, table_name, line); - continue; - } - } - } else { - int ret = table_manager_update_runtime(maat_inst->tbl_mgr, table_name, - table_id, line, update_type); - if (ret < 0) { - log_error(maat_inst->logger, MODULE_MAAT_RULE, - "[%s:%d] table<%s> update runtime error for rules:%s", - __FUNCTION__, __LINE__, table_name, line); - return -1; - } - } + } else { + maat_rt = maat_inst->maat_rt; + } + + table_manager_update_runtime(maat_rt->ref_tbl_mgr, table_name, table_id, line, update_type); return 0; } @@ -213,6 +201,10 @@ static void maat_plugin_table_garbage_collect_routine(struct table_manager *tbl_ table_type = table_manager_get_table_type(tbl_mgr, i); switch (table_type) { + case TABLE_TYPE_COMPILE: + runtime = table_manager_get_runtime(tbl_mgr, i); + compile_runtime_garbage_collect_routine(runtime); + break; case TABLE_TYPE_PLUGIN: runtime = table_manager_get_runtime(tbl_mgr, i); ex_data_rt = plugin_runtime_get_ex_data_rt(runtime); @@ -353,7 +345,7 @@ long long maat_runtime_get_sequence(struct maat_runtime *maat_rt, const char *ke } long long sequence = 1; - int map_ret = maat_kv_read(maat_rt->sequence_map, key, &sequence, 1); + int map_ret = maat_kv_read(maat_rt->sequence_map, key, &sequence); if (map_ret < 0) { maat_kv_register(maat_rt->sequence_map, key, sequence); } else { diff --git a/src/maat_table.c b/src/maat_table.c index 92e4080..e0185ca 100644 --- a/src/maat_table.c +++ b/src/maat_table.c @@ -47,11 +47,9 @@ struct table_manager { struct rule_tag *accept_tags; size_t n_accept_tag; - enum maat_expr_engine expr_engine; int default_compile_table_id; int g2g_table_id; struct maat_kv_store *tablename2id_map; - struct maat_kv_store *conj_tablename2id_map; struct maat_garbage_bin *ref_garbage_bin; struct log_handle *logger; @@ -518,7 +516,7 @@ maat_table_new(cJSON *json, struct maat_kv_store *reserved_word_map, } ret = maat_kv_read(reserved_word_map, item->valuestring, - (long long *)&(ptable->table_type), 1); + (long long *)&(ptable->table_type)); if (ret < 0) { log_error(logger, MODULE_TABLE, "[%s:%d] table:%s table_type %s is illegal", @@ -580,159 +578,90 @@ static void maat_table_free(struct maat_table *maat_tbl) FREE(maat_tbl); } -int register_single_tablename2id(struct maat_kv_store *tablename2id_map, const char *table_name, - int table_id, struct log_handle *logger) +static int register_tablename2id(cJSON *json, struct maat_kv_store *tablename2id_map, + struct log_handle *logger) { - if (strlen(table_name) >= NAME_MAX) { - log_error(logger, MODULE_TABLE, - "[%s:%d] table:<%s> name length exceed maxium:%d", - __FUNCTION__, __LINE__, table_name, NAME_MAX); - return -1; - } - - long long tmp_table_id = -1; - int ret = maat_kv_read(tablename2id_map, table_name, &tmp_table_id, 1); - if (ret > 0 && tmp_table_id != table_id) { - log_error(logger, MODULE_TABLE, - "[%s:%d] table:<%s>(table_id:%lld) has already been registered" - ", can't register again", - __FUNCTION__, __LINE__, table_name, tmp_table_id); - return -1; - } - - maat_kv_register(tablename2id_map, table_name, table_id); - return 0; -} - -static int register_conjunction_tablename2id(cJSON *root, struct maat_kv_store *conj_tablename2id_map, - struct log_handle *logger) -{ - int json_array_size = cJSON_GetArraySize(root); - - for (int i = 0; i < json_array_size; i++) { - cJSON *json = cJSON_GetArrayItem(root, i); - cJSON *item = cJSON_GetObjectItem(json, "table_id"); - int table_id = item->valueint; - - item = cJSON_GetObjectItem(json, "db_tables"); - if (NULL == item || item->type != cJSON_Array) { - continue; - } - - int n_table_name = cJSON_GetArraySize(item); - for (int j = 0; j < n_table_name; j++) { - cJSON *tmp_item = cJSON_GetArrayItem(item, j); - if (NULL == tmp_item || tmp_item->type != cJSON_String) { - log_error(logger, MODULE_TABLE, - "[%s:%d] table(table_id:%d) db_tables element format invalid" - ", should be string", __FUNCTION__, __LINE__, table_id); - return -1; - } - - int ret = maat_kv_append(conj_tablename2id_map, tmp_item->valuestring, table_id); - if (ret < 0) { - return -1; - } - } - } - - return 0; -} - -static int register_tablename2id(cJSON *root, struct maat_kv_store *tablename2id_map, - const char *table_info_path, struct log_handle *logger) -{ - int i = 0; - int json_array_size = cJSON_GetArraySize(root); - - //pre register tablename2id - for (i = 0; i < json_array_size; i++) { - cJSON *json = cJSON_GetArrayItem(root, i); - if (NULL == json || json->type != cJSON_Object) { - log_error(logger, MODULE_TABLE, - "[%s:%d] %s has invalid json object", - __FUNCTION__, __LINE__, table_info_path); - return -1; - } - - cJSON *item = cJSON_GetObjectItem(json, "table_id"); - if (NULL == item || item->type != cJSON_Number) { - log_error(logger, MODULE_TABLE, - "[%s:%d] %s has invalid json object, happened in table_id column ", - __FUNCTION__, __LINE__, table_info_path); - return -1; - } - int table_id = item->valueint; - - item = cJSON_GetObjectItem(json, "table_name"); - if (NULL == item || item->type != cJSON_String) { - log_error(logger, MODULE_TABLE, - "[%s:%d] %s has invalid json object, happened in table_name column", - __FUNCTION__, __LINE__, table_info_path); - return -1; - } - - int ret = register_single_tablename2id(tablename2id_map, item->valuestring, - table_id, logger); - if (ret < 0) { - return -1; - } - } - - //register db_tables's table_name - for (i = 0; i < json_array_size; i++) { - cJSON *json = cJSON_GetArrayItem(root, i); - cJSON *item = cJSON_GetObjectItem(json, "table_id"); - int table_id = item->valueint; - - item = cJSON_GetObjectItem(json, "db_tables"); - if (NULL == item || item->type != cJSON_Array) { - continue; - } - - int n_table_name = cJSON_GetArraySize(item); - for (int j = 0; j < n_table_name; j++) { - cJSON *tmp_item = cJSON_GetArrayItem(item, j); - if (NULL == tmp_item || tmp_item->type != cJSON_String) { - log_error(logger, MODULE_TABLE, - "[%s:%d] table(table_id:%d) db_tables element format invalid" - ", should be string", __FUNCTION__, __LINE__, table_id); - return -1; - } - - long long tmp_table_id = -1; - int ret = maat_kv_read(tablename2id_map, tmp_item->valuestring, &tmp_table_id, 1); - if (ret > 0) { - continue; - } - - ret = register_single_tablename2id(tablename2id_map, tmp_item->valuestring, - table_id, logger); - if (ret < 0) { - return -1; - } - } - } - - return 0; -} - -int maat_default_compile_table_id(cJSON *json, struct log_handle *logger) -{ - cJSON *item = cJSON_GetObjectItem(json, "default_compile_table"); + cJSON *item = cJSON_GetObjectItem(json, "table_id"); if (NULL == item || item->type != cJSON_Number) { return -1; } + int table_id = item->valueint; - item = cJSON_GetObjectItem(json, "table_id"); - //item is cJSON_Number which has been checked in maat_table_new - return item->valueint; + item = cJSON_GetObjectItem(json, "db_tables"); + if (item != NULL && item->type != cJSON_Array) { + log_error(logger, MODULE_TABLE, + "[%s:%d] table(table_id:%d) has db_tables, but format is invalid" + ", should be array", __FUNCTION__, __LINE__, table_id); + return -1; + } + + int ret = 0; + long long tmp_table_id = 0; + if (item != NULL) { + int n_table_name = cJSON_GetArraySize(item); + + for (int i = 0; i < n_table_name; i++) { + cJSON *tmp_item = cJSON_GetArrayItem(item, i); + if (NULL == tmp_item || tmp_item->type != cJSON_String) { + log_error(logger, MODULE_TABLE, + "[%s:%d] table(table_id:%d) db_tables element format invalid" + ", should be string", __FUNCTION__, __LINE__, table_id); + return -1; + } + + if (strlen(tmp_item->valuestring) >= NAME_MAX) { + log_error(logger, MODULE_TABLE, + "[%s:%d] table(table_id:%d) db_tables string %s length " + "exceed maxium:%d", __FUNCTION__, __LINE__, table_id, + tmp_item->valuestring, NAME_MAX); + return -1; + } + + ret = maat_kv_read(tablename2id_map, tmp_item->valuestring, &tmp_table_id); + if (ret > 0 && tmp_table_id != table_id) { + log_error(logger, MODULE_TABLE, + "[%s:%d] table:<%s>(table_id:%lld) has already been registered" + ", can't register again", __FUNCTION__, __LINE__, + tmp_item->valuestring, tmp_table_id); + return -1; + } + + maat_kv_register(tablename2id_map, tmp_item->valuestring, table_id); + } + } + + item = cJSON_GetObjectItem(json, "table_name"); + if (NULL == item || item->type != cJSON_String) { + log_error(logger, MODULE_TABLE, + "[%s:%d] table(table_id:%d) has no table_name", + __FUNCTION__, __LINE__, table_id); + return -1; + } + + if (strlen(item->valuestring) >= NAME_MAX) { + log_error(logger, MODULE_TABLE, + "[%s:%d] table(table_id:%d) table_name %s length too long", + __FUNCTION__, __LINE__, table_id, item->valuestring); + return -1; + } + + ret = maat_kv_read(tablename2id_map, item->valuestring, &tmp_table_id); + if (ret > 0 && tmp_table_id != table_id) { + log_error(logger, MODULE_TABLE, + "[%s:%d] table:<%s>(table_id:%lld) has already been registered," + " can't register again", __FUNCTION__, __LINE__, item->valuestring, + tmp_table_id); + return -1; + } + + maat_kv_register(tablename2id_map, item->valuestring, table_id); + + return 0; } struct table_manager * table_manager_create(const char *table_info_path, const char *accept_tags, - enum maat_expr_engine expr_engine, struct maat_garbage_bin *garbage_bin, - struct log_handle *logger) + struct maat_garbage_bin *garbage_bin, struct log_handle *logger) { if (NULL == table_info_path) { return NULL; @@ -773,28 +702,23 @@ table_manager_create(const char *table_info_path, const char *accept_tags, tbl_mgr->n_accept_tag = parse_accept_tag(accept_tags, &tbl_mgr->accept_tags, logger); tbl_mgr->logger = logger; tbl_mgr->tablename2id_map = maat_kv_store_new(); - tbl_mgr->conj_tablename2id_map = maat_kv_store_new(); - tbl_mgr->expr_engine = expr_engine; tbl_mgr->ref_garbage_bin = garbage_bin; - ret = register_tablename2id(root, tbl_mgr->tablename2id_map, table_info_path, logger); - if (ret < 0) { - log_error(logger, MODULE_TABLE, - "[%s:%d] register_tablename2id failed.", __FUNCTION__, __LINE__); - FREE(json_buff); - cJSON_Delete(root); - table_manager_destroy(tbl_mgr); - return NULL; - } + for (int i = 0; i < json_array_size; i++) { + json = cJSON_GetArrayItem(root, i); - ret = register_conjunction_tablename2id(root, tbl_mgr->conj_tablename2id_map, logger); - if (ret < 0) { - log_error(logger, MODULE_TABLE, - "[%s:%d] register_conjunction_tablename2id failed.", __FUNCTION__, __LINE__); - FREE(json_buff); - cJSON_Delete(root); - table_manager_destroy(tbl_mgr); - return NULL; + if (json != NULL && json->type == cJSON_Object) { + ret = register_tablename2id(json, tbl_mgr->tablename2id_map, logger); + if (ret < 0) { + log_error(logger, MODULE_TABLE, + "[%s:%d] register_tablename2id failed", + __FUNCTION__, __LINE__); + FREE(json_buff); + cJSON_Delete(root); + table_manager_destroy(tbl_mgr); + return NULL; + } + } } int default_compile_table_id = -1; @@ -812,27 +736,22 @@ table_manager_create(const char *table_info_path, const char *accept_tags, goto next; } - long long parent_table_ids[4]; - int parent_table_cnt = table_manager_get_conj_parent_table_ids(tbl_mgr, maat_tbl->table_name, - parent_table_ids, 4); - if (parent_table_cnt <= 0) { - // if table has conjuncion parent, which can share schema and - // runtime with parent. - maat_tbl->schema = maat_table_schema_new(json, maat_tbl->table_name, maat_tbl->table_type, - tbl_mgr, logger); - if (NULL == maat_tbl->schema) { - log_error(logger, MODULE_TABLE, - "[%s:%d] Maat table schema new failed, table_name:%s", - __FUNCTION__, __LINE__, maat_tbl->table_name); - ret = -1; - goto next; - } + maat_tbl->schema = maat_table_schema_new(json, maat_tbl->table_name, + maat_tbl->table_type, tbl_mgr, logger); + if (NULL == maat_tbl->schema) { + log_error(logger, MODULE_TABLE, + "[%s:%d] Maat table schema new failed, table_name:%s", + __FUNCTION__, __LINE__, maat_tbl->table_name); + ret = -1; + goto next; } - + if (maat_tbl->table_type == TABLE_TYPE_COMPILE) { if (default_compile_table_id < 0) { - default_compile_table_id = maat_default_compile_table_id(json, logger); - } + default_compile_table_id = maat_tbl->table_id; + } else if (maat_tbl->table_id < default_compile_table_id) { + default_compile_table_id = maat_tbl->table_id; + } } if (maat_tbl->table_type == TABLE_TYPE_GROUP2GROUP) { @@ -997,11 +916,6 @@ void table_manager_destroy(struct table_manager *tbl_mgr) tbl_mgr->tablename2id_map = NULL; } - if (tbl_mgr->conj_tablename2id_map != NULL) { - maat_kv_store_free(tbl_mgr->conj_tablename2id_map); - tbl_mgr->conj_tablename2id_map = NULL; - } - FREE(tbl_mgr); } @@ -1019,14 +933,14 @@ size_t table_manager_table_count(struct table_manager *tbl_mgr) return tbl_mgr->n_table; } -int table_manager_get_table_id(struct table_manager *tbl_mgr, const char *table_name) +int table_manager_get_table_id(struct table_manager *tbl_mgr, const char *name) { - if (NULL == tbl_mgr || NULL == table_name) { + if (NULL == tbl_mgr || NULL == name) { return -1; } long long table_id = -1; - int ret = maat_kv_read(tbl_mgr->tablename2id_map, table_name, &table_id, 1); + int ret = maat_kv_read(tbl_mgr->tablename2id_map, name, &table_id); if (ret < 0) { return -1; } @@ -1034,17 +948,6 @@ int table_manager_get_table_id(struct table_manager *tbl_mgr, const char *table_ return (int)table_id; } -int table_manager_get_conj_parent_table_ids(struct table_manager *tbl_mgr, const char *table_name, - long long *table_ids_array, size_t n_table_ids_array) -{ - if (NULL == tbl_mgr || NULL == table_name) { - return -1; - } - - return maat_kv_read(tbl_mgr->conj_tablename2id_map, table_name, - table_ids_array, n_table_ids_array); -} - const char *table_manager_get_table_name(struct table_manager *tbl_mgr, int table_id) { if (NULL == tbl_mgr || table_id < 0) { @@ -1115,15 +1018,6 @@ int table_manager_get_valid_column(struct table_manager *tbl_mgr, int table_id) return tbl_mgr->tbl[table_id]->valid_column; } -enum maat_expr_engine table_manager_get_expr_engine(struct table_manager *tbl_mgr) -{ - if (NULL == tbl_mgr) { - return MAAT_EXPR_ENGINE_HS; - } - - return tbl_mgr->expr_engine; -} - size_t table_manager_accept_tags_count(struct table_manager *tbl_mgr) { if (NULL == tbl_mgr) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f3be40b..a3bc0e5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,9 +3,7 @@ include_directories(${PROJECT_SOURCE_DIR}/include) include_directories(${PROJECT_SOURCE_DIR}/src/inc_internal) include_directories(${PROJECT_SOURCE_DIR}/deps) include_directories(${PROJECT_SOURCE_DIR}/scanner) -include_directories(${PROJECT_SOURCE_DIR}/scanner/expr_matcher) -include_directories(${PROJECT_SOURCE_DIR}/scanner/expr_matcher/adapter_hs) -include_directories(${PROJECT_SOURCE_DIR}/scanner/expr_matcher/adapter_rs) +include_directories(${PROJECT_SOURCE_DIR}/scanner/adapter_hs) include_directories(${PROJECT_SOURCE_DIR}/scanner/ip_matcher) include_directories(${PROJECT_SOURCE_DIR}/scanner/bool_matcher) @@ -21,8 +19,8 @@ target_link_libraries(maat_framework_gtest maat_frame_static gtest_static) add_executable(maat_framework_perf_gtest maat_framework_perf_gtest.cpp) target_link_libraries(maat_framework_perf_gtest maat_frame_static gtest_static) -add_executable(expr_matcher_gtest expr_matcher_gtest.cpp) -target_link_libraries(expr_matcher_gtest maat_frame_static gtest_static) +add_executable(adapter_hs_gtest adapter_hs_gtest.cpp) +target_link_libraries(adapter_hs_gtest maat_frame_static gtest_static) add_executable(ip_matcher_gtest ip_matcher_gtest.cpp) target_link_libraries(ip_matcher_gtest maat_frame_static gtest_static) @@ -34,7 +32,6 @@ add_executable(maat_ex_data_gtest maat_ex_data_gtest.cpp) target_link_libraries(maat_ex_data_gtest maat_frame_static gtest_static) add_subdirectory(group_exclude) -add_subdirectory(benchmark) file(COPY table_info.conf DESTINATION ./) file(COPY tsg_table_info.conf DESTINATION ./) @@ -42,13 +39,13 @@ file(COPY file_test_tableinfo.conf DESTINATION ./) file(COPY literal_expr.conf DESTINATION ./) file(COPY regex_expr.conf DESTINATION ./) file(COPY maat_json.json DESTINATION ./) +file(COPY maat_json.json DESTINATION ../tools/) file(COPY ntcrule DESTINATION ./) file(COPY tsgrule DESTINATION ./) file(COPY testdata DESTINATION ./) file(COPY test_streamfiles DESTINATION ./) file(COPY json_update DESTINATION ./) file(COPY group_exclude DESTINATION ./) -file(COPY benchmark DESTINATION ./) include(GoogleTest) -gtest_discover_tests(maat_framework_gtest) +gtest_discover_tests(maat_framework_gtest) \ No newline at end of file diff --git a/test/adapter_hs_gtest.cpp b/test/adapter_hs_gtest.cpp new file mode 100644 index 0000000..b413916 --- /dev/null +++ b/test/adapter_hs_gtest.cpp @@ -0,0 +1,730 @@ +#include + +#include "log/log.h" +#include "adapter_hs.h" +#include "maat_utils.h" +#include "cJSON/cJSON.h" + +struct log_handle *g_logger = NULL; + +enum hs_match_mode match_method_to_match_mode(const char *method) +{ + enum hs_match_mode mode = HS_MATCH_MODE_INVALID; + + if (strcmp(method, "sub") == 0) { + mode = HS_MATCH_MODE_SUB; + } else if (strcmp(method, "exactly") == 0) { + mode = HS_MATCH_MODE_EXACTLY; + } else if (strcmp(method, "prefix") == 0) { + mode = HS_MATCH_MODE_PREFIX; + } else if (strcmp(method, "suffix") == 0) { + mode = HS_MATCH_MODE_SUFFIX; + } else { + assert(0); + } + + return mode; +} + +enum hs_case_sensitive case_sensitive_str_to_enum(const char *str) +{ + enum hs_case_sensitive case_sensitive = HS_CASE_SENSITIVE; + + if (strcmp(str, "yes") == 0) { + case_sensitive = HS_CASE_SENSITIVE; + } else if (strcmp(str, "no") == 0) { + case_sensitive = HS_CASE_INSENSITIVE; + } else { + assert(0); + } + + return case_sensitive; +} + +int is_hexbin_str_to_int(const char *str) +{ + int ret = 0; + + if (strcmp(str, "yes") == 0) { + ret = 1; + } + + return ret; +} + +static int convertHextoint(char srctmp) +{ + if (isdigit(srctmp)) { + return srctmp - '0'; + } else { + char temp = toupper(srctmp); + temp = temp - 'A' + 10; + return temp; + } +} + +static size_t hex2bin(char *hex, int hex_len, char *binary, size_t size) +{ + size_t resultlen = 0; + int high,low; + for (int i = 0; i < hex_len && size > resultlen; i += 2, resultlen++) { + high = convertHextoint(hex[i]); + low = convertHextoint(hex[i+1]); + binary[resultlen] = high * 16 + low; + } + + size = resultlen; + binary[resultlen] = '\0'; + + return resultlen; +} + +enum hs_pattern_type pattern_type_str_to_enum(const char *str) +{ + enum hs_pattern_type pattern_type; + + if (strcmp(str, "regex") == 0) { + pattern_type = HS_PATTERN_TYPE_REG; + } else if (strcmp(str, "literal") == 0) { + pattern_type = HS_PATTERN_TYPE_STR; + } else { + assert(0); + } + + return pattern_type; +} + +int parse_config_file(const char *filename, struct expr_rule exprs[], size_t *n_expr) +{ + unsigned char *json_buff = NULL; + size_t json_buff_size = 0; + + int ret = load_file_to_memory(filename, &json_buff, &json_buff_size); + if (ret < 0) { + printf("load file:%s to memory failed.\n", filename); + return -1; + } + + size_t rule_cnt = 0; + cJSON *rules_obj = NULL; + cJSON *root = cJSON_Parse((const char *)json_buff); + if (NULL == root) { + printf("Error before: %-200.200s\n", cJSON_GetErrorPtr()); + ret = -1; + goto next; + } + + rules_obj = cJSON_GetObjectItem(root, "expr_rules"); + if (NULL == rules_obj) { + printf("Error before: %-200.200s\n", cJSON_GetErrorPtr()); + ret = -1; + goto next; + } + + rule_cnt = cJSON_GetArraySize(rules_obj); + for (size_t i = 0; i < rule_cnt; i++) { + cJSON *expr_obj = cJSON_GetArrayItem(rules_obj, i); + cJSON *tmp_item = cJSON_GetObjectItem(expr_obj, "expr_id"); + if (tmp_item != NULL && tmp_item->type == cJSON_Number) { + exprs[i].expr_id = tmp_item->valueint; + } + + tmp_item = cJSON_GetObjectItem(expr_obj, "pattern_num"); + if (tmp_item != NULL && tmp_item->type == cJSON_Number) { + exprs[i].n_patterns = tmp_item->valueint; + } + + tmp_item = cJSON_GetObjectItem(expr_obj, "patterns"); + if (NULL == tmp_item || tmp_item->type != cJSON_Array) { + printf("json has no patterns array.\n"); + ret = -1; + goto next; + } + + size_t pattern_cnt = cJSON_GetArraySize(tmp_item); + for (size_t j = 0; j < pattern_cnt; j++) { + cJSON *pat_item = cJSON_GetArrayItem(tmp_item, j); + + cJSON *item = cJSON_GetObjectItem(pat_item, "pattern_type"); + if (item != NULL && item->type == cJSON_String) { + exprs[i].patterns[j].pattern_type = pattern_type_str_to_enum(item->valuestring); + } + + item = cJSON_GetObjectItem(pat_item, "match_method"); + if (item != NULL && item->type == cJSON_String) { + exprs[i].patterns[j].match_mode = match_method_to_match_mode(item->valuestring); + } + + item = cJSON_GetObjectItem(pat_item, "case_sensitive"); + if (item != NULL && item->type == cJSON_String) { + exprs[i].patterns[j].case_sensitive = case_sensitive_str_to_enum(item->valuestring); + } + + item = cJSON_GetObjectItem(pat_item, "is_hexbin"); + if (item != NULL && item->type == cJSON_String) { + exprs[i].patterns[j].is_hexbin = is_hexbin_str_to_int(item->valuestring); + } + + item = cJSON_GetObjectItem(pat_item, "pattern"); + if (item != NULL && item->type == cJSON_String) { + exprs[i].patterns[j].pat = ALLOC(char, strlen(item->valuestring) + 1); + + if (exprs[i].patterns[j].is_hexbin == 1) { + size_t pat_str_len = strlen(item->valuestring) + 1; + char *pat_str = ALLOC(char, pat_str_len); + pat_str_len = hex2bin(item->valuestring, strlen(item->valuestring), + pat_str, pat_str_len); + + memcpy(exprs[i].patterns[j].pat, pat_str, pat_str_len); + free(pat_str); + exprs[i].patterns[j].pat_len = pat_str_len; + } else { + memcpy(exprs[i].patterns[j].pat, item->valuestring, + strlen(item->valuestring)); + exprs[i].patterns[j].pat_len = strlen(item->valuestring); + } + } + + if (exprs[i].patterns->match_mode == HS_MATCH_MODE_SUB) { + item = cJSON_GetObjectItem(pat_item, "offset"); + if (item != NULL && item->type == cJSON_String) { + int key_left_offset = -1; + int key_right_offset = -1; + sscanf(item->valuestring, "%d~%d", &key_left_offset, &key_right_offset); + if (key_left_offset < -1 || key_right_offset < -1) { + printf("Error: offset should not less than -1, left_offset:%d, right_offset:%d\n", + key_left_offset, key_right_offset); + } + exprs[i].patterns[j].start_offset = key_left_offset; + exprs[i].patterns[j].end_offset = key_right_offset; + } else { + exprs[i].patterns[j].start_offset = -1; + exprs[i].patterns[j].end_offset = -1; + } + } + + if (exprs[i].patterns->match_mode == HS_MATCH_MODE_EXACTLY) { + exprs[i].patterns[j].start_offset = 0; + exprs[i].patterns[j].end_offset = exprs[i].patterns[j].pat_len - 1; + } + } + exprs[i].n_patterns = pattern_cnt; + } + + *n_expr = rule_cnt; +next: + cJSON_Delete(root); + FREE(json_buff); + return ret; +} + +void expr_array_free(struct expr_rule rules[], size_t n_rule) +{ + for (size_t i = 0; i < n_rule; i++) { + for (size_t j = 0; j < rules[i].n_patterns; j++) { + if (rules[i].patterns[j].pat != NULL) { + free(rules[i].patterns[j].pat); + rules[i].patterns[j].pat = NULL; + } + } + } +} + +TEST(adapter_hs_init, invalid_input_parameter) +{ + struct expr_rule rules[64]; + size_t n_rule = 0; + + struct adapter_hs *hs_instance = adapter_hs_new(NULL, 0, 1, g_logger); + EXPECT_TRUE(hs_instance == NULL); + + hs_instance = adapter_hs_new(rules, n_rule, 1, g_logger); + EXPECT_TRUE(hs_instance == NULL); + + n_rule = 1; + rules[0].expr_id = 101; + rules[0].n_patterns = 10; + hs_instance = adapter_hs_new(rules, n_rule, 1, g_logger); + EXPECT_TRUE(hs_instance == NULL); + + memset(rules, 0, sizeof(rules)); + n_rule = 1; + rules[0].expr_id = 101; + rules[0].n_patterns = 1; + hs_instance = adapter_hs_new(rules, n_rule, 1, g_logger); + EXPECT_TRUE(hs_instance == NULL); +} + +TEST(adapter_hs_scan, literal_sub_has_normal_offset) +{ + struct expr_rule rules[64] = {0}; + size_t n_rule = 0; + + int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); + EXPECT_EQ(ret, 0); + + struct adapter_hs *hs_instance = adapter_hs_new(rules, n_rule, 1, g_logger); + EXPECT_TRUE(hs_instance != NULL); + expr_array_free(rules, n_rule); + + char scan_data1[64] = "hello aaa"; + struct hs_scan_result result[64] = {0}; + size_t n_result = 0; + + ret = adapter_hs_scan(hs_instance, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 0); + + char scan_data2[64] = "Ahello aaa"; + memset(result, 0, sizeof(result)); + n_result = 0; + ret = adapter_hs_scan(hs_instance, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 101); + + char scan_data3[64] = "Aahello aaa"; + memset(result, 0, sizeof(result)); + n_result = 0; + ret = adapter_hs_scan(hs_instance, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 101); + + char scan_data4[64] = "Aaahello aaa"; + memset(result, 0, sizeof(result)); + n_result = 0; + ret = adapter_hs_scan(hs_instance, 0, scan_data4, strlen(scan_data4), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 0); + + adapter_hs_free(hs_instance); + hs_instance = NULL; +} + +TEST(adapter_hs_scan, literal_sub_has_left_unlimit_offset) +{ + struct expr_rule rules[64] = {0}; + size_t n_rule = 0; + + int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); + EXPECT_EQ(ret, 0); + + struct adapter_hs *hs_instance = adapter_hs_new(rules, n_rule, 1, g_logger); + EXPECT_TRUE(hs_instance != NULL); + expr_array_free(rules, n_rule); + + char scan_data1[64] = "hello bbb"; + struct hs_scan_result result[64] = {0}; + size_t n_result = 0; + + ret = adapter_hs_scan(hs_instance, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 102); + + char scan_data2[64] = "Ahello bbb"; + memset(result, 0, sizeof(result)); + n_result = 0; + ret = adapter_hs_scan(hs_instance, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 102); + + char scan_data3[64] = "Aahello bbb"; + memset(result, 0, sizeof(result)); + n_result = 0; + ret = adapter_hs_scan(hs_instance, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 102); + + char scan_data4[64] = "Aaahello bbb"; + memset(result, 0, sizeof(result)); + n_result = 0; + ret = adapter_hs_scan(hs_instance, 0, scan_data4, strlen(scan_data4), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 0); + + adapter_hs_free(hs_instance); + hs_instance = NULL; +} + +TEST(adapter_hs_scan, literal_sub_has_right_unlimit_offset) +{ + struct expr_rule rules[64] = {0}; + size_t n_rule = 0; + + int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); + EXPECT_EQ(ret, 0); + + struct adapter_hs *hs_instance = adapter_hs_new(rules, n_rule, 1, g_logger); + EXPECT_TRUE(hs_instance != NULL); + expr_array_free(rules, n_rule); + + char scan_data1[64] = "hello ccc"; + struct hs_scan_result result[64] = {0}; + size_t n_result = 0; + + ret = adapter_hs_scan(hs_instance, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 0); + + char scan_data2[64] = "1234hello ccc"; + memset(result, 0, sizeof(result)); + n_result = 0; + ret = adapter_hs_scan(hs_instance, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 0); + + char scan_data3[64] = "12345hello ccc"; + memset(result, 0, sizeof(result)); + n_result = 0; + ret = adapter_hs_scan(hs_instance, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 103); + + char scan_data4[64] = "12345hello cccAaBb"; + memset(result, 0, sizeof(result)); + n_result = 0; + ret = adapter_hs_scan(hs_instance, 0, scan_data4, strlen(scan_data4), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 103); + + char scan_data5[64] = "123456hello cccAaBb"; + memset(result, 0, sizeof(result)); + n_result = 0; + ret = adapter_hs_scan(hs_instance, 0, scan_data5, strlen(scan_data5), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 103); + + adapter_hs_free(hs_instance); + hs_instance = NULL; +} + +TEST(adapter_hs_scan, literal_sub_with_no_offset) +{ + struct expr_rule rules[64] = {0}; + size_t n_rule = 0; + + int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); + EXPECT_EQ(ret, 0); + + struct adapter_hs *hs_instance = adapter_hs_new(rules, n_rule, 1, g_logger); + EXPECT_TRUE(hs_instance != NULL); + expr_array_free(rules, n_rule); + + char scan_data1[64] = "hello ddd"; + struct hs_scan_result result[64] = {0}; + size_t n_result = 0; + ret = adapter_hs_scan(hs_instance, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 104); + + char scan_data2[64] = "123hello ddd"; + memset(result, 0, sizeof(result)); + n_result = 0; + ret = adapter_hs_scan(hs_instance, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 104); + + char scan_data3[64] = "123hello ddd456"; + memset(result, 0, sizeof(result)); + n_result = 0; + ret = adapter_hs_scan(hs_instance, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 104); + + char scan_data4[64] = "helloddd"; + memset(result, 0, sizeof(result)); + n_result = 0; + ret = adapter_hs_scan(hs_instance, 0, scan_data4, strlen(scan_data4), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 0); + + adapter_hs_free(hs_instance); + hs_instance = NULL; +} + +TEST(adapter_hs_scan, literal_exactly) +{ + struct expr_rule rules[64] = {0}; + size_t n_rule = 0; + + int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); + EXPECT_EQ(ret, 0); + + struct adapter_hs *hs_instance = adapter_hs_new(rules, n_rule, 1, g_logger); + EXPECT_TRUE(hs_instance != NULL); + expr_array_free(rules, n_rule); + + char scan_data1[64] = "hello eee"; + struct hs_scan_result result[64] = {0}; + size_t n_result = 0; + + ret = adapter_hs_scan(hs_instance, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 105); + + char scan_data2[64] = "Ahello eee"; + memset(result, 0, sizeof(result)); + n_result = 0; + + ret = adapter_hs_scan(hs_instance, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 0); + + char scan_data3[64] = "hello eeeB"; + memset(result, 0, sizeof(result)); + n_result = 0; + + ret = adapter_hs_scan(hs_instance, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 0); + + adapter_hs_free(hs_instance); + hs_instance = NULL; +} + +TEST(adapter_hs_scan, literal_prefix) +{ + struct expr_rule rules[64] = {0}; + size_t n_rule = 0; + + int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); + EXPECT_EQ(ret, 0); + + struct adapter_hs *hs_instance = adapter_hs_new(rules, n_rule, 1, g_logger); + EXPECT_TRUE(hs_instance != NULL); + expr_array_free(rules, n_rule); + + char scan_data1[64] = "hello fff"; + struct hs_scan_result result[64] = {0}; + size_t n_result = 0; + + ret = adapter_hs_scan(hs_instance, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 106); + + char scan_data2[64] = "Ahello fff"; + memset(result, 0, sizeof(result)); + n_result = 0; + + ret = adapter_hs_scan(hs_instance, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 0); + + char scan_data3[64] = "Ahello fffBCD"; + memset(result, 0, sizeof(result)); + n_result = 0; + + ret = adapter_hs_scan(hs_instance, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 0); + + char scan_data4[64] = "hello fffBCD"; + memset(result, 0, sizeof(result)); + n_result = 0; + + ret = adapter_hs_scan(hs_instance, 0, scan_data4, strlen(scan_data4), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 106); + + adapter_hs_free(hs_instance); + hs_instance = NULL; +} + +TEST(adapter_hs_scan, literal_suffix) +{ + struct expr_rule rules[64] = {0}; + size_t n_rule = 0; + + int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); + EXPECT_EQ(ret, 0); + + struct adapter_hs *hs_instance = adapter_hs_new(rules, n_rule, 1, g_logger); + EXPECT_TRUE(hs_instance != NULL); + expr_array_free(rules, n_rule); + + char scan_data1[64] = "hello ggg"; + struct hs_scan_result result[64] = {0}; + size_t n_result = 0; + + ret = adapter_hs_scan(hs_instance, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 107); + + char scan_data2[64] = "ABChello ggg"; + memset(result, 0, sizeof(result)); + n_result = 0; + + ret = adapter_hs_scan(hs_instance, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 107); + + char scan_data3[64] = "ABChello gggDEF"; + memset(result, 0, sizeof(result)); + n_result = 0; + + ret = adapter_hs_scan(hs_instance, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 0); + + char scan_data4[64] = "hello gggDEF"; + memset(result, 0, sizeof(result)); + n_result = 0; + + ret = adapter_hs_scan(hs_instance, 0, scan_data4, strlen(scan_data4), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 0); + + adapter_hs_free(hs_instance); + hs_instance = NULL; +} + +TEST(adapter_hs_scan, literal_sub_with_hexbin) +{ + struct expr_rule rules[64] = {0}; + size_t n_rule = 0; + + int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); + EXPECT_EQ(ret, 0); + + struct adapter_hs *hs_instance = adapter_hs_new(rules, n_rule, 1, g_logger); + EXPECT_TRUE(hs_instance != NULL); + expr_array_free(rules, n_rule); + + char scan_data1[64] = "Content-Type: /html"; + struct hs_scan_result result[64] = {0}; + size_t n_result = 0; + ret = adapter_hs_scan(hs_instance, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 108); + + char scan_data2[64] = " html"; + memset(result, 0, sizeof(result)); + n_result = 0; + + ret = adapter_hs_scan(hs_instance, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 0); + + adapter_hs_free(hs_instance); + hs_instance = NULL; +} + +TEST(adapter_hs_scan, literal_with_chinese) +{ + struct expr_rule rules[64] = {0}; + size_t n_rule = 0; + + int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); + EXPECT_EQ(ret, 0); + + struct adapter_hs *hs_instance = adapter_hs_new(rules, n_rule, 1, g_logger); + EXPECT_TRUE(hs_instance != NULL); + expr_array_free(rules, n_rule); + + char data0[64] = "#中国 你好"; + struct hs_scan_result result0[64] = {0}; + size_t n_result0 = 0; + ret = adapter_hs_scan(hs_instance, 0, data0, strlen(data0), result0, 64, &n_result0); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result0, 1); + EXPECT_EQ(result0[0].rule_id, 110); + + adapter_hs_free(hs_instance); + hs_instance = NULL; +} + +TEST(adapter_hs_scan, same_pattern_different_offset) +{ + struct expr_rule rules[64] = {0}; + size_t n_rule = 0; + + int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); + EXPECT_EQ(ret, 0); + + struct adapter_hs *hs_instance = adapter_hs_new(rules, n_rule, 1, g_logger); + EXPECT_TRUE(hs_instance != NULL); + expr_array_free(rules, n_rule); + + char data[64] = "onetoday,anothertoday"; + struct hs_scan_result result[64] = {0}; + size_t n_result = 0; + ret = adapter_hs_scan(hs_instance, 0, data, strlen(data), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 112); + + adapter_hs_free(hs_instance); + hs_instance = NULL; +} + +TEST(adapter_hs_scan, long_scan_data) +{ + struct expr_rule rules[64] = {0}; + size_t n_rule = 0; + + int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); + EXPECT_EQ(ret, 0); + + struct adapter_hs *hs_instance = adapter_hs_new(rules, n_rule, 1, g_logger); + EXPECT_TRUE(hs_instance != NULL); + expr_array_free(rules, n_rule); + + const char* scan_data = "A directed path in a directed graph is a finite or infinite\ +sequence of edges which joins a sequence of distinct vertices, but with the added restriction\ +that the edges be all directed in the same direction."; + struct hs_scan_result result[64] = {0}; + size_t n_result = 0; + ret = adapter_hs_scan(hs_instance, 0, scan_data, strlen(scan_data), result, 64, &n_result); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result, 1); + EXPECT_EQ(result[0].rule_id, 113); + + adapter_hs_free(hs_instance); + hs_instance = NULL; +} + +TEST(adapter_hs_scan, regex_expression_check) +{ + struct expr_rule rules[64] = {0}; + size_t n_rule = 0; + + int ret = parse_config_file("./regex_expr.conf", rules, &n_rule); + EXPECT_EQ(ret, 0); + + for (size_t i = 0; i < n_rule; i++) { + for (size_t j = 0; j < rules[i].n_patterns; j++) { + adapter_hs_verify_regex_expression(rules[i].patterns[j].pat, g_logger); + } + } + + expr_array_free(rules, n_rule); +} + +int main(int argc, char **argv) +{ + int ret = 0; + ::testing::InitGoogleTest(&argc, argv); + g_logger = log_handle_create("./adapter_hs_gtest.log", 0); + + ret = RUN_ALL_TESTS(); + + log_handle_destroy(g_logger); + + return ret; +} \ No newline at end of file diff --git a/test/expr_matcher_gtest.cpp b/test/expr_matcher_gtest.cpp deleted file mode 100644 index 7143e27..0000000 --- a/test/expr_matcher_gtest.cpp +++ /dev/null @@ -1,1330 +0,0 @@ -#include - -#include "log/log.h" -#include "adapter_hs.h" -#include "maat_utils.h" -#include "cJSON/cJSON.h" - -struct log_handle *g_logger = NULL; - -enum expr_match_mode match_method_to_match_mode(const char *method) -{ - enum expr_match_mode mode = EXPR_MATCH_MODE_INVALID; - - if (strcmp(method, "sub") == 0) { - mode = EXPR_MATCH_MODE_SUB; - } else if (strcmp(method, "exactly") == 0) { - mode = EXPR_MATCH_MODE_EXACTLY; - } else if (strcmp(method, "prefix") == 0) { - mode = EXPR_MATCH_MODE_PREFIX; - } else if (strcmp(method, "suffix") == 0) { - mode = EXPR_MATCH_MODE_SUFFIX; - } else { - assert(0); - } - - return mode; -} - -enum expr_case_sensitive case_sensitive_str_to_enum(const char *str) -{ - enum expr_case_sensitive case_sensitive = EXPR_CASE_SENSITIVE; - - if (strcmp(str, "yes") == 0) { - case_sensitive = EXPR_CASE_SENSITIVE; - } else if (strcmp(str, "no") == 0) { - case_sensitive = EXPR_CASE_INSENSITIVE; - } else { - assert(0); - } - - return case_sensitive; -} - -int is_hexbin_str_to_int(const char *str) -{ - int ret = 0; - - if (strcmp(str, "yes") == 0) { - ret = 1; - } - - return ret; -} - -static int convertHextoint(char srctmp) -{ - if (isdigit(srctmp)) { - return srctmp - '0'; - } else { - char temp = toupper(srctmp); - temp = temp - 'A' + 10; - return temp; - } -} - -static size_t hex2bin(char *hex, int hex_len, char *binary, size_t size) -{ - size_t resultlen = 0; - int high,low; - for (int i = 0; i < hex_len && size > resultlen; i += 2, resultlen++) { - high = convertHextoint(hex[i]); - low = convertHextoint(hex[i+1]); - binary[resultlen] = high * 16 + low; - } - - size = resultlen; - binary[resultlen] = '\0'; - - return resultlen; -} - -enum expr_pattern_type pattern_type_str_to_enum(const char *str) -{ - enum expr_pattern_type pattern_type = EXPR_PATTERN_TYPE_STR; - - if (strcmp(str, "regex") == 0) { - pattern_type = EXPR_PATTERN_TYPE_REG; - } else if (strcmp(str, "literal") == 0) { - pattern_type = EXPR_PATTERN_TYPE_STR; - } else { - assert(0); - } - - return pattern_type; -} - -int parse_config_file(const char *filename, struct expr_rule exprs[], size_t *n_expr) -{ - unsigned char *json_buff = NULL; - size_t json_buff_size = 0; - - int ret = load_file_to_memory(filename, &json_buff, &json_buff_size); - if (ret < 0) { - printf("load file:%s to memory failed.\n", filename); - return -1; - } - - size_t rule_cnt = 0; - cJSON *rules_obj = NULL; - cJSON *root = cJSON_Parse((const char *)json_buff); - if (NULL == root) { - printf("Error before: %-200.200s\n", cJSON_GetErrorPtr()); - ret = -1; - goto next; - } - - rules_obj = cJSON_GetObjectItem(root, "expr_rules"); - if (NULL == rules_obj) { - printf("Error before: %-200.200s\n", cJSON_GetErrorPtr()); - ret = -1; - goto next; - } - - rule_cnt = cJSON_GetArraySize(rules_obj); - for (size_t i = 0; i < rule_cnt; i++) { - cJSON *expr_obj = cJSON_GetArrayItem(rules_obj, i); - cJSON *tmp_item = cJSON_GetObjectItem(expr_obj, "expr_id"); - if (tmp_item != NULL && tmp_item->type == cJSON_Number) { - exprs[i].expr_id = tmp_item->valueint; - } - - tmp_item = cJSON_GetObjectItem(expr_obj, "pattern_num"); - if (tmp_item != NULL && tmp_item->type == cJSON_Number) { - exprs[i].n_patterns = tmp_item->valueint; - } - - tmp_item = cJSON_GetObjectItem(expr_obj, "patterns"); - if (NULL == tmp_item || tmp_item->type != cJSON_Array) { - printf("json has no patterns array.\n"); - ret = -1; - goto next; - } - - size_t pattern_cnt = cJSON_GetArraySize(tmp_item); - for (size_t j = 0; j < pattern_cnt; j++) { - cJSON *pat_item = cJSON_GetArrayItem(tmp_item, j); - - cJSON *item = cJSON_GetObjectItem(pat_item, "pattern_type"); - if (item != NULL && item->type == cJSON_String) { - exprs[i].patterns[j].type = pattern_type_str_to_enum(item->valuestring); - } - - item = cJSON_GetObjectItem(pat_item, "match_method"); - if (item != NULL && item->type == cJSON_String) { - exprs[i].patterns[j].match_mode = match_method_to_match_mode(item->valuestring); - } - - item = cJSON_GetObjectItem(pat_item, "case_sensitive"); - if (item != NULL && item->type == cJSON_String) { - exprs[i].patterns[j].case_sensitive = case_sensitive_str_to_enum(item->valuestring); - } - - int is_hexbin = 0; - item = cJSON_GetObjectItem(pat_item, "is_hexbin"); - if (item != NULL && item->type == cJSON_String) { - is_hexbin = is_hexbin_str_to_int(item->valuestring); - } - - item = cJSON_GetObjectItem(pat_item, "pattern"); - if (item != NULL && item->type == cJSON_String) { - exprs[i].patterns[j].pat = ALLOC(char, strlen(item->valuestring) + 1); - - if (is_hexbin == 1) { - size_t pat_str_len = strlen(item->valuestring) + 1; - char *pat_str = ALLOC(char, pat_str_len); - pat_str_len = hex2bin(item->valuestring, strlen(item->valuestring), - pat_str, pat_str_len); - - memcpy(exprs[i].patterns[j].pat, pat_str, pat_str_len); - free(pat_str); - exprs[i].patterns[j].pat_len = pat_str_len; - } else { - memcpy(exprs[i].patterns[j].pat, item->valuestring, - strlen(item->valuestring)); - exprs[i].patterns[j].pat_len = strlen(item->valuestring); - } - } - - if (exprs[i].patterns->match_mode == EXPR_MATCH_MODE_SUB) { - item = cJSON_GetObjectItem(pat_item, "offset"); - if (item != NULL && item->type == cJSON_String) { - int key_left_offset = -1; - int key_right_offset = -1; - sscanf(item->valuestring, "%d~%d", &key_left_offset, &key_right_offset); - if (key_left_offset < -1 || key_right_offset < -1) { - printf("Error: offset should not less than -1, left_offset:%d, right_offset:%d\n", - key_left_offset, key_right_offset); - } - exprs[i].patterns[j].start_offset = key_left_offset; - exprs[i].patterns[j].end_offset = key_right_offset; - } else { - exprs[i].patterns[j].start_offset = -1; - exprs[i].patterns[j].end_offset = -1; - } - } - - if (exprs[i].patterns->match_mode == EXPR_MATCH_MODE_EXACTLY) { - exprs[i].patterns[j].start_offset = 0; - exprs[i].patterns[j].end_offset = exprs[i].patterns[j].pat_len - 1; - } - } - exprs[i].n_patterns = pattern_cnt; - } - - *n_expr = rule_cnt; -next: - cJSON_Delete(root); - FREE(json_buff); - return ret; -} - -void expr_array_free(struct expr_rule rules[], size_t n_rule) -{ - for (size_t i = 0; i < n_rule; i++) { - for (size_t j = 0; j < rules[i].n_patterns; j++) { - if (rules[i].patterns[j].pat != NULL) { - free(rules[i].patterns[j].pat); - rules[i].patterns[j].pat = NULL; - } - } - } -} - -TEST(hs_expr_matcher_init, invalid_input_parameter) -{ - struct expr_rule rules[64]; - size_t n_rule = 0; - - struct expr_matcher *matcher = expr_matcher_new(NULL, 0, EXPR_ENGINE_TYPE_HS, 1, g_logger); - EXPECT_TRUE(matcher == NULL); - - matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_HS, 1, g_logger); - EXPECT_TRUE(matcher == NULL); - - n_rule = 1; - rules[0].expr_id = 101; - rules[0].n_patterns = 10; - matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_HS, 1, g_logger); - EXPECT_TRUE(matcher == NULL); - - memset(rules, 0, sizeof(rules)); - n_rule = 1; - rules[0].expr_id = 101; - rules[0].n_patterns = 1; - matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_HS, 1, g_logger); - EXPECT_TRUE(matcher == NULL); -} - -TEST(rs_expr_matcher_init, invalid_input_parameter) -{ - struct expr_rule rules[64]; - size_t n_rule = 0; - - struct expr_matcher *matcher = expr_matcher_new(NULL, 0, EXPR_ENGINE_TYPE_RS, 1, g_logger); - EXPECT_TRUE(matcher == NULL); - - matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_RS, 1, g_logger); - EXPECT_TRUE(matcher == NULL); - - n_rule = 1; - rules[0].expr_id = 101; - rules[0].n_patterns = 10; - matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_RS, 1, g_logger); - EXPECT_TRUE(matcher == NULL); - - memset(rules, 0, sizeof(rules)); - n_rule = 1; - rules[0].expr_id = 101; - rules[0].n_patterns = 1; - matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_RS, 1, g_logger); - EXPECT_TRUE(matcher == NULL); -} - -TEST(hs_expr_matcher_match, literal_sub_has_normal_offset) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_HS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char scan_data1[64] = "hello aaa"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - char scan_data2[64] = "Ahello aaa"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 101); - - char scan_data3[64] = "Aahello aaa"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 101); - - char scan_data4[64] = "Aaahello aaa"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data4, strlen(scan_data4), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(rs_expr_matcher_match, literal_sub_has_normal_offset) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_RS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char scan_data1[64] = "hello aaa"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - char scan_data2[64] = "Ahello aaa"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 101); - - char scan_data3[64] = "Aahello aaa"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 101); - - char scan_data4[64] = "Aaahello aaa"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data4, strlen(scan_data4), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(hs_expr_matcher_match, literal_sub_has_left_unlimit_offset) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_HS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char scan_data1[64] = "hello bbb"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 102); - - char scan_data2[64] = "Ahello bbb"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 102); - - char scan_data3[64] = "Aahello bbb"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 102); - - char scan_data4[64] = "Aaahello bbb"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data4, strlen(scan_data4), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(rs_expr_matcher_match, literal_sub_has_left_unlimit_offset) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_RS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char scan_data1[64] = "hello bbb"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 102); - - char scan_data2[64] = "Ahello bbb"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 102); - - char scan_data3[64] = "Aahello bbb"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 102); - - char scan_data4[64] = "Aaahello bbb"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data4, strlen(scan_data4), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(hs_expr_matcher_match, literal_sub_has_right_unlimit_offset) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_HS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char scan_data1[64] = "hello ccc"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - char scan_data2[64] = "1234hello ccc"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - char scan_data3[64] = "12345hello ccc"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 103); - - char scan_data4[64] = "12345hello cccAaBb"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data4, strlen(scan_data4), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 103); - - char scan_data5[64] = "123456hello cccAaBb"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data5, strlen(scan_data5), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 103); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(rs_expr_matcher_match, literal_sub_has_right_unlimit_offset) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_RS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char scan_data1[64] = "hello ccc"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - char scan_data2[64] = "1234hello ccc"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - char scan_data3[64] = "12345hello ccc"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 103); - - char scan_data4[64] = "12345hello cccAaBb"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data4, strlen(scan_data4), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 103); - - char scan_data5[64] = "123456hello cccAaBb"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data5, strlen(scan_data5), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 103); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(hs_expr_matcher_match, literal_sub_with_no_offset) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_HS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char scan_data1[64] = "hello ddd"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 104); - - char scan_data2[64] = "123hello ddd"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 104); - - char scan_data3[64] = "123hello ddd456"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 104); - - char scan_data4[64] = "helloddd"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data4, strlen(scan_data4), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(rs_expr_matcher_match, literal_sub_with_no_offset) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_RS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char scan_data1[64] = "hello ddd"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 104); - - char scan_data2[64] = "123hello ddd"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 104); - - char scan_data3[64] = "123hello ddd456"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 104); - - char scan_data4[64] = "helloddd"; - memset(result, 0, sizeof(result)); - n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data4, strlen(scan_data4), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(hs_expr_matcher_match, literal_exactly) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_HS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char scan_data1[64] = "hello eee"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 105); - - char scan_data2[64] = "Ahello eee"; - memset(result, 0, sizeof(result)); - n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - char scan_data3[64] = "hello eeeB"; - memset(result, 0, sizeof(result)); - n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(rs_expr_matcher_match, literal_exactly) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_RS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char scan_data1[64] = "hello eee"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 105); - - char scan_data2[64] = "Ahello eee"; - memset(result, 0, sizeof(result)); - n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - char scan_data3[64] = "hello eeeB"; - memset(result, 0, sizeof(result)); - n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(hs_expr_matcher_match, literal_prefix) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_HS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char scan_data1[64] = "hello fff"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 106); - - char scan_data2[64] = "Ahello fff"; - memset(result, 0, sizeof(result)); - n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - char scan_data3[64] = "Ahello fffBCD"; - memset(result, 0, sizeof(result)); - n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - char scan_data4[64] = "hello fffBCD"; - memset(result, 0, sizeof(result)); - n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data4, strlen(scan_data4), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 106); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(rs_expr_matcher_match, literal_prefix) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_RS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char scan_data1[64] = "hello fff"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 106); - - char scan_data2[64] = "Ahello fff"; - memset(result, 0, sizeof(result)); - n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - char scan_data3[64] = "Ahello fffBCD"; - memset(result, 0, sizeof(result)); - n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - char scan_data4[64] = "hello fffBCD"; - memset(result, 0, sizeof(result)); - n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data4, strlen(scan_data4), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 106); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(hs_expr_matcher_match, literal_suffix) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_HS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char scan_data1[64] = "hello ggg"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 107); - - char scan_data2[64] = "ABChello ggg"; - memset(result, 0, sizeof(result)); - n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 107); - - char scan_data3[64] = "ABChello gggDEF"; - memset(result, 0, sizeof(result)); - n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - char scan_data4[64] = "hello gggDEF"; - memset(result, 0, sizeof(result)); - n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data4, strlen(scan_data4), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(rs_expr_matcher_match, literal_suffix) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_RS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char scan_data1[64] = "hello ggg"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 107); - - char scan_data2[64] = "ABChello ggg"; - memset(result, 0, sizeof(result)); - n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 107); - - char scan_data3[64] = "ABChello gggDEF"; - memset(result, 0, sizeof(result)); - n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data3, strlen(scan_data3), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - char scan_data4[64] = "hello gggDEF"; - memset(result, 0, sizeof(result)); - n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data4, strlen(scan_data4), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(hs_expr_matcher_match, literal_sub_with_hex) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_HS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char scan_data1[64] = "Content-Type: /html"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 108); - - char scan_data2[64] = " html"; - memset(result, 0, sizeof(result)); - n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(rs_expr_matcher_match, literal_sub_with_hex) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_RS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char scan_data1[64] = "Content-Type: /html"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 108); - - char scan_data2[64] = " html"; - memset(result, 0, sizeof(result)); - n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data2, strlen(scan_data2), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 0); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(hs_expr_matcher_match, literal_with_chinese) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_HS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char data0[64] = "#中国 你好"; - struct expr_scan_result result0[64] = {0}; - size_t n_result0 = 0; - ret = expr_matcher_match(matcher, 0, data0, strlen(data0), result0, 64, &n_result0); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result0, 1); - EXPECT_EQ(result0[0].rule_id, 110); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(rs_expr_matcher_match, literal_with_chinese) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_RS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char data0[64] = "#中国 你好"; - struct expr_scan_result result0[64] = {0}; - size_t n_result0 = 0; - ret = expr_matcher_match(matcher, 0, data0, strlen(data0), result0, 64, &n_result0); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result0, 1); - EXPECT_EQ(result0[0].rule_id, 110); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(hs_expr_matcher_match, same_pattern_different_offset) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_HS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char data[64] = "onetoday,anothertoday"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - ret = expr_matcher_match(matcher, 0, data, strlen(data), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 112); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(rs_expr_matcher_match, same_pattern_different_offset) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_RS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - char data[64] = "onetoday,anothertoday"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - ret = expr_matcher_match(matcher, 0, data, strlen(data), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 112); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(hs_expr_matcher_match, long_scan_data) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_HS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - const char* scan_data = "A directed path in a directed graph is a finite or infinite\ -sequence of edges which joins a sequence of distinct vertices, but with the added restriction\ -that the edges be all directed in the same direction."; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data, strlen(scan_data), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 113); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(rs_expr_matcher_match, long_scan_data) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_RS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - const char* scan_data = "A directed path in a directed graph is a finite or infinite\ -sequence of edges which joins a sequence of distinct vertices, but with the added restriction\ -that the edges be all directed in the same direction."; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - ret = expr_matcher_match(matcher, 0, scan_data, strlen(scan_data), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 113); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(expr_matcher_match, regex_expression_check) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./regex_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - for (size_t i = 0; i < n_rule; i++) { - for (size_t j = 0; j < rules[i].n_patterns; j++) { - expr_matcher_verify_regex_expression(rules[i].patterns[j].pat, g_logger); - } - } - - expr_array_free(rules, n_rule); -} - -TEST(hs_expr_matcher_stream, basic) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_HS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - const char* scan_data1 = "A directed path in a directed graph is a finite"; - const char *scan_data2 = " or infinite sequence of edges which joins a sequence of distinct vertices"; - - struct expr_scan_result result[64] = {0}; - size_t n_hit_result = 0; - int thread_id = 0; - - struct expr_matcher_stream *stream = expr_matcher_stream_open(matcher, thread_id); - EXPECT_TRUE(stream != NULL); - - ret = expr_matcher_stream_match(stream, scan_data1, strlen(scan_data1), result, 64, &n_hit_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_hit_result, 0); - - ret = expr_matcher_stream_match(stream, scan_data2, strlen(scan_data2), result, 64, &n_hit_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_hit_result, 1); - EXPECT_EQ(result[0].rule_id, 113); - - expr_matcher_stream_close(stream); - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(rs_expr_matcher_stream, basic) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_RS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - const char* scan_data1 = "A directed path in a directed graph is a finite"; - const char *scan_data2 = " or infinite sequence of edges which joins a sequence of distinct vertices"; - - struct expr_scan_result result[64] = {0}; - size_t n_hit_result = 0; - int thread_id = 0; - - struct expr_matcher_stream *stream = expr_matcher_stream_open(matcher, thread_id); - EXPECT_TRUE(stream != NULL); - - ret = expr_matcher_stream_match(stream, scan_data1, strlen(scan_data1), result, 64, &n_hit_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_hit_result, 0); - - ret = expr_matcher_stream_match(stream, scan_data2, strlen(scan_data2), result, 64, &n_hit_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_hit_result, 1); - EXPECT_EQ(result[0].rule_id, 113); - - expr_matcher_stream_close(stream); - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(hs_expr_matcher, regex_basic) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - ret = expr_matcher_verify_regex_expression("[0-9]rain", g_logger); - EXPECT_EQ(ret, 1); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_HS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - const char *scan_data1 = "http://www.cyberessays.com/search_results.php?action=search&query=username,abckkk,1234567"; - //const char *scan_data2 = "8rain"; - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 114); - - expr_matcher_free(matcher); - matcher = NULL; -} - -TEST(rs_expr_matcher, regex_basic) -{ - struct expr_rule rules[64] = {0}; - size_t n_rule = 0; - - int ret = parse_config_file("./literal_expr.conf", rules, &n_rule); - EXPECT_EQ(ret, 0); - - ret = expr_matcher_verify_regex_expression("[0-9]rain", g_logger); - EXPECT_EQ(ret, 1); - - struct expr_matcher *matcher = expr_matcher_new(rules, n_rule, EXPR_ENGINE_TYPE_RS, 1, g_logger); - EXPECT_TRUE(matcher != NULL); - expr_array_free(rules, n_rule); - - const char *scan_data1 = "http://www.cyberessays.com/search_results.php?action=search&query=username,abckkk,1234567"; - //const char *scan_data2 = "8rain"; - - struct expr_scan_result result[64] = {0}; - size_t n_result = 0; - - ret = expr_matcher_match(matcher, 0, scan_data1, strlen(scan_data1), result, 64, &n_result); - EXPECT_EQ(ret, 0); - EXPECT_EQ(n_result, 1); - EXPECT_EQ(result[0].rule_id, 114); - - expr_matcher_free(matcher); - matcher = NULL; -} - -int main(int argc, char **argv) -{ - int ret = 0; - ::testing::InitGoogleTest(&argc, argv); - g_logger = log_handle_create("./adapter_hs_gtest.log", 0); - - ret = RUN_ALL_TESTS(); - - log_handle_destroy(g_logger); - - return ret; -} \ No newline at end of file diff --git a/test/json_update/corrupted.json b/test/json_update/corrupted.json index fb491db..5508ec0 100644 --- a/test/json_update/corrupted.json +++ b/test/json_update/corrupted.json @@ -1,5 +1,5 @@ { - "compile_table": "COMPILE_DEFAULT", + "compile_table": "COMPILE", "group_table": "GROUP", "rules": [ { diff --git a/test/json_update/new.json b/test/json_update/new.json index 9c9f9ac..865a873 100644 --- a/test/json_update/new.json +++ b/test/json_update/new.json @@ -1,6 +1,6 @@ { - "compile_table": "COMPILE_DEFAULT", - "group2compile_table": "GROUP2COMPILE_DEFAULT", + "compile_table": "COMPILE", + "group2compile_table": "GROUP2COMPILE", "group2group_table": "GROUP2GROUP", "rules": [ { diff --git a/test/json_update/old.json b/test/json_update/old.json index af32b8e..3ca45e2 100644 --- a/test/json_update/old.json +++ b/test/json_update/old.json @@ -1,6 +1,6 @@ { - "compile_table": "COMPILE_DEFAULT", - "group2compile_table": "GROUP2COMPILE_DEFAULT", + "compile_table": "COMPILE", + "group2compile_table": "GROUP2COMPILE", "group2group_table": "GROUP2GROUP", "rules": [ { diff --git a/test/literal_expr.conf b/test/literal_expr.conf index 7bb2db6..72cec1c 100644 --- a/test/literal_expr.conf +++ b/test/literal_expr.conf @@ -5,7 +5,6 @@ "pattern_num": 1, "patterns": [ { - "pattern_type": "literal", "match_method": "sub", "case_sensitive": "yes", "is_hexbin": "no", @@ -19,7 +18,6 @@ "pattern_num": 1, "patterns": [ { - "pattern_type": "literal", "match_method": "sub", "case_sensitive": "yes", "is_hexbin": "no", @@ -33,7 +31,6 @@ "pattern_num": 1, "patterns": [ { - "pattern_type": "literal", "match_method": "sub", "case_sensitive": "yes", "is_hexbin": "no", @@ -47,7 +44,6 @@ "pattern_num": 1, "patterns": [ { - "pattern_type": "literal", "match_method": "sub", "case_sensitive": "yes", "is_hexbin": "no", @@ -60,7 +56,6 @@ "pattern_num": 1, "patterns": [ { - "pattern_type": "literal", "match_method": "exactly", "case_sensitive": "yes", "is_hexbin": "no", @@ -73,7 +68,6 @@ "pattern_num": 1, "patterns": [ { - "pattern_type": "literal", "match_method": "prefix", "case_sensitive": "yes", "is_hexbin": "no", @@ -86,7 +80,6 @@ "pattern_num": 1, "patterns": [ { - "pattern_type": "literal", "match_method": "suffix", "case_sensitive": "yes", "is_hexbin": "no", @@ -99,7 +92,6 @@ "pattern_num": 1, "patterns": [ { - "pattern_type": "literal", "match_method": "sub", "case_sensitive": "yes", "is_hexbin": "yes", @@ -113,14 +105,12 @@ "pattern_num": 2, "patterns": [ { - "pattern_type": "literal", "match_method": "sub", "case_sensitive": "yes", "is_hexbin": "no", "pattern": "multi" }, { - "pattern_type": "literal", "match_method": "sub", "case_sensitive": "yes", "is_hexbin": "no", @@ -133,7 +123,6 @@ "pattern_num": 1, "patterns": [ { - "pattern_type": "literal", "match_method": "sub", "case_sensitive": "yes", "is_hexbin": "no", @@ -146,7 +135,6 @@ "pattern_num": 1, "patterns": [ { - "pattern_type": "literal", "match_method": "sub", "case_sensitive": "yes", "is_hexbin": "no", @@ -159,7 +147,6 @@ "pattern_num": 2, "patterns": [ { - "pattern_type": "literal", "match_method": "sub", "case_sensitive": "yes", "is_hexbin": "no", @@ -167,7 +154,6 @@ "offset": "3~7" }, { - "pattern_type": "literal", "match_method": "sub", "case_sensitive": "yes", "is_hexbin": "no", @@ -181,26 +167,12 @@ "pattern_num": 1, "patterns": [ { - "pattern_type": "literal", "match_method": "sub", "case_sensitive": "yes", "is_hexbin": "no", "pattern": "a finite or infinite" } ] - }, - { - "expr_id": 114, - "pattern_num": 1, - "patterns": [ - { - "pattern_type": "regex", - "match_method": "sub", - "case_sensitive": "yes", - "is_hexbin": "no", - "pattern": "query=(.*)" - } - ] } ] -} +} \ No newline at end of file diff --git a/test/maat_ex_data_gtest.cpp b/test/maat_ex_data_gtest.cpp index 84e2848..07d4223 100644 --- a/test/maat_ex_data_gtest.cpp +++ b/test/maat_ex_data_gtest.cpp @@ -163,4 +163,4 @@ int main(int argc, char ** argv) maat_free(g_maat_inst); g_maat_inst = NULL; return ret; -} +} \ No newline at end of file diff --git a/test/maat_framework_gtest.cpp b/test/maat_framework_gtest.cpp index 1649f02..36f4f32 100644 --- a/test/maat_framework_gtest.cpp +++ b/test/maat_framework_gtest.cpp @@ -287,12 +287,12 @@ int test_add_expr_command(struct maat *maat_inst, const char *expr_table, memset(huge_serv_def, 's', sizeof(huge_serv_def) - 1); huge_serv_def[sizeof(huge_serv_def) - 1] = '\0'; - int ret = compile_table_set_line(maat_inst, "COMPILE_DEFAULT", MAAT_OP_ADD, compile_id, + int ret = compile_table_set_line(maat_inst, "COMPILE", MAAT_OP_ADD, compile_id, huge_serv_def, 1, timeout); EXPECT_EQ(ret, 1); long long group_id = maat_cmd_incrby(maat_inst, "SEQUENCE_GROUP", 1); - ret = group2compile_table_set_line(maat_inst, "GROUP2COMPILE_DEFAULT", MAAT_OP_ADD, group_id, + ret = group2compile_table_set_line(maat_inst, "GROUP2COMPILE", MAAT_OP_ADD, group_id, compile_id, 0, "null", 1, timeout); EXPECT_EQ(ret, 1); @@ -306,7 +306,7 @@ int test_add_expr_command(struct maat *maat_inst, const char *expr_table, int del_command(struct maat *maat_inst, int compile_id) { - return compile_table_set_line(maat_inst, "COMPILE_DEFAULT", MAAT_OP_DEL, compile_id, "null", 1, 0); + return compile_table_set_line(maat_inst, "COMPILE", MAAT_OP_DEL, compile_id, "null", 1, 0); } static void random_keyword_generate(char *keyword_buf, size_t sz) @@ -461,11 +461,6 @@ void scan_with_old_or_new_cfg(struct maat *maat_inst, int is_old) if (is_old) { EXPECT_EQ(ret, MAAT_SCAN_HIT); EXPECT_TRUE(results[0] == 1); - - int table_id = -1; - int table_cnt = maat_state_get_compile_table_ids(state, results, 1, &table_id); - EXPECT_EQ(table_cnt, 1); - EXPECT_EQ(table_id, 0); } else { EXPECT_EQ(ret, MAAT_SCAN_OK); } @@ -749,8 +744,7 @@ TEST_F(MaatFlagScan, FlagPlus) { state = NULL; } -//hyperscan engine -class MaatHsStringScan : public testing::Test +class MaatStringScan : public testing::Test { protected: static void SetUpTestCase() { @@ -772,7 +766,6 @@ protected: maat_options_set_stat_file(opts, "./stat.log"); maat_options_set_logger(opts, "./maat_framework_gtest.log", LOG_LEVEL_INFO); maat_options_set_accept_tags(opts, accept_tags); - //maat_options_set_expr_engine(opts, MAAT_EXPR_ENGINE_HS); //default _shared_maat_inst = maat_new(opts, table_info_path); maat_options_free(opts); @@ -792,12 +785,12 @@ protected: static struct maat *_shared_maat_inst; }; -struct maat *MaatHsStringScan::_shared_maat_inst; -struct log_handle *MaatHsStringScan::logger; +struct maat *MaatStringScan::_shared_maat_inst; +struct log_handle *MaatStringScan::logger; -TEST_F(MaatHsStringScan, ScanDataOnlyOneByte) { +TEST_F(MaatStringScan, ScanDataOnlyOneByte) { const char *table_name = "HTTP_URL"; - struct maat *maat_inst = MaatHsStringScan::_shared_maat_inst; + struct maat *maat_inst = MaatStringScan::_shared_maat_inst; int table_id = maat_get_table_id(maat_inst, table_name); ASSERT_GT(table_id, 0); @@ -816,9 +809,9 @@ TEST_F(MaatHsStringScan, ScanDataOnlyOneByte) { state = NULL; } -TEST_F(MaatHsStringScan, Full) { +TEST_F(MaatStringScan, Full) { const char *table_name = "HTTP_URL"; - struct maat *maat_inst = MaatHsStringScan::_shared_maat_inst; + struct maat *maat_inst = MaatStringScan::_shared_maat_inst; int table_id = maat_get_table_id(maat_inst, table_name); ASSERT_GT(table_id, 0); @@ -838,14 +831,14 @@ TEST_F(MaatHsStringScan, Full) { state = NULL; } -TEST_F(MaatHsStringScan, Regex) { +TEST_F(MaatStringScan, Regex) { int ret = 0; long long results[ARRAY_SIZE] = {0}; size_t n_hit_result = 0; int thread_id = 0; const char *cookie = "Cookie: Txa123aheadBCAxd"; const char *table_name = "HTTP_URL"; - struct maat *maat_inst = MaatHsStringScan::_shared_maat_inst; + struct maat *maat_inst = MaatStringScan::_shared_maat_inst; struct maat_state *state = maat_state_new(maat_inst, thread_id); int table_id = maat_get_table_id(maat_inst, table_name); @@ -857,7 +850,7 @@ TEST_F(MaatHsStringScan, Regex) { state = NULL; } -TEST_F(MaatHsStringScan, ExprPlus) { +TEST_F(MaatStringScan, ExprPlus) { long long results[ARRAY_SIZE] = {0}; size_t n_hit_result = 0; int thread_id = 0; @@ -866,7 +859,7 @@ TEST_F(MaatHsStringScan, ExprPlus) { const char *scan_data1 = "http://www.cyberessays.com/search_results.php?action=search&query=abckkk,1234567"; const char *scan_data2 = "Addis Sapphire Hotel"; const char *table_name = "HTTP_SIGNATURE"; - struct maat *maat_inst = MaatHsStringScan::_shared_maat_inst; + struct maat *maat_inst = MaatStringScan::_shared_maat_inst; struct maat_state *state = maat_state_new(maat_inst, thread_id); int table_id = maat_get_table_id(maat_inst, table_name); @@ -892,12 +885,12 @@ TEST_F(MaatHsStringScan, ExprPlus) { state = NULL; } -TEST_F(MaatHsStringScan, ExprPlusWithOffset) +TEST_F(MaatStringScan, ExprPlusWithOffset) { long long results[ARRAY_SIZE] = {0}; size_t n_hit_result = 0; int thread_id = 0; - struct maat *maat_inst = MaatHsStringScan::_shared_maat_inst; + struct maat *maat_inst = MaatStringScan::_shared_maat_inst; struct maat_state *state = maat_state_new(maat_inst, thread_id); const char *region_name = "Payload"; unsigned char udp_payload_not_hit[] = { /* Stun packet */ @@ -946,11 +939,11 @@ TEST_F(MaatHsStringScan, ExprPlusWithOffset) state = NULL; } -TEST_F(MaatHsStringScan, ExprPlusWithHex) { +TEST_F(MaatStringScan, ExprPlusWithHex) { long long results[ARRAY_SIZE] = {0}; size_t n_hit_result = 0; int thread_id = 0; - struct maat *maat_inst = MaatHsStringScan::_shared_maat_inst; + struct maat *maat_inst = MaatStringScan::_shared_maat_inst; struct maat_state *state = maat_state_new(maat_inst, thread_id); const char *scan_data1 = "text/html; charset=UTF-8"; const char *scan_data2 = "Batman\\:Take me Home.Superman/:Fine,stay with me."; @@ -982,11 +975,11 @@ TEST_F(MaatHsStringScan, ExprPlusWithHex) { state = NULL; } -TEST_F(MaatHsStringScan, ExprAndExprPlus) { +TEST_F(MaatStringScan, ExprAndExprPlus) { long long results[ARRAY_SIZE] = {0}; size_t n_hit_result = 0; int thread_id = 0; - struct maat *maat_inst = MaatHsStringScan::_shared_maat_inst; + struct maat *maat_inst = MaatStringScan::_shared_maat_inst; struct maat_state *state = maat_state_new(maat_inst, thread_id); const char *expr_table_name = "HTTP_URL"; const char *expr_plus_table_name = "HTTP_SIGNATURE"; @@ -1014,11 +1007,11 @@ TEST_F(MaatHsStringScan, ExprAndExprPlus) { state = NULL; } -TEST_F(MaatHsStringScan, ShouldNotHitExprPlus) { +TEST_F(MaatStringScan, ShouldNotHitExprPlus) { long long results[ARRAY_SIZE] = {0}; size_t n_hit_result = 0; int thread_id = 0; - struct maat *maat_inst = MaatHsStringScan::_shared_maat_inst; + struct maat *maat_inst = MaatStringScan::_shared_maat_inst; struct maat_state *state = maat_state_new(maat_inst, thread_id); const char *region_name = "tcp.payload"; unsigned char udp_payload_not_hit[] = { /* Stun packet */ @@ -1048,10 +1041,10 @@ TEST_F(MaatHsStringScan, ShouldNotHitExprPlus) { state = NULL; } -TEST_F(MaatHsStringScan, Expr8) { +TEST_F(MaatStringScan, Expr8) { const char *table_name = "KEYWORDS_TABLE"; int thread_id = 0; - struct maat *maat_inst = MaatHsStringScan::_shared_maat_inst; + struct maat *maat_inst = MaatStringScan::_shared_maat_inst; struct maat_state *state = maat_state_new(maat_inst, thread_id); int table_id = maat_get_table_id(maat_inst, table_name); char scan_data[128] = "string1, string2, string3, string4, string5, string6, string7, string8"; @@ -1072,11 +1065,11 @@ TEST_F(MaatHsStringScan, Expr8) { state = NULL; } -TEST_F(MaatHsStringScan, HexBinCaseSensitive) { +TEST_F(MaatStringScan, HexBinCaseSensitive) { const char *table_name = "KEYWORDS_TABLE"; const char *scan_data1 = "String TeST should not hit."; const char *scan_data2 = "String TEST should hit"; - struct maat *maat_inst = MaatHsStringScan::_shared_maat_inst; + struct maat *maat_inst = MaatStringScan::_shared_maat_inst; int thread_id = 0; int table_id = maat_get_table_id(maat_inst, table_name); @@ -1099,7 +1092,7 @@ TEST_F(MaatHsStringScan, HexBinCaseSensitive) { maat_state_free(state); } -TEST_F(MaatHsStringScan, BugReport20190325) { +TEST_F(MaatStringScan, BugReport20190325) { unsigned char scan_data[] = {/* Packet 1 */ 0x01, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x32, 0x00, 0x00, @@ -1119,7 +1112,7 @@ TEST_F(MaatHsStringScan, BugReport20190325) { 0x00, 0x31, 0x3a, 0x47, 0x32, 0x2e, 0x34, 0x30, 0x00}; const char *table_name = "TROJAN_PAYLOAD"; - struct maat *maat_inst = MaatHsStringScan::_shared_maat_inst; + struct maat *maat_inst = MaatStringScan::_shared_maat_inst; int thread_id = 0; int table_id = maat_get_table_id(maat_inst, table_name); @@ -1137,13 +1130,13 @@ TEST_F(MaatHsStringScan, BugReport20190325) { state = NULL; } -TEST_F(MaatHsStringScan, PrefixAndSuffix) { +TEST_F(MaatStringScan, PrefixAndSuffix) { const char *hit_twice = "ceshi3@mailhost.cn"; const char *hit_suffix = "11111111111ceshi3@mailhost.cn"; const char *hit_prefix = "ceshi3@mailhost.cn11111111111"; const char *cont_sz_table_name = "CONTENT_SIZE"; const char *mail_addr_table_name = "MAIL_ADDR"; - struct maat *maat_inst = MaatHsStringScan::_shared_maat_inst; + struct maat *maat_inst = MaatStringScan::_shared_maat_inst; int thread_id = 0; int cont_sz_table_id = maat_get_table_id(maat_inst, cont_sz_table_name); @@ -1183,10 +1176,10 @@ TEST_F(MaatHsStringScan, PrefixAndSuffix) { state = NULL; } -TEST_F(MaatHsStringScan, MaatUnescape) { +TEST_F(MaatStringScan, MaatUnescape) { const char *scan_data = "Batman\\:Take me Home.Superman/:Fine,stay with me."; const char *table_name = "KEYWORDS_TABLE"; - struct maat *maat_inst = MaatHsStringScan::_shared_maat_inst; + struct maat *maat_inst = MaatStringScan::_shared_maat_inst; int thread_id = 0; int table_id = maat_get_table_id(maat_inst, table_name); @@ -1204,13 +1197,13 @@ TEST_F(MaatHsStringScan, MaatUnescape) { state = NULL; } -TEST_F(MaatHsStringScan, OffsetChunk64) { +TEST_F(MaatStringScan, OffsetChunk64) { const char *table_name = "IMAGE_FP"; const char *file_name = "./testdata/mesa_logo.jpg"; long long results[ARRAY_SIZE] = {0}; size_t n_hit_result = 0; int thread_id = 0; - struct maat *maat_inst = MaatHsStringScan::_shared_maat_inst; + struct maat *maat_inst = MaatStringScan::_shared_maat_inst; struct maat_state *state = maat_state_new(maat_inst, thread_id); FILE *fp = fopen(file_name, "r"); @@ -1243,13 +1236,13 @@ TEST_F(MaatHsStringScan, OffsetChunk64) { state = NULL; } -TEST_F(MaatHsStringScan, OffsetChunk1460) { +TEST_F(MaatStringScan, OffsetChunk1460) { const char *table_name = "IMAGE_FP"; const char *file_name = "./testdata/mesa_logo.jpg"; long long results[ARRAY_SIZE] = {0}; size_t n_hit_result = 0; int thread_id = 0; - struct maat *maat_inst = MaatHsStringScan::_shared_maat_inst; + struct maat *maat_inst = MaatStringScan::_shared_maat_inst; struct maat_state *state = maat_state_new(maat_inst, thread_id); FILE *fp = fopen(file_name, "r"); @@ -1282,14 +1275,14 @@ TEST_F(MaatHsStringScan, OffsetChunk1460) { state = NULL; } -TEST_F(MaatHsStringScan, StreamScanUTF8) { +TEST_F(MaatStringScan, StreamScanUTF8) { const char *table_name = "TROJAN_PAYLOAD"; const char* file_name = "./testdata/jd.com.html"; long long results[ARRAY_SIZE] = {0}; size_t n_hit_result = 0; int thread_id = 0; char scan_data[2048]; - struct maat *maat_inst = MaatHsStringScan::_shared_maat_inst; + struct maat *maat_inst = MaatStringScan::_shared_maat_inst; struct maat_state *state = maat_state_new(maat_inst, thread_id); FILE *fp = fopen(file_name, "r"); @@ -1320,11 +1313,11 @@ TEST_F(MaatHsStringScan, StreamScanUTF8) { state = NULL; } -TEST_F(MaatHsStringScan, StreamInput) { +TEST_F(MaatStringScan, StreamInput) { long long results[ARRAY_SIZE] = {0}; size_t n_hit_result = 0; int thread_id = 0; - struct maat *maat_inst = MaatHsStringScan::_shared_maat_inst; + struct maat *maat_inst = MaatStringScan::_shared_maat_inst; struct maat_state *state = maat_state_new(maat_inst, thread_id); const char *scan_data = "http://www.cyberessays.com/search_results.php?action=search&query=yulingjing,abckkk,1234567"; const char *table_name = "HTTP_URL"; @@ -1349,13 +1342,13 @@ TEST_F(MaatHsStringScan, StreamInput) { state = NULL; } -TEST_F(MaatHsStringScan, dynamic_config) { +TEST_F(MaatStringScan, dynamic_config) { const char *table_name = "HTTP_URL"; char data[128] = "hello world, welcome to maat version4, it's funny."; long long results[ARRAY_SIZE] = {0}; size_t n_hit_result = 0; int thread_id = 0; - struct maat *maat_inst = MaatHsStringScan::_shared_maat_inst; + struct maat *maat_inst = MaatStringScan::_shared_maat_inst; struct maat_state *state = maat_state_new(maat_inst, thread_id); int table_id = maat_get_table_id(maat_inst, table_name); @@ -1365,8 +1358,8 @@ TEST_F(MaatHsStringScan, dynamic_config) { EXPECT_EQ(n_hit_result, 0); maat_state_reset(state); - const char *compile_table_name = "COMPILE_DEFAULT"; - const char *g2c_table_name = "GROUP2COMPILE_DEFAULT"; + const char *compile_table_name = "COMPILE"; + const char *g2c_table_name = "GROUP2COMPILE"; /* compile table add line */ long long compile_id = maat_cmd_incrby(maat_inst, "TEST_SEQ", 1); @@ -1386,7 +1379,7 @@ TEST_F(MaatHsStringScan, dynamic_config) { keywords, NULL, 1, 0, 0, 0); /* EXPR_TYPE_AND MATCH_METHOD_SUB */ EXPECT_EQ(ret, 1); - sleep(WAIT_FOR_EFFECTIVE_S * 3); + sleep(WAIT_FOR_EFFECTIVE_S); ret = maat_scan_string(maat_inst, table_id, data, strlen(data), results, ARRAY_SIZE, &n_hit_result, state); @@ -1419,677 +1412,7 @@ TEST_F(MaatHsStringScan, dynamic_config) { state = NULL; } -class MaatRsStringScan : public testing::Test -{ -protected: - static void SetUpTestCase() { - const char *accept_tags = "{\"tags\":[{\"tag\":\"location\",\"value\":\"北京/朝阳/华严北里/甲22号\"}," - "{\"tag\":\"isp\",\"value\":\"移动\"},{\"tag\":\"location\",\"value\":\"Astana\"}]}"; - char redis_ip[64] = "127.0.0.1"; - int redis_port = 6379; - int redis_db = 0; - - logger = log_handle_create("./maat_framework_gtest.log", 0); - int ret = write_config_to_redis(redis_ip, redis_port, redis_db, logger); - if (ret < 0) { - log_error(logger, MODULE_FRAMEWORK_GTEST, - "[%s:%d] write config to redis failed.", __FUNCTION__, __LINE__); - } - - struct maat_options *opts = maat_options_new(); - maat_options_set_redis(opts, redis_ip, redis_port, redis_db); - maat_options_set_stat_file(opts, "./stat.log"); - maat_options_set_logger(opts, "./maat_framework_gtest.log", LOG_LEVEL_INFO); - maat_options_set_accept_tags(opts, accept_tags); - maat_options_set_expr_engine(opts, MAAT_EXPR_ENGINE_RS); - - _shared_maat_inst = maat_new(opts, table_info_path); - maat_options_free(opts); - if (NULL == _shared_maat_inst) { - log_error(logger, MODULE_FRAMEWORK_GTEST, - "[%s:%d] create maat instance in MaatStringScan failed.", - __FUNCTION__, __LINE__); - } - } - - static void TearDownTestCase() { - maat_free(_shared_maat_inst); - log_handle_destroy(logger); - } - - static struct log_handle *logger; - static struct maat *_shared_maat_inst; -}; - -struct maat *MaatRsStringScan::_shared_maat_inst; -struct log_handle *MaatRsStringScan::logger; - -TEST_F(MaatRsStringScan, ScanDataOnlyOneByte) { - const char *table_name = "HTTP_URL"; - struct maat *maat_inst = MaatRsStringScan::_shared_maat_inst; - - int table_id = maat_get_table_id(maat_inst, table_name); - ASSERT_GT(table_id, 0); - - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - int thread_id = 0; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - const char scan_data = 0x20; - - int ret = maat_scan_string(maat_inst, table_id, &scan_data, sizeof(scan_data), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_OK); - EXPECT_EQ(n_hit_result, 0); - maat_state_free(state); - state = NULL; -} - -TEST_F(MaatRsStringScan, Full) { - const char *table_name = "HTTP_URL"; - struct maat *maat_inst = MaatRsStringScan::_shared_maat_inst; - - int table_id = maat_get_table_id(maat_inst, table_name); - ASSERT_GT(table_id, 0); - - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - int thread_id = 0; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - const char *scan_data = "http://www.cyberessays.com/search_results.php?action=search&query=username,abckkk,1234567"; - - int ret = maat_scan_string(maat_inst, table_id, scan_data, strlen(scan_data), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(n_hit_result, 1); - EXPECT_EQ(results[0], 125); - maat_state_free(state); - state = NULL; -} - -TEST_F(MaatRsStringScan, Regex) { - int ret = 0; - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - int thread_id = 0; - const char *cookie = "Cookie: Txa123aheadBCAxd"; - const char *table_name = "HTTP_URL"; - struct maat *maat_inst = MaatRsStringScan::_shared_maat_inst; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - - int table_id = maat_get_table_id(maat_inst, table_name); - ret = maat_scan_string(maat_inst, table_id, cookie, strlen(cookie), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(results[0], 146); - maat_state_free(state); - state = NULL; -} - -TEST_F(MaatRsStringScan, ExprPlus) { - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - int thread_id = 0; - const char *region_name1 ="HTTP URL"; - const char *region_name2 ="我的diStricT"; - const char *scan_data1 = "http://www.cyberessays.com/search_results.php?action=search&query=abckkk,1234567"; - const char *scan_data2 = "Addis Sapphire Hotel"; - const char *table_name = "HTTP_SIGNATURE"; - struct maat *maat_inst = MaatRsStringScan::_shared_maat_inst; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - - int table_id = maat_get_table_id(maat_inst, table_name); - int ret = maat_scan_string(maat_inst, table_id, scan_data1, strlen(scan_data1), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_ERR);//Should return error for district not setting. - - ret = maat_state_set_scan_district(state, table_id, region_name1, strlen(region_name1)); - ASSERT_EQ(ret, 0); - ret = maat_scan_string(maat_inst, table_id, scan_data1, strlen(scan_data1), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(results[0], 128); - maat_state_reset(state); - - ret = maat_state_set_scan_district(state, table_id, region_name2, strlen(region_name2)); - ASSERT_EQ(ret, 0); - ret = maat_scan_string(maat_inst, table_id, scan_data2, strlen(scan_data2), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(results[0], 190); - maat_state_free(state); - state = NULL; -} - -TEST_F(MaatRsStringScan, ExprPlusWithOffset) -{ - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - int thread_id = 0; - struct maat *maat_inst = MaatRsStringScan::_shared_maat_inst; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - const char *region_name = "Payload"; - unsigned char udp_payload_not_hit[] = { /* Stun packet */ - 0x00, 0x03, 0x00, 0x4a, 0x21, 0x12, 0xa4, 0x42, - 0x4f, 0xc2, 0xc2, 0x70, 0xb3, 0xa8, 0x4e, 0x22, - 0xf5, 0x22, 0x87, 0x4c, 0x40, 0x00, 0x00, 0x46, - 0x03, 0x02, 0xab, 0x39, 0xbb, 0x97, 0xe5, 0x01, - 0x3a, 0x46, 0x1c, 0x28, 0x5b, 0xab, 0xfa, 0x9a, - 0xab, 0x2e, 0x71, 0x39, 0x66, 0xa0, 0xd7, 0xb9, - 0xd8, 0x41, 0xa7, 0xa0, 0x84, 0xa9, 0xf3, 0x1b, - 0x03, 0x7f, 0xa8, 0x28, 0xa2, 0xd3, 0x64, 0xc2, - 0x3d, 0x20, 0xe0, 0xb1, 0x41, 0x12, 0x6c, 0x2f, - 0xc5, 0xbb, 0xc3, 0xba, 0x69, 0x73, 0x52, 0x64, - 0xf6, 0x30, 0x81, 0xf4, 0x3f, 0xc2, 0x19, 0x6a, - 0x68, 0x61, 0x93, 0x08, 0xc0, 0x0a }; - unsigned char udp_payload_hit[] = { /* Stun packet */ //rule:"1-1:03&9-10:2d&14-16:2d34&19-21:2d&24-25:2d" - 0x00, 0x03, 0x00, 0x4a, 0x21, 0x12, 0xa4, 0x42, //1-1:03 - 0x4f, 0xc2, 0x2d, 0x70, 0xb3, 0xa8, 0x4e, 0x2d, //10-10:2d - 0x34, 0x22, 0x87, 0x4c, 0x2d, 0x00, 0x00, 0x46, //15-16:2d34 - 0x2d, 0x34, 0xab, 0x39, 0xbb, 0x97, 0xe5, 0x01, //20-20:2d - 0x03, 0x46, 0x1c, 0x28, 0x5b, 0xab, 0xfa, 0x9a, //24-24:2d - 0xab, 0x2e, 0x71, 0x39, 0x66, 0xa0, 0xd7, 0xb9, - 0xd8, 0x41, 0xa7, 0xa0, 0x84, 0xa9, 0xf3, 0x1b, - 0x03, 0x7f, 0xa8, 0x28, 0xa2, 0xd3, 0x64, 0xc2, - 0x3d, 0x20, 0xe0, 0xb1, 0x41, 0x12, 0x6c, 0x2f, - 0xc5, 0xbb, 0xc3, 0xba, 0x69, 0x73, 0x52, 0x64, - 0xf6, 0x30, 0x81, 0xf4, 0x3f, 0xc2, 0x19, 0x6a, - 0x68, 0x61, 0x93, 0x08, 0xc0, 0x0a }; - - int table_id = maat_get_table_id(maat_inst, "APP_PAYLOAD"); - ASSERT_GT(table_id, 0); - - int ret = maat_state_set_scan_district(state, table_id, region_name, strlen(region_name)); - EXPECT_EQ(ret, 0); - - ret = maat_scan_string(maat_inst, table_id, (char*)udp_payload_not_hit, sizeof(udp_payload_not_hit), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_OK); - - ret = maat_scan_string(maat_inst, table_id, (char*)udp_payload_hit, sizeof(udp_payload_hit), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(results[0], 148); - - maat_state_free(state); - state = NULL; -} - -TEST_F(MaatRsStringScan, ExprPlusWithHex) { - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - int thread_id = 0; - struct maat *maat_inst = MaatRsStringScan::_shared_maat_inst; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - const char *scan_data1 = "text/html; charset=UTF-8"; - const char *scan_data2 = "Batman\\:Take me Home.Superman/:Fine,stay with me."; - const char *region_name1 = "Content-Type"; - const char *region_name2 = "User-Agent"; - - int table_id = maat_get_table_id(maat_inst, "HTTP_SIGNATURE"); - ASSERT_GT(table_id, 0); - - int ret = maat_state_set_scan_district(state, table_id, region_name1, strlen(region_name1)); - ASSERT_EQ(ret, 0); - ret = maat_scan_string(maat_inst, table_id, scan_data1, strlen(scan_data1), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(results[0], 156); - - ret = maat_state_set_scan_district(state, table_id, region_name2, strlen(region_name2)); - ASSERT_EQ(ret, 0); - ret = maat_scan_string(maat_inst, table_id, scan_data1, strlen(scan_data1), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_OK); //maat-v3 consider as half hit, it's unreasonable - - table_id = maat_get_table_id(maat_inst, "KEYWORDS_TABLE"); - ret = maat_scan_string(maat_inst, table_id, scan_data2, strlen(scan_data2), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(results[0], 132); - maat_state_free(state); - state = NULL; -} - -TEST_F(MaatRsStringScan, ExprAndExprPlus) { - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - int thread_id = 0; - struct maat *maat_inst = MaatRsStringScan::_shared_maat_inst; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - const char *expr_table_name = "HTTP_URL"; - const char *expr_plus_table_name = "HTTP_SIGNATURE"; - const char *region_name = "I love China"; - const char *scan_data = "today is Monday and yesterday is Tuesday"; - - int expr_table_id = maat_get_table_id(maat_inst, expr_table_name); - int expr_plus_table_id = maat_get_table_id(maat_inst, expr_plus_table_name); - - int ret = maat_scan_string(maat_inst, expr_plus_table_id, scan_data, strlen(scan_data), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_ERR); - - ret = maat_state_set_scan_district(state, expr_plus_table_id, region_name, strlen(region_name)); - ASSERT_EQ(ret, 0); - ret = maat_scan_string(maat_inst, expr_plus_table_id, scan_data, strlen(scan_data), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HALF_HIT); - - ret = maat_scan_string(maat_inst, expr_table_id, scan_data, strlen(scan_data), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(results[0], 195); - maat_state_free(state); - state = NULL; -} - -TEST_F(MaatRsStringScan, ShouldNotHitExprPlus) { - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - int thread_id = 0; - struct maat *maat_inst = MaatRsStringScan::_shared_maat_inst; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - const char *region_name = "tcp.payload"; - unsigned char udp_payload_not_hit[] = { /* Stun packet */ - 0x00, 0x03, 0x00, 0x4a, 0x21, 0x12, 0xa4, 0x42, - 0x4f, 0xc2, 0xc2, 0x70, 0xb3, 0xa8, 0x4e, 0x22, - 0xf5, 0x22, 0x87, 0x4c, 0x40, 0x00, 0x00, 0x46, - 0x03, 0x02, 0xab, 0x39, 0xbb, 0x97, 0xe5, 0x01, - 0x3a, 0x46, 0x1c, 0x28, 0x5b, 0xab, 0xfa, 0x9a, - 0xab, 0x2e, 0x71, 0x39, 0x66, 0xa0, 0xd7, 0xb9, - 0xd8, 0x41, 0xa7, 0xa0, 0x84, 0xa9, 0xf3, 0x1b, - 0x03, 0x7f, 0xa8, 0x28, 0xa2, 0xd3, 0x64, 0xc2, - 0x3d, 0x20, 0xe0, 0xb1, 0x41, 0x12, 0x6c, 0x2f, - 0xc5, 0xbb, 0xc3, 0xba, 0x69, 0x73, 0x52, 0x64, - 0xf6, 0x30, 0x81, 0xf4, 0x3f, 0xc2, 0x19, 0x6a, - 0x68, 0x61, 0x93, 0x08, 0xc0, 0x0a, 0xab, 0x00 }; - - int table_id = maat_get_table_id(maat_inst, "APP_PAYLOAD"); - ASSERT_GT(table_id, 0); - - int ret = maat_state_set_scan_district(state, table_id, region_name, strlen(region_name)); - ASSERT_EQ(ret, 0); - - ret = maat_scan_string(maat_inst, table_id, (char *)udp_payload_not_hit, sizeof(udp_payload_not_hit), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_OK); //maat-v3 consider as half hit, it's unreasonable - maat_state_free(state); - state = NULL; -} - -TEST_F(MaatRsStringScan, Expr8) { - const char *table_name = "KEYWORDS_TABLE"; - int thread_id = 0; - struct maat *maat_inst = MaatRsStringScan::_shared_maat_inst; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - int table_id = maat_get_table_id(maat_inst, table_name); - char scan_data[128] = "string1, string2, string3, string4, string5, string6, string7, string8"; - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - - int ret = maat_scan_string(maat_inst, table_id, scan_data, strlen(scan_data), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(n_hit_result, 1); - EXPECT_EQ(results[0], 182); - - struct maat_hit_path hit_path[HIT_PATH_SIZE] = {0}; - int n_read = 0; - n_read = maat_state_get_hit_paths(state, hit_path, HIT_PATH_SIZE); - EXPECT_NE(n_read, 0); - maat_state_free(state); - state = NULL; -} - -TEST_F(MaatRsStringScan, HexBinCaseSensitive) { - const char *table_name = "KEYWORDS_TABLE"; - const char *scan_data1 = "String TeST should not hit."; - const char *scan_data2 = "String TEST should hit"; - struct maat *maat_inst = MaatRsStringScan::_shared_maat_inst; - int thread_id = 0; - - int table_id = maat_get_table_id(maat_inst, table_name); - ASSERT_GT(table_id, 0); - - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - int ret = maat_scan_string(maat_inst, table_id, scan_data1, strlen(scan_data1), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_OK); - maat_state_reset(state); - - ret = maat_scan_string(maat_inst, table_id, scan_data2, strlen(scan_data2), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(n_hit_result, 2); - EXPECT_EQ(results[0], 206); - EXPECT_EQ(results[1], 191); - maat_state_free(state); -} - -TEST_F(MaatRsStringScan, BugReport20190325) { - unsigned char scan_data[] = {/* Packet 1 */ - 0x01, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, - 0x00, 0xf4, 0x01, 0x00, 0x00, 0x32, 0x00, 0x00, - 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2d, 0x3d, 0x3d, 0x20, 0x48, 0x3d, 0x48, 0x20, - 0x3d, 0x3d, 0x2d, 0x3a, 0x00, 0x02, 0x00, 0x00, - 0x00, 0x07, 0x0e, 0x00, 0x00, 0xe8, 0x03, 0x00, - 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x33, - 0x2e, 0x31, 0x39, 0x2e, 0x30, 0x2d, 0x31, 0x35, - 0x2d, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x00, 0x31, 0x3a, 0x47, 0x32, 0x2e, 0x34, 0x30, - 0x00}; - const char *table_name = "TROJAN_PAYLOAD"; - struct maat *maat_inst = MaatRsStringScan::_shared_maat_inst; - int thread_id = 0; - - int table_id = maat_get_table_id(maat_inst, table_name); - ASSERT_GT(table_id, 0); - - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - int ret = maat_scan_string(maat_inst, table_id, (char *)scan_data, sizeof(scan_data), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(n_hit_result, 1); - EXPECT_EQ(results[0], 150); - maat_state_free(state); - state = NULL; -} - -TEST_F(MaatRsStringScan, PrefixAndSuffix) { - const char *hit_twice = "ceshi3@mailhost.cn"; - const char *hit_suffix = "11111111111ceshi3@mailhost.cn"; - const char *hit_prefix = "ceshi3@mailhost.cn11111111111"; - const char *cont_sz_table_name = "CONTENT_SIZE"; - const char *mail_addr_table_name = "MAIL_ADDR"; - struct maat *maat_inst = MaatRsStringScan::_shared_maat_inst; - int thread_id = 0; - - int cont_sz_table_id = maat_get_table_id(maat_inst, cont_sz_table_name); - ASSERT_GT(cont_sz_table_id, 0); - - int mail_addr_table_id = maat_get_table_id(maat_inst, mail_addr_table_name); - ASSERT_GT(mail_addr_table_id, 0); - - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - int ret = maat_scan_integer(maat_inst, cont_sz_table_id, 2015, results, - ARRAY_SIZE, &n_hit_result, state); - - ret = maat_scan_string(maat_inst, mail_addr_table_id, hit_twice, strlen(hit_twice), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(n_hit_result, 2); - EXPECT_EQ(results[0], 151); - EXPECT_EQ(results[1], 152); - maat_state_reset(state); - - ret = maat_scan_string(maat_inst, mail_addr_table_id, hit_suffix, strlen(hit_suffix), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(n_hit_result, 1); - EXPECT_EQ(results[0], 151); - - ret = maat_scan_integer(maat_inst, cont_sz_table_id, 2015, results, - ARRAY_SIZE, &n_hit_result, state); - ret = maat_scan_string(maat_inst, mail_addr_table_id, hit_prefix, strlen(hit_prefix), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(n_hit_result, 1); - EXPECT_EQ(results[0], 152); - maat_state_free(state); - state = NULL; -} - -TEST_F(MaatRsStringScan, MaatUnescape) { - const char *scan_data = "Batman\\:Take me Home.Superman/:Fine,stay with me."; - const char *table_name = "KEYWORDS_TABLE"; - struct maat *maat_inst = MaatRsStringScan::_shared_maat_inst; - int thread_id = 0; - - int table_id = maat_get_table_id(maat_inst, table_name); - ASSERT_GT(table_id, 0); - - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - int ret = maat_scan_string(maat_inst, table_id, scan_data, strlen(scan_data), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(n_hit_result, 1); - EXPECT_EQ(results[0], 132); - maat_state_free(state); - state = NULL; -} - -TEST_F(MaatRsStringScan, OffsetChunk64) { - const char *table_name = "IMAGE_FP"; - const char *file_name = "./testdata/mesa_logo.jpg"; - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - int thread_id = 0; - struct maat *maat_inst = MaatRsStringScan::_shared_maat_inst; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - - FILE *fp = fopen(file_name, "r"); - ASSERT_FALSE(fp==NULL); - - char scan_data[64]; - int table_id = maat_get_table_id(maat_inst, table_name); - ASSERT_GT(table_id, 0); - - struct maat_stream *sp = maat_stream_new(maat_inst, table_id, state); - ASSERT_TRUE(sp != NULL); - - int ret = 0; - int read_size = 0; - int pass_flag = 0; - while (0 == feof(fp)) { - read_size = fread(scan_data, 1, sizeof(scan_data), fp); - ret = maat_stream_scan(sp, scan_data, read_size, - results, ARRAY_SIZE, &n_hit_result, state); - if (ret > 0) { - pass_flag = 1; - break; - } - } - EXPECT_EQ(pass_flag, 1); - EXPECT_EQ(results[0], 136); - maat_stream_free(sp); - fclose(fp); - maat_state_free(state); - state = NULL; -} - -TEST_F(MaatRsStringScan, OffsetChunk1460) { - const char *table_name = "IMAGE_FP"; - const char *file_name = "./testdata/mesa_logo.jpg"; - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - int thread_id = 0; - struct maat *maat_inst = MaatRsStringScan::_shared_maat_inst; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - - FILE *fp = fopen(file_name, "r"); - ASSERT_FALSE(fp==NULL); - - char scan_data[1460]; - int table_id = maat_get_table_id(maat_inst, table_name); - ASSERT_GT(table_id, 0); - - struct maat_stream *sp = maat_stream_new(maat_inst, table_id, state); - ASSERT_TRUE(sp != NULL); - - int ret = 0; - int read_size = 0; - int pass_flag = 0; - while (0 == feof(fp)) { - read_size = fread(scan_data, 1, sizeof(scan_data), fp); - ret = maat_stream_scan(sp, scan_data, read_size, - results, ARRAY_SIZE, &n_hit_result, state); - if (ret > 0) { - pass_flag = 1; - break; - } - } - EXPECT_EQ(pass_flag, 1); - EXPECT_EQ(results[0], 136); - maat_stream_free(sp); - fclose(fp); - maat_state_free(state); - state = NULL; -} - -TEST_F(MaatRsStringScan, StreamScanUTF8) { - const char *table_name = "TROJAN_PAYLOAD"; - const char* file_name = "./testdata/jd.com.html"; - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - int thread_id = 0; - char scan_data[1500]; - struct maat *maat_inst = MaatRsStringScan::_shared_maat_inst; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - - FILE *fp = fopen(file_name, "r"); - ASSERT_FALSE(fp == NULL); - - int table_id = maat_get_table_id(maat_inst, table_name); - ASSERT_GT(table_id, 0); - - struct maat_stream *sp = maat_stream_new(maat_inst, table_id, state); - ASSERT_FALSE(sp == NULL); - - int pass_flag = 0; - while (0 == feof(fp)) { - size_t read_size = fread(scan_data, 1, sizeof(scan_data), fp); - //read_size can't exceed 1500 - int ret = maat_stream_scan(sp, scan_data, read_size, results, ARRAY_SIZE, - &n_hit_result, state); - if (ret == MAAT_SCAN_HIT) { - pass_flag = 1; - break; - } - } - - EXPECT_EQ(pass_flag, 1); - EXPECT_EQ(results[0], 157); - maat_stream_free(sp); - fclose(fp); - maat_state_free(state); - state = NULL; -} - -TEST_F(MaatRsStringScan, StreamInput) { - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - int thread_id = 0; - struct maat *maat_inst = MaatRsStringScan::_shared_maat_inst; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - const char *scan_data = "http://www.cyberessays.com/search_results.php?action=search&query=yulingjing,abckkk,1234567"; - const char *table_name = "HTTP_URL"; - - int table_id = maat_get_table_id(maat_inst, table_name); - ASSERT_GT(table_id, 0); - - struct maat_stream *sp = maat_stream_new(maat_inst, table_id, state); - ASSERT_TRUE(sp != NULL); - - int ret = maat_stream_scan(sp, "www.cyberessays.com", strlen("www.cyberessays.com"), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_OK); - - ret = maat_stream_scan(sp, scan_data, strlen(scan_data), results, ARRAY_SIZE, - &n_hit_result, state); - maat_stream_free(sp); - - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(results[0], 125); - maat_state_free(state); - state = NULL; -} - -TEST_F(MaatRsStringScan, dynamic_config) { - const char *table_name = "HTTP_URL"; - char data[128] = "hello world, welcome to maat version4, it's funny."; - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - int thread_id = 0; - struct maat *maat_inst = MaatRsStringScan::_shared_maat_inst; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - - int table_id = maat_get_table_id(maat_inst, table_name); - int ret = maat_scan_string(maat_inst, table_id, data, strlen(data), results, - ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HALF_HIT); - EXPECT_EQ(n_hit_result, 0); - maat_state_reset(state); - - const char *compile_table_name = "COMPILE_DEFAULT"; - const char *g2c_table_name = "GROUP2COMPILE_DEFAULT"; - - /* compile table add line */ - long long compile_id = maat_cmd_incrby(maat_inst, "TEST_SEQ", 1); - ret = compile_table_set_line(maat_inst, compile_table_name, MAAT_OP_ADD, compile_id, "null", 1, 0); - EXPECT_EQ(ret, 1); - - /* group2compile table add line */ - long long group_id = maat_cmd_incrby(maat_inst, "SEQUENCE_GROUP", 1); - ret = group2compile_table_set_line(maat_inst, g2c_table_name, MAAT_OP_ADD, group_id, - compile_id, 0, "null", 1, 0); - EXPECT_EQ(ret, 1); - - /* expr table add line */ - long long item_id = maat_cmd_incrby(maat_inst, "SEQUENCE_REGION", 1); - const char *keywords = "welcome to maat"; - ret = expr_table_set_line(maat_inst, table_name, MAAT_OP_ADD, item_id, group_id, - keywords, NULL, 1, 0, 0, 0); /* EXPR_TYPE_AND MATCH_METHOD_SUB */ - EXPECT_EQ(ret, 1); - - sleep(WAIT_FOR_EFFECTIVE_S * 3); - - ret = maat_scan_string(maat_inst, table_id, data, strlen(data), results, - ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(n_hit_result, 1); - EXPECT_EQ(results[0], compile_id); - maat_state_reset(state); - - /* expr table del line */ - ret = expr_table_set_line(maat_inst, table_name, MAAT_OP_DEL, item_id, group_id, - keywords, NULL, 1, 0, 0, 0); /* EXPR_TYPE_AND MATCH_METHOD_SUB */ - EXPECT_EQ(ret, 1); - - /* group2compile table del line */ - ret = group2compile_table_set_line(maat_inst, g2c_table_name, MAAT_OP_DEL, group_id, - compile_id, 0, "null", 1, 0); - EXPECT_EQ(ret, 1); - - /* compile table del line */ - ret = compile_table_set_line(maat_inst, compile_table_name, MAAT_OP_DEL, compile_id, "null", 1, 0); - EXPECT_EQ(ret, 1); - - sleep(WAIT_FOR_EFFECTIVE_S); - - ret = maat_scan_string(maat_inst, table_id, data, strlen(data), results, - ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HALF_HIT); - EXPECT_EQ(n_hit_result, 0); - maat_state_free(state); - state = NULL; -} - -class MaatHsStreamScan : public testing::Test +class MaatStreamScan : public testing::Test { protected: static void SetUpTestCase() { @@ -2122,9 +1445,9 @@ protected: static struct maat *_shared_maat_inst; }; -struct maat *MaatHsStreamScan::_shared_maat_inst; +struct maat *MaatStreamScan::_shared_maat_inst; -TEST_F(MaatHsStreamScan, dynamic_config) { +TEST_F(MaatStreamScan, dynamic_config) { const char *scan_data1 = "hello world cyberessays.com/search_results.php?action=search&query=yulingjing,abckkk,1234567"; const char *table_name = "HTTP_URL"; const char *keywords1 = "hello"; @@ -2132,103 +1455,7 @@ TEST_F(MaatHsStreamScan, dynamic_config) { long long results[ARRAY_SIZE] = {0}; size_t n_hit_result = 0; int thread_id = 0; - struct maat *maat_inst = MaatHsStreamScan::_shared_maat_inst; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - - // STEP 1: add keywords1 and wait scan stream to hit - long long compile1_id = maat_cmd_incrby(maat_inst, "TEST_SEQ", 1); - int ret = test_add_expr_command(maat_inst, table_name, compile1_id, 0, keywords1); - EXPECT_EQ(ret, 1); - - sleep(WAIT_FOR_EFFECTIVE_S); - - int table_id = maat_get_table_id(maat_inst, table_name); - ASSERT_GT(table_id, 0); - - struct maat_stream *sp = maat_stream_new(maat_inst, table_id, state); - ASSERT_TRUE(sp != NULL); - - ret = maat_stream_scan(sp, "www.cyberessays.com", strlen("www.cyberessays.com"), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_OK); - - ret = maat_stream_scan(sp, scan_data1, strlen(scan_data1), results, ARRAY_SIZE, - &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(results[0], compile1_id); - maat_state_reset(state); - - // STEP 2: Inc config update, use same stream to scan and wait old expr_runtime invalid - random_keyword_generate(keyword_buf, sizeof(keyword_buf)); - long long compile_id = maat_cmd_incrby(maat_inst, "TEST_SEQ", 1); - ret = test_add_expr_command(maat_inst, table_name, compile_id, 0, keyword_buf); - EXPECT_EQ(ret, 1); - - // Inc config has not yet taken effect, stream scan can hit compile - ret = maat_stream_scan(sp, scan_data1, strlen(scan_data1), results, ARRAY_SIZE, - &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(results[0], compile1_id); - maat_state_reset(state); - - sleep(WAIT_FOR_EFFECTIVE_S); - - // Inc config has taken effect, stream reference old expr_runtime, should not hit compile - ret = maat_stream_scan(sp, scan_data1, strlen(scan_data1), results, ARRAY_SIZE, - &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_OK); - - maat_stream_free(sp); - maat_state_free(state); - sp = NULL; - state = NULL; -} - -class MaatRsStreamScan : public testing::Test -{ -protected: - static void SetUpTestCase() { - char redis_ip[64] = "127.0.0.1"; - int redis_port = 6379; - int redis_db = 0; - - struct maat_options *opts = maat_options_new(); - maat_options_set_redis(opts, redis_ip, redis_port, redis_db); - maat_options_set_logger(opts, "./maat_framework_gtest.log", LOG_LEVEL_INFO); - - _shared_maat_inst = maat_new(opts, table_info_path); - assert(_shared_maat_inst != NULL); - - maat_cmd_flushDB(_shared_maat_inst); - maat_free(_shared_maat_inst); - - maat_options_set_foreign_cont_dir(opts, "./foreign_files/"); - maat_options_set_rule_effect_interval_ms(opts, 0); - maat_options_set_gc_timeout_ms(opts, 0); // start GC immediately - maat_options_set_stat_file(opts, "./stat.log"); - maat_options_set_expr_engine(opts, MAAT_EXPR_ENGINE_RS); - _shared_maat_inst = maat_new(opts, table_info_path); - maat_options_free(opts); - } - - static void TearDownTestCase() { - maat_free(_shared_maat_inst); - } - - static struct maat *_shared_maat_inst; -}; - -struct maat *MaatRsStreamScan::_shared_maat_inst; - -TEST_F(MaatRsStreamScan, dynamic_config) { - const char *scan_data1 = "hello world cyberessays.com/search_results.php?action=search&query=yulingjing,abckkk,1234567"; - const char *table_name = "HTTP_URL"; - const char *keywords1 = "hello"; - char keyword_buf[128]; - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - int thread_id = 0; - struct maat *maat_inst = MaatRsStreamScan::_shared_maat_inst; + struct maat *maat_inst = MaatStreamScan::_shared_maat_inst; struct maat_state *state = maat_state_new(maat_inst, thread_id); // STEP 1: add keywords1 and wait scan stream to hit @@ -2598,8 +1825,8 @@ TEST_F(MaatIPScan, dynamic_config) { EXPECT_EQ(n_hit_result, 0); maat_state_reset(state); - const char *compile_table_name = "COMPILE_DEFAULT"; - const char *g2c_table_name = "GROUP2COMPILE_DEFAULT"; + const char *compile_table_name = "COMPILE"; + const char *g2c_table_name = "GROUP2COMPILE"; /* compile table add line */ long long compile_id = maat_cmd_incrby(maat_inst, "TEST_SEQ", 1); @@ -4148,7 +3375,7 @@ void compile_ex_param_dup(int table_id, void **to, void **from, long argl, void TEST_F(CompileTable, CompileRuleUpdate) { struct maat *maat_inst = CompileTable::_shared_maat_inst; - const char *compile_table_name = "COMPILE_DEFAULT"; + const char *compile_table_name = "COMPILE"; long long compile_id = maat_cmd_incrby(maat_inst, "TEST_SEQ", 1); int ret = compile_table_set_line(maat_inst, compile_table_name, MAAT_OP_ADD, compile_id, "null", 1, 0); @@ -4180,12 +3407,6 @@ TEST_F(CompileTable, Conjunction1) { EXPECT_EQ(results[0], 197); EXPECT_EQ(results[1], 141); - int table_ids[2] = {-1, -1}; - ret = maat_state_get_compile_table_ids(state, results, 2, table_ids); - EXPECT_EQ(ret, 2); - EXPECT_EQ(table_ids[0], 0); - EXPECT_EQ(table_ids[1], 1); - struct maat_hit_path hit_path[HIT_PATH_SIZE] = {0}; int n_read = maat_state_get_hit_paths(state, hit_path, HIT_PATH_SIZE); EXPECT_EQ(n_read, 2); @@ -4214,12 +3435,6 @@ TEST_F(CompileTable, Conjunction2) { EXPECT_EQ(results[0], 197); EXPECT_EQ(results[1], 141); - int table_ids[2] = {-1, -1}; - ret = maat_state_get_compile_table_ids(state, results, 2, table_ids); - EXPECT_EQ(ret, 2); - EXPECT_EQ(table_ids[0], 0); - EXPECT_EQ(table_ids[1], 1); - struct maat_hit_path hit_path[HIT_PATH_SIZE] = {0}; int n_read = maat_state_get_hit_paths(state, hit_path, HIT_PATH_SIZE); EXPECT_EQ(n_read, 2); @@ -4236,40 +3451,6 @@ TEST_F(CompileTable, Conjunction2) { state = NULL; } -TEST_F(CompileTable, GetHitCompileTableID) { - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - int thread_id = 0; - const char *compile_table_name = "COMPILE_FIREWALL_CONJUNCTION"; - const char *scan_data = "This is a firewall engine."; - const char *table_name = "HTTP_URL"; - struct maat *maat_inst = CompileTable::_shared_maat_inst; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - - int table_id = maat_get_table_id(maat_inst, table_name); - ASSERT_GT(table_id, 0); - - int compile_table_id = maat_get_table_id(maat_inst, compile_table_name); - ASSERT_GT(table_id, 0); - - int ret = maat_state_set_scan_compile_table(state, compile_table_id); - EXPECT_EQ(ret, 0); - - ret = maat_scan_string(maat_inst, table_id, scan_data, strlen(scan_data), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(n_hit_result, 1); - EXPECT_EQ(results[0], 198); - - int hit_compile_table_id = -1; - ret = maat_state_get_compile_table_ids(state, results, 1, &hit_compile_table_id); - EXPECT_EQ(ret, 1); - EXPECT_EQ(hit_compile_table_id, 4); - - maat_state_free(state); - state = NULL; -} - class Policy : public testing::Test { protected: @@ -4399,18 +3580,16 @@ TEST_F(Policy, CompileEXData) { int thread_id = 0; const char *url = "firewall should hit"; const char *table_name = "HTTP_URL"; - const char *plugin_table_name = "COMPILE_FIREWALL_PLUGIN"; - const char *compile_table_name = "COMPILE_FIREWALL_CONJUNCTION"; + const char *compile_table_name = "COMPILE_FIREWALL"; const char *expect_name = "I have a name"; struct maat *maat_inst = Policy::_shared_maat_inst; struct maat_state *state = maat_state_new(maat_inst, thread_id); int table_id = maat_get_table_id(maat_inst, table_name); - int plugin_table_id = maat_get_table_id(maat_inst, plugin_table_name); int compile_table_id = maat_get_table_id(maat_inst, compile_table_name); int ex_data_counter = 0; - int ret = maat_plugin_table_ex_schema_register(maat_inst, plugin_table_name, + int ret = maat_plugin_table_ex_schema_register(maat_inst, compile_table_name, compile_ex_param_new, compile_ex_param_free, compile_ex_param_dup, @@ -4427,7 +3606,7 @@ TEST_F(Policy, CompileEXData) { EXPECT_EQ(n_hit_result, 1); EXPECT_EQ(results[0], 198); - void *ex_data = maat_plugin_table_get_ex_data(maat_inst, plugin_table_id, + void *ex_data = maat_plugin_table_get_ex_data(maat_inst, compile_table_id, (char *)&results[0], sizeof(long long)); ASSERT_TRUE(ex_data!=NULL); struct rule_ex_param *param = (struct rule_ex_param *)ex_data; @@ -4969,8 +4148,8 @@ TEST_F(MaatCmdTest, SetIP) { size_t n_hit_result = 0; int thread_id = 0; const char *ip_table_name = "IP_CONFIG"; - const char *compile_table_name = "COMPILE_DEFAULT"; - const char *g2c_table_name = "GROUP2COMPILE_DEFAULT"; + const char *compile_table_name = "COMPILE"; + const char *g2c_table_name = "GROUP2COMPILE"; struct maat *maat_inst = MaatCmdTest::_shared_maat_inst; struct maat_state *state = maat_state_new(maat_inst, thread_id); @@ -5023,7 +4202,6 @@ TEST_F(MaatCmdTest, SetExpr) { const char *keywords1 = "Hiredis"; const char *keywords2 = "C Client"; - const char *compile_table_name = "COMPILE_DEFAULT"; char escape_buff1[256], escape_buff2[256]; char keywords[512]; @@ -5053,10 +4231,10 @@ TEST_F(MaatCmdTest, SetExpr) { EXPECT_TRUE(results[0] == compile_id || results[0] == (compile_id - 1)); maat_state_reset(state); - ret = compile_table_set_line(maat_inst, compile_table_name, MAAT_OP_DEL, compile_id-1, + ret = compile_table_set_line(maat_inst, "COMPILE", MAAT_OP_DEL, compile_id-1, "null", 1, 0); EXPECT_EQ(ret, 1); - ret = compile_table_set_line(maat_inst, compile_table_name, MAAT_OP_DEL, compile_id, + ret = compile_table_set_line(maat_inst, "COMPILE", MAAT_OP_DEL, compile_id, "null", 1, 0); EXPECT_EQ(ret, 1); sleep(WAIT_FOR_EFFECTIVE_S); @@ -5081,9 +4259,9 @@ TEST_F(MaatCmdTest, SetExpr8) { const char *scan_data8 = "string1, string2, string3, string4, string5, string6, string7, string8"; const char *scan_data7 = "string1, string2, string3, string4, string5, string6, string7"; - const char *compile_table_name = "COMPILE_DEFAULT"; + const char *compile_table_name = "COMPILE"; const char *table_name = "KEYWORDS_TABLE"; - const char *g2c_table_name = "GROUP2COMPILE_DEFAULT"; + const char *g2c_table_name = "GROUP2COMPILE"; const char *keywords8 = "string1&string2&string3&string4&string5&string6&string7&string8"; const char *keywords7 = "string1&string2&string3&string4&string5&string6&string7"; @@ -5150,8 +4328,6 @@ TEST_F(MaatCmdTest, SameFilterRefByOneCompile) { const char *vtable_name = "HTTP_URL_FILTER"; const char *scan_data = "http://filtermenot.com"; const char *keywords = "menot.com"; - const char *compile_table_name = "COMPILE_DEFAULT"; - const char *g2c_table_name = "GROUP2COMPILE_DEFAULT"; long long results[ARRAY_SIZE] = {0}; size_t n_hit_result = 0; int thread_id = 0; @@ -5162,17 +4338,17 @@ TEST_F(MaatCmdTest, SameFilterRefByOneCompile) { ASSERT_GT(table_id, 0); long long compile_id = maat_cmd_incrby(maat_inst, "TEST_SEQ", 1); - int ret = compile_table_set_line(maat_inst, compile_table_name, MAAT_OP_ADD, compile_id, + int ret = compile_table_set_line(maat_inst, "COMPILE", MAAT_OP_ADD, compile_id, "null", 2, 0); // compile has two clause EXPECT_EQ(ret, 1); //clause1 & clause2 has same filter => {vtable_id, group_id} long long group_id = maat_cmd_incrby(maat_inst, "SEQUENCE_GROUP", 1); - ret = group2compile_table_set_line(maat_inst, g2c_table_name, MAAT_OP_ADD, + ret = group2compile_table_set_line(maat_inst, "GROUP2COMPILE", MAAT_OP_ADD, group_id, compile_id, 0, vtable_name, 1, 0); EXPECT_EQ(ret, 1); - ret = group2compile_table_set_line(maat_inst, g2c_table_name, MAAT_OP_ADD, + ret = group2compile_table_set_line(maat_inst, "GROUP2COMPILE", MAAT_OP_ADD, group_id, compile_id, 0, vtable_name, 2, 0); EXPECT_EQ(ret, 1); @@ -5276,9 +4452,9 @@ TEST_F(MaatCmdTest, ReturnRuleIDWithDescendingOrder) { TEST_F(MaatCmdTest, SubGroup) { const char *table_name = "HTTP_URL"; - const char *compile_table_name = "COMPILE_DEFAULT"; - const char *g2c_table_name = "GROUP2COMPILE_DEFAULT"; + const char *g2c_table_name = "GROUP2COMPILE"; const char *g2g_table_name = "GROUP2GROUP"; + const char *compile_table_name = "COMPILE"; const char *scan_data1 = "www.v2ex.com/t/573028#程序员的核心竞争力是什么"; const char *keyword1 = "程序员&核心竞争力"; const char *scan_data2 = "https://ask.leju.com/bj/detail/12189672562229248/?bi=tg&type=sina-pc" @@ -5432,8 +4608,8 @@ TEST_F(MaatCmdTest, SubGroup) { TEST_F(MaatCmdTest, RefGroup) { const char *table_name = "HTTP_URL"; - const char* compile_table_name = "COMPILE_DEFAULT"; - const char* g2c_table_name = "GROUP2COMPILE_DEFAULT"; + const char* g2c_table_name = "GROUP2COMPILE"; + const char* compile_table_name = "COMPILE"; const char* scan_data1 = "m.facebook.com/help/2297503110373101?helpref=hc_nav&refid=69"; const char* keyword1 = "something-should-not-hit"; const char* keyword2 = "facebook.com/help/2297503110373101"; @@ -5510,8 +4686,8 @@ TEST_F(MaatCmdTest, RefGroup) { } TEST_F(MaatCmdTest, VirtualTable) { - const char* compile_table_name = "COMPILE_DEFAULT"; - const char* g2c_table_name = "GROUP2COMPILE_DEFAULT"; + const char* g2c_table_name = "GROUP2COMPILE"; + const char* compile_table_name = "COMPILE"; const char* table_name="HTTP_SIGNATURE"; int thread_id = 0; struct maat *maat_inst = MaatCmdTest::_shared_maat_inst; @@ -5892,12 +5068,11 @@ void plugin_ex_dup_cb(int table_id, void **to, void **from, long argl, void *arg } TEST_F(MaatCmdTest, CompileEXData) { - const char *plugin_table_name = "COMPILE_FIREWALL_PLUGIN"; - const char *compile_table_name = "COMPILE_FIREWALL_DEFAULT"; + const char *compile_table_name = "COMPILE_FIREWALL"; struct maat *maat_inst = MaatCmdTest::_shared_maat_inst; int *ex_data_counter = MaatCmdTest::_ex_data_counter; - int plugin_table_id = maat_get_table_id(maat_inst, plugin_table_name); - EXPECT_GT(plugin_table_id, 0); + int compile_table_id = maat_get_table_id(maat_inst, compile_table_name); + EXPECT_GT(compile_table_id, 0); long long compile1_id = maat_cmd_incrby(maat_inst, "TEST_SEQ", 1); int ret = compile_table_set_line(maat_inst, compile_table_name, MAAT_OP_ADD, @@ -5910,7 +5085,7 @@ TEST_F(MaatCmdTest, CompileEXData) { sleep(WAIT_FOR_EFFECTIVE_S); *ex_data_counter = 0; - ret = maat_plugin_table_ex_schema_register(maat_inst, plugin_table_name, + ret = maat_plugin_table_ex_schema_register(maat_inst, compile_table_name, compile_ex_param_new, compile_ex_param_free, compile_ex_param_dup, @@ -5918,13 +5093,13 @@ TEST_F(MaatCmdTest, CompileEXData) { ASSERT_TRUE(ret == 0); EXPECT_EQ(*ex_data_counter, 2); - void *ex_data = maat_plugin_table_get_ex_data(maat_inst, plugin_table_id, + void *ex_data = maat_plugin_table_get_ex_data(maat_inst, compile_table_id, (char *)&compile1_id, sizeof(long long)); ASSERT_TRUE(ex_data != NULL); struct rule_ex_param *param = (struct rule_ex_param *)ex_data; EXPECT_EQ(param->id, 1111); - ex_data = maat_plugin_table_get_ex_data(maat_inst, plugin_table_id, + ex_data = maat_plugin_table_get_ex_data(maat_inst, compile_table_id, (char *)&compile2_id, sizeof(long long)); ASSERT_TRUE(ex_data != NULL); param = (struct rule_ex_param *)ex_data; @@ -5932,10 +5107,10 @@ TEST_F(MaatCmdTest, CompileEXData) { ret = compile_table_set_line(maat_inst, compile_table_name, MAAT_OP_DEL, compile2_id, "test:compile2,2222", 1, 0); - sleep(WAIT_FOR_EFFECTIVE_S); + sleep(WAIT_FOR_EFFECTIVE_S * 5); EXPECT_EQ(param->id, 2222); sleep(2); - //excced gc_timeout_s(3s), the data pointed by param has been freed + //excced gc_timeout_s(11s), the data pointed by param has been freed } TEST_F(MaatCmdTest, PluginEXData) { @@ -6275,8 +5450,8 @@ TEST_F(MaatCmdTest, UpdateBoolPlugin) { #define COMPILE_ID_NUMS 1000 TEST_F(MaatCmdTest, GroupInMassCompiles) { - const char* compile_table_name = "COMPILE_DEFAULT"; - const char* g2c_table_name = "GROUP2COMPILE_DEFAULT"; + const char* g2c_table_name = "GROUP2COMPILE"; + const char* compile_table_name = "COMPILE"; const char* table_url = "HTTP_URL"; const char* table_appid = "APP_ID"; int thread_id = 0; @@ -6379,339 +5554,10 @@ TEST_F(MaatCmdTest, GroupInMassCompiles) { state = NULL; } -TEST_F(MaatCmdTest, HitGroup) { - const char *compile_table_name = "COMPILE_DEFAULT"; - const char *g2c_table_name = "GROUP2COMPILE_DEFAULT"; - const char *g2g_table_name = "GROUP2GROUP"; - const char *http_sig_table_name = "HTTP_SIGNATURE"; - const char *ip_table_name = "IP_CONFIG"; - const char *keywords_table_name = "KEYWORDS_TABLE"; - int thread_id = 0; - struct maat *maat_inst = MaatCmdTest::_shared_maat_inst; - struct maat_state *state = maat_state_new(maat_inst, thread_id); - - /* compile1 */ - long long compile1_id = maat_cmd_incrby(maat_inst, "TEST_SEQ", 1); - int ret = compile_table_set_line(maat_inst, compile_table_name, MAAT_OP_ADD, compile1_id, - "null", 2, 0); - EXPECT_EQ(ret, 1); - - //group1 -> compile1 - long long group1_id = maat_cmd_incrby(maat_inst, "SEQUENCE_GROUP", 1); - ret = group2compile_table_set_line(maat_inst, g2c_table_name, MAAT_OP_ADD, group1_id, - compile1_id, 0, "HTTP_REQUEST_HEADER", 1, 0); - EXPECT_EQ(ret, 1); - - //item1 -> group1 -> compile1 - long long item1_id = maat_cmd_incrby(maat_inst, "SEQUENCE_REGION", 1); - ret = expr_table_set_line(maat_inst, http_sig_table_name, MAAT_OP_ADD, item1_id, group1_id, - "hit group item first", "URL", 0, 0, 0, 0); /*EXPR_TYPE_STRING MATCH_METHOD_SUB*/ - EXPECT_EQ(ret, 1); - - /* item1 -> group1 -> compile1 - / - group21_/ - */ - long long group21_id = maat_cmd_incrby(maat_inst, "SEQUENCE_GROUP", 1); - ret = group2compile_table_set_line(maat_inst, g2c_table_name, MAAT_OP_ADD, group21_id, - compile1_id, 0, "HTTP_RESPONSE_HEADER", 2, 0); - EXPECT_EQ(ret, 1); - - /* item1 -> group1 -> compile1 - / - group2 -> group21 _/ - */ - long long group2_id = maat_cmd_incrby(maat_inst, "SEQUENCE_GROUP", 1); - ret = group2group_table_set_line(maat_inst, g2g_table_name, MAAT_OP_ADD, group2_id, - group21_id, 0, 0); - EXPECT_EQ(ret, 1); - - /* item1 -> group1 -> compile1 - / - item2 -> group2 -> group21 _/ - */ - long long item2_id = maat_cmd_incrby(maat_inst, "SEQUENCE_REGION", 1); - ret = expr_table_set_line(maat_inst, http_sig_table_name, MAAT_OP_ADD, item2_id, group2_id, - "hit group item second", "Cookie", 0, 0, 0, 0); /*EXPR_TYPE_STRING MATCH_METHOD_SUB*/ - EXPECT_EQ(ret, 1); - - /* - item1 -> group1 -> group11 - \ - \ -> compile1 - / - item2 -> group2 -> group21 _/ - */ - long long group11_id = maat_cmd_incrby(maat_inst, "SEQUENCE_GROUP", 1); - ret = group2group_table_set_line(maat_inst, g2g_table_name, MAAT_OP_ADD, group1_id, - group11_id, 0, 0); - EXPECT_EQ(ret, 1); - - //item3 -> group3, group3 is not referenced by any compile. - long long item3_id = maat_cmd_incrby(maat_inst, "SEQUENCE_REGION", 1); - long long group3_id = maat_cmd_incrby(maat_inst, "SEQUENCE_GROUP", 1); - ret = ip_table_set_line(maat_inst, ip_table_name, MAAT_OP_ADD, item3_id, group3_id, - IPv4, "220.181.38.150", "220.181.38.151", 0, 65535, 0); - EXPECT_EQ(ret, 1); - - char temp[1024]={0}; - //item4 -> group4, group4 is not referenced by any compile. - long long item4_id = maat_cmd_incrby(maat_inst, "SEQUENCE_REGION", 1); - long long group4_id = maat_cmd_incrby(maat_inst, "SEQUENCE_GROUP", 1); - ret = expr_table_set_line(maat_inst, keywords_table_name, MAAT_OP_ADD, item4_id, group4_id, - str_escape(temp, sizeof(temp), "hit group item forth"), - NULL, 0, 0, 0, 0); /*EXPR_TYPE_STRING MATCH_METHOD_SUB*/ - EXPECT_EQ(ret, 1); - - /* - item1 -> group1 -> group11 - / \ - item5 -> / \ -> compile1 - / - item2 -> group2 -> group21 _/ - */ - //item5 -> group1 which means group1 has multi items - long long item5_id = maat_cmd_incrby(maat_inst, "SEQUENCE_REGION", 1); - ret = expr_table_set_line(maat_inst, keywords_table_name, MAAT_OP_ADD, item5_id, group1_id, - str_escape(temp, sizeof(temp), "hit group item fifth"), - NULL, 0, 0, 0, 0); /*EXPR_TYPE_STRING MATCH_METHOD_SUB*/ - EXPECT_EQ(ret, 1); - - sleep(WAIT_FOR_EFFECTIVE_S * 2); - - const char* http_url = "en.wikipedia.org hit group item first"; - const char* http_resp_hdr_cookie = "laptop=thinkpad X1 extrem;hit group item second" - "main[XWJOKE]=hoho; Hm_lvt_bbac0322e6ee13093f98d5c4b5a10912=1578874808;"; - - int http_req_table_id = maat_get_table_id(maat_inst, "HTTP_REQUEST_HEADER"); - ASSERT_GT(http_req_table_id, 0); - - ret = maat_state_set_scan_district(state, http_req_table_id, "URL", strlen("URL")); - EXPECT_EQ(ret, 0); - - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - ret = maat_scan_string(maat_inst, http_req_table_id, http_url, strlen(http_url), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HALF_HIT); - - size_t scan_count = maat_state_get_scan_count(state); - EXPECT_EQ(scan_count, 1); - - struct maat_hit_group hit_groups[128]; - memset(hit_groups, 0, sizeof(hit_groups)); - int n_hit_group = maat_state_get_hit_groups(state, MAAT_LIST_TYPE_FULL, hit_groups, 128); - EXPECT_EQ(n_hit_group, 2); - EXPECT_EQ(hit_groups[0].item_id, item1_id); - EXPECT_EQ(hit_groups[0].group_id, group1_id); - EXPECT_EQ(hit_groups[0].vtable_id, http_req_table_id); - - EXPECT_EQ(hit_groups[1].item_id, item1_id); - EXPECT_EQ(hit_groups[1].group_id, group11_id); - EXPECT_EQ(hit_groups[1].vtable_id, http_req_table_id); - - memset(hit_groups, 0, sizeof(hit_groups)); - n_hit_group = maat_state_get_hit_groups(state, MAAT_LIST_TYPE_INC, hit_groups, 128); - EXPECT_EQ(n_hit_group, 2); - EXPECT_EQ(hit_groups[0].item_id, item1_id); - EXPECT_EQ(hit_groups[0].group_id, group1_id); - EXPECT_EQ(hit_groups[0].vtable_id, http_req_table_id); - - EXPECT_EQ(hit_groups[1].item_id, item1_id); - EXPECT_EQ(hit_groups[1].group_id, group11_id); - EXPECT_EQ(hit_groups[1].vtable_id, http_req_table_id); - - int http_res_table_id = maat_get_table_id(maat_inst, "HTTP_RESPONSE_HEADER"); - ASSERT_GT(http_res_table_id, 0); - - ret = maat_state_set_scan_district(state, http_res_table_id, "Cookie", strlen("Cookie")); - EXPECT_EQ(ret, 0); - - ret = maat_scan_string(maat_inst, http_res_table_id, http_resp_hdr_cookie, strlen(http_resp_hdr_cookie), - results, ARRAY_SIZE, &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HIT); - EXPECT_EQ(n_hit_result, 1); - EXPECT_EQ(results[0], compile1_id); - scan_count = maat_state_get_scan_count(state); - EXPECT_EQ(scan_count, 2); - - n_hit_group = maat_state_get_hit_groups(state, MAAT_LIST_TYPE_FULL, hit_groups, 128); - EXPECT_EQ(n_hit_group, 4); - EXPECT_EQ(hit_groups[0].item_id, item1_id); - EXPECT_EQ(hit_groups[0].group_id, group1_id); - EXPECT_EQ(hit_groups[0].vtable_id, http_req_table_id); - - EXPECT_EQ(hit_groups[1].item_id, item1_id); - EXPECT_EQ(hit_groups[1].group_id, group11_id); - EXPECT_EQ(hit_groups[1].vtable_id, http_req_table_id); - - EXPECT_EQ(hit_groups[2].item_id, item2_id); - EXPECT_EQ(hit_groups[2].group_id, group21_id); - EXPECT_EQ(hit_groups[2].vtable_id, http_res_table_id); - - EXPECT_EQ(hit_groups[3].item_id, item2_id); - EXPECT_EQ(hit_groups[3].group_id, group2_id); - EXPECT_EQ(hit_groups[3].vtable_id, http_res_table_id); - - memset(hit_groups, 0, sizeof(hit_groups)); - n_hit_group = maat_state_get_hit_groups(state, MAAT_LIST_TYPE_INC, hit_groups, 128); - EXPECT_EQ(n_hit_group, 2); - EXPECT_EQ(hit_groups[0].item_id, item2_id); - EXPECT_EQ(hit_groups[0].group_id, group21_id); - EXPECT_EQ(hit_groups[0].vtable_id, http_res_table_id); - - EXPECT_EQ(hit_groups[1].item_id, item2_id); - EXPECT_EQ(hit_groups[1].group_id, group2_id); - EXPECT_EQ(hit_groups[1].vtable_id, http_res_table_id); - - - const char* keywords1="In graph theory, hit group item forth"; - const char *keywords2="To test one group hit group item fifth"; - - int keywords_table_id = maat_get_table_id(maat_inst, keywords_table_name); - ASSERT_GT(keywords_table_id, 0); - - struct maat_stream *stream = maat_stream_new(maat_inst, keywords_table_id, state); - ret = maat_stream_scan(stream, keywords1, strlen(keywords1), results, ARRAY_SIZE, - &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HALF_HIT); - scan_count = maat_state_get_scan_count(state); - EXPECT_EQ(scan_count, 3); - - n_hit_group = maat_state_get_hit_groups(state, MAAT_LIST_TYPE_FULL, hit_groups, 128); - EXPECT_EQ(n_hit_group, 5); - EXPECT_EQ(hit_groups[0].item_id, item1_id); - EXPECT_EQ(hit_groups[0].group_id, group1_id); - EXPECT_EQ(hit_groups[0].vtable_id, http_req_table_id); - - EXPECT_EQ(hit_groups[1].item_id, item1_id); - EXPECT_EQ(hit_groups[1].group_id, group11_id); - EXPECT_EQ(hit_groups[1].vtable_id, http_req_table_id); - - EXPECT_EQ(hit_groups[2].item_id, item2_id); - EXPECT_EQ(hit_groups[2].group_id, group21_id); - EXPECT_EQ(hit_groups[2].vtable_id, http_res_table_id); - - EXPECT_EQ(hit_groups[3].item_id, item2_id); - EXPECT_EQ(hit_groups[3].group_id, group2_id); - EXPECT_EQ(hit_groups[3].vtable_id, http_res_table_id); - - EXPECT_EQ(hit_groups[4].item_id, item4_id); - EXPECT_EQ(hit_groups[4].group_id, group4_id); - EXPECT_EQ(hit_groups[4].vtable_id, 0); //physical table(keywords_table) vtable_id is 0 - - int ip_table_id = maat_get_table_id(maat_inst, ip_table_name); - ASSERT_GT(ip_table_id, 0); - - uint32_t ip_addr; - inet_pton(AF_INET, "220.181.38.150", &ip_addr); - uint16_t port = htons(17272); - ret = maat_scan_ipv4(maat_inst, ip_table_id, ip_addr, port, 6, results, ARRAY_SIZE, - &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HALF_HIT); - scan_count = maat_state_get_scan_count(state); - EXPECT_EQ(scan_count, 4); - - n_hit_group = maat_state_get_hit_groups(state, MAAT_LIST_TYPE_FULL, hit_groups, 128); - EXPECT_EQ(n_hit_group, 6); - EXPECT_EQ(hit_groups[0].item_id, item1_id); - EXPECT_EQ(hit_groups[0].group_id, group1_id); - EXPECT_EQ(hit_groups[0].vtable_id, http_req_table_id); - - EXPECT_EQ(hit_groups[1].item_id, item1_id); - EXPECT_EQ(hit_groups[1].group_id, group11_id); - EXPECT_EQ(hit_groups[1].vtable_id, http_req_table_id); - - EXPECT_EQ(hit_groups[2].item_id, item2_id); - EXPECT_EQ(hit_groups[2].group_id, group21_id); - EXPECT_EQ(hit_groups[2].vtable_id, http_res_table_id); - - EXPECT_EQ(hit_groups[3].item_id, item2_id); - EXPECT_EQ(hit_groups[3].group_id, group2_id); - EXPECT_EQ(hit_groups[3].vtable_id, http_res_table_id); - - EXPECT_EQ(hit_groups[4].item_id, item3_id); - EXPECT_EQ(hit_groups[4].group_id, group3_id); - EXPECT_EQ(hit_groups[4].vtable_id, 0); //physical table(ip_table) vtable_id is 0 - - EXPECT_EQ(hit_groups[5].item_id, item4_id); - EXPECT_EQ(hit_groups[5].group_id, group4_id); - EXPECT_EQ(hit_groups[5].vtable_id, 0); //physical table(keywords_table) vtable_id is 0 - - memset(hit_groups, 0, sizeof(hit_groups)); - n_hit_group = maat_state_get_hit_groups(state, MAAT_LIST_TYPE_INC, hit_groups, 128); - EXPECT_EQ(n_hit_group, 2); - EXPECT_EQ(hit_groups[0].item_id, item3_id); - EXPECT_EQ(hit_groups[0].group_id, group3_id); - EXPECT_EQ(hit_groups[0].vtable_id, 0); //physical table(keywords_table) vtable_id is 0 - - EXPECT_EQ(hit_groups[1].item_id, item4_id); - EXPECT_EQ(hit_groups[1].group_id, group4_id); - EXPECT_EQ(hit_groups[1].vtable_id, 0); - - ret = maat_stream_scan(stream, keywords2, strlen(keywords2), results, ARRAY_SIZE, - &n_hit_result, state); - EXPECT_EQ(ret, MAAT_SCAN_HALF_HIT); - - scan_count = maat_state_get_scan_count(state); - EXPECT_EQ(scan_count, 5); - - memset(hit_groups, 0, sizeof(hit_groups)); - n_hit_group = maat_state_get_hit_groups(state, MAAT_LIST_TYPE_FULL, hit_groups, 128); - EXPECT_EQ(n_hit_group, 8); - EXPECT_EQ(hit_groups[0].item_id, item1_id); - EXPECT_EQ(hit_groups[0].group_id, group1_id); - EXPECT_EQ(hit_groups[0].vtable_id, http_req_table_id); - - EXPECT_EQ(hit_groups[1].item_id, item1_id); - EXPECT_EQ(hit_groups[1].group_id, group11_id); - EXPECT_EQ(hit_groups[1].vtable_id, http_req_table_id); - - - EXPECT_EQ(hit_groups[2].item_id, item2_id); - EXPECT_EQ(hit_groups[2].group_id, group21_id); - EXPECT_EQ(hit_groups[2].vtable_id, http_res_table_id); - - EXPECT_EQ(hit_groups[3].item_id, item2_id); - EXPECT_EQ(hit_groups[3].group_id, group2_id); - EXPECT_EQ(hit_groups[3].vtable_id, http_res_table_id); - - EXPECT_EQ(hit_groups[4].item_id, item3_id); - EXPECT_EQ(hit_groups[4].group_id, group3_id); - EXPECT_EQ(hit_groups[4].vtable_id, 0); //physical table(ip_table) vtable_id is 0 - - EXPECT_EQ(hit_groups[5].item_id, item4_id); - EXPECT_EQ(hit_groups[5].group_id, group4_id); - EXPECT_EQ(hit_groups[5].vtable_id, 0); //physical table(keywords_table) vtable_id is 0 - - EXPECT_EQ(hit_groups[6].item_id, item5_id); - EXPECT_EQ(hit_groups[6].group_id, group1_id); - EXPECT_EQ(hit_groups[6].vtable_id, 0); - - EXPECT_EQ(hit_groups[7].item_id, item5_id); - EXPECT_EQ(hit_groups[7].group_id, group11_id); - EXPECT_EQ(hit_groups[7].vtable_id, 0); - - memset(hit_groups, 0, sizeof(hit_groups)); - n_hit_group = maat_state_get_hit_groups(state, MAAT_LIST_TYPE_INC, hit_groups, 128); - EXPECT_EQ(n_hit_group, 2); - EXPECT_EQ(hit_groups[0].item_id, item5_id); - EXPECT_EQ(hit_groups[0].group_id, group1_id); - EXPECT_EQ(hit_groups[0].vtable_id, 0); //physical table(keywords_table) vtable_id is 0 - - EXPECT_EQ(hit_groups[1].item_id, item5_id); - EXPECT_EQ(hit_groups[1].group_id, group11_id); - EXPECT_EQ(hit_groups[1].vtable_id, 0); //physical table(keywords_table) vtable_id is 0 - - maat_stream_free(stream); - maat_state_free(state); - state = NULL; -} - TEST_F(MaatCmdTest, HitPath) { - const char *compile_table_name = "COMPILE_DEFAULT"; - const char *g2c_table_name = "GROUP2COMPILE_DEFAULT"; - const char *g2g_table_name = "GROUP2GROUP"; + const char *g2g_table_name = "GROUP2GROUP"; + const char *g2c_table_name = "GROUP2COMPILE"; + const char *compile_table_name = "COMPILE"; const char *http_sig_table_name = "HTTP_SIGNATURE"; const char *ip_table_name = "IP_CONFIG"; const char *keywords_table_name = "KEYWORDS_TABLE"; @@ -6792,7 +5638,7 @@ TEST_F(MaatCmdTest, HitPath) { NULL, 0, 0, 0, 0); /*EXPR_TYPE_STRING MATCH_METHOD_SUB*/ EXPECT_EQ(ret, 1); - sleep(WAIT_FOR_EFFECTIVE_S * 2); + sleep(WAIT_FOR_EFFECTIVE_S); const char* http_url = "en.wikipedia.org/wiki/Path_(graph_theory)"; const char* http_resp_hdr_cookie = "laptop=thinkpad X1 extrem;time=2020-02-11T15:34:00;" @@ -6816,6 +5662,23 @@ TEST_F(MaatCmdTest, HitPath) { size_t scan_count = maat_state_get_scan_count(state); EXPECT_EQ(scan_count, 1); + struct maat_hit_group hit_groups[128]; + memset(hit_groups, 0, sizeof(hit_groups)); + int n_hit_group = maat_state_get_hit_groups(state, MAAT_LIST_TYPE_FULL, hit_groups, sizeof(hit_groups)); + EXPECT_EQ(n_hit_group, 2); + EXPECT_EQ(hit_groups[0].group_id, group1_id); + EXPECT_EQ(hit_groups[0].vtable_id, http_req_table_id); + EXPECT_EQ(hit_groups[1].group_id, group11_id); + EXPECT_EQ(hit_groups[1].vtable_id, http_req_table_id); + + memset(hit_groups, 0, sizeof(hit_groups)); + n_hit_group = maat_state_get_hit_groups(state, MAAT_LIST_TYPE_INC, hit_groups, sizeof(hit_groups)); + EXPECT_EQ(n_hit_group, 2); + EXPECT_EQ(hit_groups[0].group_id, group1_id); + EXPECT_EQ(hit_groups[0].vtable_id, http_req_table_id); + EXPECT_EQ(hit_groups[1].group_id, group11_id); + EXPECT_EQ(hit_groups[1].vtable_id, http_req_table_id); + struct maat_hit_path hit_path[128]; memset(hit_path, 0, sizeof(hit_path)); int n_read = maat_state_get_hit_paths(state, hit_path, sizeof(hit_path)); @@ -6852,6 +5715,25 @@ TEST_F(MaatCmdTest, HitPath) { scan_count = maat_state_get_scan_count(state); EXPECT_EQ(scan_count, 2); + n_hit_group = maat_state_get_hit_groups(state, MAAT_LIST_TYPE_FULL, hit_groups, sizeof(hit_groups)); + EXPECT_EQ(n_hit_group, 4); + EXPECT_EQ(hit_groups[0].group_id, group1_id); + EXPECT_EQ(hit_groups[0].vtable_id, http_req_table_id); + EXPECT_EQ(hit_groups[1].group_id, group21_id); + EXPECT_EQ(hit_groups[1].vtable_id, http_res_table_id); + EXPECT_EQ(hit_groups[2].group_id, group2_id); + EXPECT_EQ(hit_groups[2].vtable_id, http_res_table_id); + EXPECT_EQ(hit_groups[3].group_id, group11_id); + EXPECT_EQ(hit_groups[3].vtable_id, http_req_table_id); + + memset(hit_groups, 0, sizeof(hit_groups)); + n_hit_group = maat_state_get_hit_groups(state, MAAT_LIST_TYPE_INC, hit_groups, sizeof(hit_groups)); + EXPECT_EQ(n_hit_group, 2); + EXPECT_EQ(hit_groups[0].group_id, group21_id); + EXPECT_EQ(hit_groups[0].vtable_id, http_res_table_id); + EXPECT_EQ(hit_groups[1].group_id, group2_id); + EXPECT_EQ(hit_groups[1].vtable_id, http_res_table_id); + n_read = maat_state_get_hit_paths(state, hit_path, sizeof(hit_path)); EXPECT_EQ(n_read, 4); @@ -6905,6 +5787,25 @@ that the edges be all directed in the same direction."; scan_count = maat_state_get_scan_count(state); EXPECT_EQ(scan_count, 3); + n_hit_group = maat_state_get_hit_groups(state, MAAT_LIST_TYPE_FULL, hit_groups, sizeof(hit_groups)); + EXPECT_EQ(n_hit_group, 5); + EXPECT_EQ(hit_groups[0].group_id, group1_id); + EXPECT_EQ(hit_groups[0].vtable_id, http_req_table_id); + EXPECT_EQ(hit_groups[1].group_id, group21_id); + EXPECT_EQ(hit_groups[1].vtable_id, http_res_table_id); + EXPECT_EQ(hit_groups[2].group_id, group2_id); + EXPECT_EQ(hit_groups[2].vtable_id, http_res_table_id); + EXPECT_EQ(hit_groups[3].group_id, group11_id); + EXPECT_EQ(hit_groups[3].vtable_id, http_req_table_id); + EXPECT_EQ(hit_groups[4].group_id, group4_id); + EXPECT_EQ(hit_groups[4].vtable_id, 0); //physical table(keywords_table) vtable_id is 0 + + memset(hit_groups, 0, sizeof(hit_groups)); + n_hit_group = maat_state_get_hit_groups(state, MAAT_LIST_TYPE_INC, hit_groups, sizeof(hit_groups)); + EXPECT_EQ(n_hit_group, 1); + EXPECT_EQ(hit_groups[0].group_id, group4_id); + EXPECT_EQ(hit_groups[0].vtable_id, 0); //physical table(keywords_table) vtable_id is 0 + n_read = maat_state_get_hit_paths(state, hit_path, sizeof(hit_path)); EXPECT_EQ(n_read, 5); @@ -6930,6 +5831,27 @@ that the edges be all directed in the same direction."; scan_count = maat_state_get_scan_count(state); EXPECT_EQ(scan_count, 4); + n_hit_group = maat_state_get_hit_groups(state, MAAT_LIST_TYPE_FULL, hit_groups, sizeof(hit_groups)); + EXPECT_EQ(n_hit_group, 6); + EXPECT_EQ(hit_groups[0].group_id, group1_id); + EXPECT_EQ(hit_groups[0].vtable_id, http_req_table_id); + EXPECT_EQ(hit_groups[1].group_id, group21_id); + EXPECT_EQ(hit_groups[1].vtable_id, http_res_table_id); + EXPECT_EQ(hit_groups[2].group_id, group2_id); + EXPECT_EQ(hit_groups[2].vtable_id, http_res_table_id); + EXPECT_EQ(hit_groups[3].group_id, group11_id); + EXPECT_EQ(hit_groups[3].vtable_id, http_req_table_id); + EXPECT_EQ(hit_groups[4].group_id, group3_id); + EXPECT_EQ(hit_groups[4].vtable_id, 0); //physical table(ip_table) vtable_id is 0 + EXPECT_EQ(hit_groups[5].group_id, group4_id); + EXPECT_EQ(hit_groups[5].vtable_id, 0); //physical table(keywords_table) vtable_id is 0 + + memset(hit_groups, 0, sizeof(hit_groups)); + n_hit_group = maat_state_get_hit_groups(state, MAAT_LIST_TYPE_INC, hit_groups, sizeof(hit_groups)); + EXPECT_EQ(n_hit_group, 1); + EXPECT_EQ(hit_groups[0].group_id, group3_id); + EXPECT_EQ(hit_groups[0].vtable_id, 0); //physical table(keywords_table) vtable_id is 0 + n_read = maat_state_get_hit_paths(state, hit_path, sizeof(hit_path)); EXPECT_EQ(n_read, 6); @@ -6968,9 +5890,9 @@ that the edges be all directed in the same direction."; TEST_F(MaatCmdTest, SameSuperGroupRefByMultiCompile) { char temp[1024]={0}; int thread_id = 0; - const char *compile_table_name = "COMPILE_DEFAULT"; - const char *g2c_table_name = "GROUP2COMPILE_DEFAULT"; - const char *g2g_table_name = "GROUP2GROUP"; + const char *g2g_table_name = "GROUP2GROUP"; + const char *g2c_table_name = "GROUP2COMPILE"; + const char *compile_table_name = "COMPILE"; const char *http_sig_table_name = "HTTP_SIGNATURE"; struct maat *maat_inst = MaatCmdTest::_shared_maat_inst; @@ -7059,8 +5981,8 @@ TEST_F(MaatCmdTest, SameSuperGroupRefByMultiCompile) { } TEST_F(MaatCmdTest, SameScanStatusWhenClauseUpdate_TSG6419) { - const char* compile_table_name = "COMPILE_DEFAULT"; - const char *g2c_table_name = "GROUP2COMPILE_DEFAULT"; + const char *g2c_table_name = "GROUP2COMPILE"; + const char* compile_table_name = "COMPILE"; const char* ip_table_name = "IP_PLUS_CONFIG"; const char *app_id_table_name = "APP_ID"; int thread_id = 0; @@ -7153,8 +6075,8 @@ TEST_F(MaatCmdTest, SameScanStatusWhenClauseUpdate_TSG6419) { } TEST_F(MaatCmdTest, GroupEdit) { - const char *compile_table_name = "COMPILE_DEFAULT"; - const char *g2c_table_name = "GROUP2COMPILE_DEFAULT"; + const char *g2c_table_name = "GROUP2COMPILE"; + const char *compile_table_name = "COMPILE"; const char *ip_table_name = "IP_PLUS_CONFIG"; const char *app_id_table_name = "APP_ID"; int thread_id = 0; @@ -7272,8 +6194,8 @@ TEST_F(MaatCmdTest, GroupEdit) { } TEST_F(MaatCmdTest, CompileDelete_TSG6548) { - const char* compile_table_name = "COMPILE_DEFAULT"; - const char* g2c_table_name = "GROUP2COMPILE_DEFAULT"; + const char* g2c_table_name = "GROUP2COMPILE"; + const char* compile_table_name = "COMPILE"; const char* ip_table_name = "IP_PLUS_CONFIG"; int thread_id = 0; struct maat *maat_inst = MaatCmdTest::_shared_maat_inst; @@ -7295,7 +6217,7 @@ TEST_F(MaatCmdTest, CompileDelete_TSG6548) { IPv4, "192.168.73.163", "192.168.73.180", 0, 65535, 0); EXPECT_EQ(ret, 1); - sleep(WAIT_FOR_EFFECTIVE_S * 2); + sleep(WAIT_FOR_EFFECTIVE_S); uint32_t ip_addr; inet_pton(AF_INET, "192.168.73.169", &ip_addr); @@ -7343,8 +6265,8 @@ TEST_F(MaatCmdTest, CompileDelete_TSG6548) { } TEST_F(MaatCmdTest, UpdateDeadLockDetection) { - const char* compile_table_name = "COMPILE_DEFAULT"; - const char* g2c_table_name = "GROUP2COMPILE_DEFAULT"; + const char* g2c_table_name = "GROUP2COMPILE"; + const char* compile_table_name = "COMPILE"; const char* table_http_url = "HTTP_URL"; int thread_id = 0; struct maat *maat_inst = MaatCmdTest::_shared_maat_inst; @@ -7418,8 +6340,8 @@ TEST_F(MaatCmdTest, UpdateDeadLockDetection) { } TEST_F(MaatCmdTest, StreamScanWhenExprTableIncUpdate) { - const char* compile_table_name = "COMPILE_DEFAULT"; - const char* g2c_table_name = "GROUP2COMPILE_DEFAULT"; + const char* g2c_table_name = "GROUP2COMPILE"; + const char* compile_table_name = "COMPILE"; const char* scan_table_name = "KEYWORDS_TABLE"; int thread_id = 0; struct maat *maat_inst = MaatCmdTest::_shared_maat_inst; @@ -7480,8 +6402,8 @@ TEST_F(MaatCmdTest, StreamScanWhenExprTableIncUpdate) { } TEST_F(MaatCmdTest, StreamScanSegfaultWhenVersionRollBack_TSG6324) { - const char* compile_table_name = "COMPILE_DEFAULT"; - const char* g2c_table_name = "GROUP2COMPILE_DEFAULT"; + const char* g2c_table_name = "GROUP2COMPILE"; + const char* compile_table_name = "COMPILE"; const char* scan_table_name = "KEYWORDS_TABLE"; int thread_id = 0; struct maat *maat_inst = MaatCmdTest::_shared_maat_inst; @@ -7523,7 +6445,7 @@ TEST_F(MaatCmdTest, StreamScanSegfaultWhenVersionRollBack_TSG6324) { //DON'T DO THIS!!! //Roll back version, trigger full update. //This operation generates FATAL logs in test_maat_redis.log.yyyy-mm-dd. - //For example: Add group 22 vt_id 0 to clause 2 of compile 979 failed, group is already existed + //For example: Add group 22 vt_id 0 to clause 2 of compile 979 failed, group is already exisited maat_cmd_incrby(maat_inst, "MAAT_VERSION", -100); //Wating for scanner garbage collect expiration. @@ -7539,8 +6461,8 @@ TEST_F(MaatCmdTest, StreamScanSegfaultWhenVersionRollBack_TSG6324) { } TEST_F(MaatCmdTest, IPAndStreamScanWhenIncUpdate) { - const char *compile_table_name = "COMPILE_DEFAULT"; - const char *g2c_table_name = "GROUP2COMPILE_DEFAULT"; + const char *g2c_table_name = "GROUP2COMPILE"; + const char *compile_table_name = "COMPILE"; const char *expr_table_name = "KEYWORDS_TABLE"; const char *ip_table_name = "IP_PLUS_CONFIG"; int thread_id = 0; @@ -7631,8 +6553,8 @@ TEST_F(MaatCmdTest, IPAndStreamScanWhenIncUpdate) { } TEST_F(MaatCmdTest, IPAndStreamScanWhenFullUpdate) { - const char *compile_table_name = "COMPILE_DEFAULT"; - const char *g2c_table_name = "GROUP2COMPILE_DEFAULT"; + const char *g2c_table_name = "GROUP2COMPILE"; + const char *compile_table_name = "COMPILE"; const char *ip_table_name = "IP_PLUS_CONFIG"; const char *expr_table_name = "KEYWORDS_TABLE"; int thread_id = 0; @@ -7721,8 +6643,8 @@ TEST_F(MaatCmdTest, IPAndStreamScanWhenFullUpdate) { } TEST_F(MaatCmdTest, IPAndStringScanWhenIncUpdate) { - const char *compile_table_name = "COMPILE_DEFAULT"; - const char *g2c_table_name = "GROUP2COMPILE_DEFAULT"; + const char *g2c_table_name = "GROUP2COMPILE"; + const char *compile_table_name = "COMPILE"; const char *expr_table_name = "HTTP_URL"; const char *ip_table_name = "IP_PLUS_CONFIG"; const char *keywords = "IP&stringinc"; @@ -7812,8 +6734,8 @@ TEST_F(MaatCmdTest, IPAndStringScanWhenIncUpdate) { } TEST_F(MaatCmdTest, IPAndStringScanWhenFullupdate) { - const char *compile_table_name = "COMPILE_DEFAULT"; - const char *g2c_table_name = "GROUP2COMPILE_DEFAULT"; + const char *g2c_table_name = "GROUP2COMPILE"; + const char *compile_table_name = "COMPILE"; const char *ip_table_name = "IP_PLUS_CONFIG"; const char *expr_table_name = "HTTP_URL"; const char *keywords = "IP&string"; @@ -8136,4 +7058,4 @@ int main(int argc, char ** argv) ret=RUN_ALL_TESTS(); return ret; -} +} \ No newline at end of file diff --git a/test/maat_framework_perf_gtest.cpp b/test/maat_framework_perf_gtest.cpp index 7cc237e..4d7204a 100644 --- a/test/maat_framework_perf_gtest.cpp +++ b/test/maat_framework_perf_gtest.cpp @@ -55,6 +55,7 @@ int make_serial_rule(const char *table_name, const char *line, void *u_para) char *buff = ALLOC(char, strlen(line) + 1); memcpy(buff, line, strlen(line) + 1); + while (buff[strlen(buff) - 1] == '\n' || buff[strlen(buff) - 1] == '\t') { buff[strlen(buff) - 1] = '\0'; } @@ -256,79 +257,33 @@ static int ip_table_set_line(struct maat *maat_inst, const char *table_name, enu return maat_cmd_set_line(maat_inst, &line_rule); } -static int integer_table_set_line(struct maat *maat_inst, const char *table_name, - enum maat_operation op, long long item_id, - long long group_id, int low_bound, int up_bound, - int expire_after) -{ - char table_line[1024] = {0}; - int table_id = maat_get_table_id(maat_inst, table_name); - if (table_id < 0) { - return 0; - } - - sprintf(table_line, "%lld\t%lld\t%d\t%d\t%d", - item_id, group_id, low_bound, up_bound, op); - struct maat_cmd_line line_rule; - - line_rule.rule_id = item_id; - line_rule.table_line = table_line; - line_rule.table_name = table_name; - line_rule.expire_after = expire_after; - - return maat_cmd_set_line(maat_inst, &line_rule); -} - -static int flag_table_set_line(struct maat *maat_inst, const char *table_name, - enum maat_operation op, long long item_id, - long long group_id, long long flag, - long long flag_mask, int expire_after) -{ - char table_line[1024] = {0}; - int table_id = maat_get_table_id(maat_inst, table_name); - if (table_id < 0) { - return 0; - } - - sprintf(table_line, "%lld\t%lld\t%lld\t%lld\t%d", - item_id, group_id, flag, flag_mask, op); - struct maat_cmd_line line_rule; - - line_rule.rule_id = item_id; - line_rule.table_line = table_line; - line_rule.table_name = table_name; - line_rule.expire_after = expire_after; - - return maat_cmd_set_line(maat_inst, &line_rule); -} - -static void test_add_expr_command(struct maat *maat_inst, const char *table_name, - const char *keywords) +void test_add_expr_command(struct maat *maat_inst, const char *table_name, + const char *keywords) { long long compile_id = maat_cmd_incrby(maat_inst, "TEST_SEQ", 1); - int ret = compile_table_set_line(maat_inst, "COMPILE_DEFAULT", MAAT_OP_ADD, compile_id, "null", 1, 0); + int ret = compile_table_set_line(maat_inst, "COMPILE", MAAT_OP_ADD, compile_id, "null", 1, 0); EXPECT_EQ(ret, 1); long long group_id = maat_cmd_incrby(maat_inst, "SEQUENCE_GROUP", 1); - ret = group2compile_table_set_line(maat_inst, "GROUP2COMPILE_DEFAULT", MAAT_OP_ADD, group_id, + ret = group2compile_table_set_line(maat_inst, "GROUP2COMPILE", MAAT_OP_ADD, group_id, compile_id, 0, "null", 1, 0); EXPECT_EQ(ret, 1); long long item_id = maat_cmd_incrby(maat_inst, "SEQUENCE_REGION", 1); ret = expr_table_set_line(maat_inst, table_name, MAAT_OP_ADD, item_id, group_id, - keywords, "null", 1, 0, 0, 0); + keywords, NULL, 1, 0, 0, 0); EXPECT_EQ(ret, 1); } -static void test_add_ip_command(struct maat *maat_inst, const char *table_name, - const char *ip, uint16_t port) +void test_add_ip_command(struct maat *maat_inst, const char *table_name, + const char *ip, uint16_t port) { long long compile_id = maat_cmd_incrby(maat_inst, "TEST_SEQ", 1); - int ret = compile_table_set_line(maat_inst, "COMPILE_DEFAULT", MAAT_OP_ADD, compile_id, "null", 1, 0); + int ret = compile_table_set_line(maat_inst, "COMPILE", MAAT_OP_ADD, compile_id, "null", 1, 0); EXPECT_EQ(ret, 1); long long group_id = maat_cmd_incrby(maat_inst, "SEQUENCE_GROUP", 1); - ret = group2compile_table_set_line(maat_inst, "GROUP2COMPILE_DEFAULT", MAAT_OP_ADD, group_id, + ret = group2compile_table_set_line(maat_inst, "GROUP2COMPILE", MAAT_OP_ADD, group_id, compile_id, 0, "null", 1, 0); EXPECT_EQ(ret, 1); @@ -338,42 +293,6 @@ static void test_add_ip_command(struct maat *maat_inst, const char *table_name, EXPECT_EQ(ret, 1); } -static void test_add_integer_command(struct maat *maat_inst, const char *table_name, - int low_bound, int up_bound) -{ - long long compile_id = maat_cmd_incrby(maat_inst, "TEST_SEQ", 1); - int ret = compile_table_set_line(maat_inst, "COMPILE_DEFAULT", MAAT_OP_ADD, compile_id, "null", 1, 0); - EXPECT_EQ(ret, 1); - - long long group_id = maat_cmd_incrby(maat_inst, "SEQUENCE_GROUP", 1); - ret = group2compile_table_set_line(maat_inst, "GROUP2COMPILE_DEFAULT", MAAT_OP_ADD, group_id, - compile_id, 0, "null", 1, 0); - EXPECT_EQ(ret, 1); - - long long item_id = maat_cmd_incrby(maat_inst, "SEQUENCE_REGION", 1); - ret = integer_table_set_line(maat_inst, table_name, MAAT_OP_ADD, item_id, group_id, - low_bound, up_bound, 0); - EXPECT_EQ(ret, 1); -} - -static void test_add_flag_command(struct maat *maat_inst, const char *table_name, - long long flag, long long flag_mask) -{ - long long compile_id = maat_cmd_incrby(maat_inst, "TEST_SEQ", 1); - int ret = compile_table_set_line(maat_inst, "COMPILE_DEFAULT", MAAT_OP_ADD, compile_id, "null", 1, 0); - EXPECT_EQ(ret, 1); - - long long group_id = maat_cmd_incrby(maat_inst, "SEQUENCE_GROUP", 1); - ret = group2compile_table_set_line(maat_inst, "GROUP2COMPILE_DEFAULT", MAAT_OP_ADD, group_id, - compile_id, 0, "null", 1, 0); - EXPECT_EQ(ret, 1); - - long long item_id = maat_cmd_incrby(maat_inst, "SEQUENCE_REGION", 1); - ret = flag_table_set_line(maat_inst, table_name, MAAT_OP_ADD, item_id, group_id, - flag, flag_mask, 0); - EXPECT_EQ(ret, 1); -} - class MaatPerfStringScan : public testing::Test { protected: @@ -426,7 +345,7 @@ void *perf_string_scan_thread(void *arg) struct maat *maat_inst = param->maat_inst; const char *table_name = param->table_name; struct timespec start, end; - const char *scan_data = "today and yesterday should hit"; + const char *scan_data = "String TEST should hit"; long long results[ARRAY_SIZE] = {0}; int hit_times = 0; size_t n_hit_result = 0; @@ -474,67 +393,13 @@ void *perf_string_update_thread(void *arg) return is_all_hit; } -void *perf_regex_scan_thread(void *arg) -{ - struct thread_param *param = (struct thread_param *)arg; - struct maat *maat_inst = param->maat_inst; - const char *table_name = param->table_name; - struct timespec start, end; - const char *scan_data = "http://www.cyberessays.com/search_results.php?action=search&query=username,abckkk,1234567"; - long long results[ARRAY_SIZE] = {0}; - int hit_times = 0; - size_t n_hit_result = 0; - struct maat_state *state = maat_state_new(maat_inst, param->thread_id); - - int table_id = maat_get_table_id(maat_inst, table_name); - - clock_gettime(CLOCK_MONOTONIC, &start); - for (int i = 0; i < param->test_count; i++) { - int ret = maat_scan_string(maat_inst, table_id, scan_data, strlen(scan_data), - results, ARRAY_SIZE, &n_hit_result, state); - if (ret == MAAT_SCAN_HIT) { - hit_times++; - } - maat_state_reset(state); - } - clock_gettime(CLOCK_MONOTONIC, &end); - - param->time_elapse_ms = (end.tv_sec - start.tv_sec) * 1000 + (end.tv_nsec - start.tv_nsec) / 1000000; - int *is_all_hit = ALLOC(int, 1); - *is_all_hit = (hit_times == param->test_count ? 1 : 0); - log_info(param->logger, MODULE_FRAMEWORK_PERF_GTEST, - "thread_id:%d regex_scan time_elapse:%lldms hit_times:%d", - param->thread_id, param->time_elapse_ms, hit_times); - return is_all_hit; -} - -void *perf_regex_update_thread(void *arg) -{ - struct thread_param *param = (struct thread_param *)arg; - struct maat *maat_inst = param->maat_inst; - const char *table_name = param->table_name; - const int CMD_EXPR_NUM = 10; - char keyword_buf[128]; - - for (int i = 0; i < CMD_EXPR_NUM; i++) { - random_keyword_generate(keyword_buf, sizeof(keyword_buf)); - test_add_expr_command(maat_inst, table_name, keyword_buf); - sleep(1); - } - - int *is_all_hit = ALLOC(int, 1); - *is_all_hit = 1; - - return is_all_hit; -} - void *perf_ip_scan_thread(void *arg) { struct thread_param *param = (struct thread_param *)arg; struct maat *maat_inst = param->maat_inst; const char *table_name = param->table_name; struct timespec start, end; - char ip_str[32] = "10.0.0.1"; + char ip_str[32] = "10.0.7.100"; uint32_t ip_addr; uint16_t port = htons(65530); @@ -597,113 +462,8 @@ void *perf_ip_update_thread(void *arg) return is_all_hit; } -void *perf_integer_scan_thread(void *arg) -{ - struct thread_param *param = (struct thread_param *)arg; - struct maat *maat_inst = param->maat_inst; - const char *table_name = param->table_name; - struct timespec start, end; - int hit_times = 0; - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - - struct maat_state *state = maat_state_new(maat_inst, param->thread_id); - int table_id = maat_get_table_id(maat_inst, table_name); - - clock_gettime(CLOCK_MONOTONIC, &start); - for (int i = 0; i < param->test_count; i++) { - int ret = maat_scan_integer(maat_inst, table_id, 3000, results, - ARRAY_SIZE, &n_hit_result, state); - if (ret == MAAT_SCAN_HIT) { - hit_times++; - } - maat_state_reset(state); - } - clock_gettime(CLOCK_MONOTONIC, &end); - - param->time_elapse_ms = (end.tv_sec - start.tv_sec) * 1000 + - (end.tv_nsec - start.tv_nsec) / 1000000; - int *is_all_hit = ALLOC(int, 1); - *is_all_hit = (hit_times == param->test_count ? 1 : 0); - log_info(param->logger, MODULE_FRAMEWORK_PERF_GTEST, - "thread_id:%d integer_scan time_elapse:%lldms hit_times:%d", - param->thread_id, param->time_elapse_ms, hit_times); - return is_all_hit; -} - -void *perf_integer_update_thread(void *arg) -{ - struct thread_param *param = (struct thread_param *)arg; - struct maat *maat_inst = param->maat_inst; - const char *table_name = param->table_name; - const int CMD_EXPR_NUM = 10; - - for (int i = 0; i < CMD_EXPR_NUM; i++) { - test_add_integer_command(maat_inst, table_name, 3001+i, 3001+i); - sleep(1); - } - - int *is_all_hit = ALLOC(int, 1); - *is_all_hit = 1; - - return is_all_hit; -} - -void *perf_flag_scan_thread(void *arg) -{ - struct thread_param *param = (struct thread_param *)arg; - struct maat *maat_inst = param->maat_inst; - const char *table_name = param->table_name; - struct timespec start, end; - int hit_times = 0; - long long results[ARRAY_SIZE] = {0}; - size_t n_hit_result = 0; - long long scan_data = 15; - - struct maat_state *state = maat_state_new(maat_inst, param->thread_id); - int table_id = maat_get_table_id(maat_inst, table_name); - - clock_gettime(CLOCK_MONOTONIC, &start); - for (int i = 0; i < param->test_count; i++) { - int ret = maat_scan_flag(maat_inst, table_id, scan_data, results, - ARRAY_SIZE, &n_hit_result, state); - if (ret == MAAT_SCAN_HIT) { - hit_times++; - } - maat_state_reset(state); - } - clock_gettime(CLOCK_MONOTONIC, &end); - - param->time_elapse_ms = (end.tv_sec - start.tv_sec) * 1000 + - (end.tv_nsec - start.tv_nsec) / 1000000; - int *is_all_hit = ALLOC(int, 1); - *is_all_hit = (hit_times == param->test_count ? 1 : 0); - log_info(param->logger, MODULE_FRAMEWORK_PERF_GTEST, - "thread_id:%d flag_scan time_elapse:%lldms hit_times:%d", - param->thread_id, param->time_elapse_ms, hit_times); - return is_all_hit; -} - -void *perf_flag_update_thread(void *arg) -{ - struct thread_param *param = (struct thread_param *)arg; - struct maat *maat_inst = param->maat_inst; - const char *table_name = param->table_name; - const int CMD_EXPR_NUM = 10; - - for (int i = 0; i < CMD_EXPR_NUM; i++) { - test_add_flag_command(maat_inst, table_name, i, 15); - sleep(1); - } - - int *is_all_hit = ALLOC(int, 1); - *is_all_hit = 1; - - return is_all_hit; -} - -TEST_F(MaatPerfStringScan, LiteralMultiThread) { - const char *table_name = "EXPR_LITERAL_PERF_CONFIG"; +TEST_F(MaatPerfStringScan, MultiThread) { + const char *table_name = "KEYWORDS_TABLE"; struct maat *maat_inst = MaatPerfStringScan::_shared_maat_inst; int table_id = maat_get_table_id(maat_inst, table_name); @@ -743,58 +503,12 @@ TEST_F(MaatPerfStringScan, LiteralMultiThread) { free(is_all_hit); } scan_per_second = scan_count * 1000 / time_elapse_ms; + //EXPECT_GT(scan_per_second, 800 * 1000); log_info(maat_inst->logger, MODULE_FRAMEWORK_PERF_GTEST, "StringScan match rate on %d-threads speed %lld lookups/s/thread", PERF_THREAD_NUM, scan_per_second); } -TEST_F(MaatPerfStringScan, RegexMultiThread) { - const char *table_name = "EXPR_REGEX_PERF_CONFIG"; - struct maat *maat_inst = MaatPerfStringScan::_shared_maat_inst; - - int table_id = maat_get_table_id(maat_inst, table_name); - ASSERT_GT(table_id, 0); - - pthread_t threads[PERF_THREAD_NUM + 1]; - struct thread_param thread_params[PERF_THREAD_NUM + 1]; - int i = 0; - int *is_all_hit = NULL; - - for (i = 0; i < PERF_THREAD_NUM + 1; i++) { - thread_params[i].maat_inst = maat_inst; - thread_params[i].thread_id = i; - thread_params[i].table_name = table_name; - thread_params[i].test_count = PERF_SCAN_COUNT; - thread_params[i].time_elapse_ms = 0; - thread_params[i].logger = logger; - - if (i < PERF_THREAD_NUM) { - pthread_create(&threads[i], NULL, perf_regex_scan_thread, thread_params+i); - } else { - thread_params[i].test_count = 0; - pthread_create(&threads[i], NULL, perf_regex_update_thread, thread_params+i); - } - } - - long long time_elapse_ms = 0; - long long scan_count = 0; - long long scan_per_second = 0; - for (i = 0; i < PERF_THREAD_NUM + 1; i++) { - pthread_join(threads[i], (void **)&is_all_hit); - time_elapse_ms += thread_params[i].time_elapse_ms; - scan_count += thread_params[i].test_count; - - EXPECT_EQ(*is_all_hit, 1); - *is_all_hit = 0; - free(is_all_hit); - } - scan_per_second = scan_count * 1000 / time_elapse_ms; - - log_info(maat_inst->logger, MODULE_FRAMEWORK_PERF_GTEST, - "RegexScan match rate on %d-threads speed %lld lookups/s/thread", - PERF_THREAD_NUM, scan_per_second); -} - class MaatPerfStreamScan : public testing::Test { protected: @@ -847,31 +561,34 @@ void *perf_stream_scan_thread(void *arg) struct maat *maat_inst = param->maat_inst; const char *table_name = param->table_name; struct timespec start, end; - const char *scan_data = "http://www.cyberessays.com/search_results.php?today and yesterday"; + const char *scan_data = "http://www.cyberessays.com/search_results.php?action=search&query=yulingjing,abckkk,1234567"; long long results[ARRAY_SIZE] = {0}; int ret = 0, hit_times = 0; size_t n_hit_result = 0; - + struct maat_state *state_array[ARRAY_SIZE]; + struct maat_stream *sp[ARRAY_SIZE]; int table_id = maat_get_table_id(maat_inst, table_name); - struct maat_state *state = maat_state_new(maat_inst, param->thread_id); - struct maat_stream *sp = maat_stream_new(maat_inst, table_id, state); - + clock_gettime(CLOCK_MONOTONIC, &start); for (int i = 0; i < param->test_count; i++) { - ret = maat_stream_scan(sp, scan_data, strlen(scan_data), results, ARRAY_SIZE, - &n_hit_result, state); - if (ret == MAAT_SCAN_HIT) { - hit_times++; + for (int j = 0; j < ARRAY_SIZE; j++) { + state_array[j] = maat_state_new(maat_inst, param->thread_id); + sp[j] = maat_stream_new(maat_inst, table_id, state_array[j]); + + ret = maat_stream_scan(sp[j], scan_data, strlen(scan_data), results, ARRAY_SIZE, + &n_hit_result, state_array[j]); + if (ret == MAAT_SCAN_HIT) { + hit_times++; + } + maat_stream_free(sp[j]); + maat_state_free(state_array[j]); } - maat_state_reset(state); } clock_gettime(CLOCK_MONOTONIC, &end); - maat_stream_free(sp); - maat_state_free(state); param->time_elapse_ms = (end.tv_sec - start.tv_sec) * 1000 + (end.tv_nsec - start.tv_nsec) / 1000000; int *is_all_hit = ALLOC(int, 1); - *is_all_hit = ((hit_times == param->test_count) ? 1 : 0); + *is_all_hit = ((hit_times == param->test_count*ARRAY_SIZE) ? 1 : 0); log_info(param->logger, MODULE_FRAMEWORK_PERF_GTEST, "thread_id:%d stream_scan time_elapse:%lldms hit_times:%d", @@ -879,40 +596,64 @@ void *perf_stream_scan_thread(void *arg) return is_all_hit; } +void *perf_stream_update_thread(void *arg) +{ + struct thread_param *param = (struct thread_param *)arg; + struct maat *maat_inst = param->maat_inst; + const char *table_name = param->table_name; + const int CMD_EXPR_NUM = 10; + char keyword_buf[128]; + + for (int i = 0; i < CMD_EXPR_NUM; i++) { + random_keyword_generate(keyword_buf, sizeof(keyword_buf)); + test_add_expr_command(maat_inst, table_name, keyword_buf); + sleep(1); + } + + int *is_all_hit = ALLOC(int, 1); + *is_all_hit = 1; + + return is_all_hit; +} + TEST_F(MaatPerfStreamScan, MultiThread) { - const char *table_name = "EXPR_LITERAL_PERF_CONFIG"; + const char *table_name = "HTTP_URL"; struct maat *maat_inst = MaatPerfStreamScan::_shared_maat_inst; int table_id = maat_get_table_id(maat_inst, table_name); ASSERT_GT(table_id, 0); - pthread_t threads[PERF_THREAD_NUM]; - struct thread_param thread_params[PERF_THREAD_NUM]; + pthread_t threads[PERF_THREAD_NUM + 1]; + struct thread_param thread_params[PERF_THREAD_NUM + 1]; int i = 0; int *is_all_hit = NULL; - for (i = 0; i < PERF_THREAD_NUM; i++) { + for (i = 0; i < PERF_THREAD_NUM + 1; i++) { thread_params[i].maat_inst = maat_inst; thread_params[i].thread_id = i; thread_params[i].table_name = table_name; - thread_params[i].test_count = PERF_SCAN_COUNT; + thread_params[i].test_count = PERF_SCAN_COUNT / 10; thread_params[i].time_elapse_ms = 0; thread_params[i].logger = logger; if (i < PERF_THREAD_NUM) { pthread_create(&threads[i], NULL, perf_stream_scan_thread, thread_params+i); - } + } else { + thread_params[i].test_count = 0; + pthread_create(&threads[i], NULL, perf_stream_update_thread, thread_params+i); + } } long long time_elapse_ms = 0; long long scan_count = 0; long long scan_per_second = 0; - for (i = 0; i < PERF_THREAD_NUM; i++) { + for (i = 0; i < PERF_THREAD_NUM + 1; i++) { pthread_join(threads[i], (void **)&is_all_hit); time_elapse_ms += thread_params[i].time_elapse_ms; scan_count += thread_params[i].test_count; //maybe expr_runtime rebuild in stream_scan, so should not expect is_all_hit always 1 - EXPECT_EQ(*is_all_hit, 1); + //EXPECT_EQ(*is_all_hit, 1); + *is_all_hit = 0; free(is_all_hit); } scan_per_second = scan_count * 1000 / time_elapse_ms; @@ -970,7 +711,7 @@ struct log_handle *MaatPerfIPScan::logger; TEST_F(MaatPerfIPScan, MultiThread) { - const char *table_name = "IP_PERF_CONFIG"; + const char *table_name = "IP_PLUS_CONFIG"; struct maat *maat_inst = MaatPerfIPScan::_shared_maat_inst; int table_id = maat_get_table_id(maat_inst, table_name); @@ -1016,192 +757,6 @@ TEST_F(MaatPerfIPScan, MultiThread) PERF_THREAD_NUM, scan_per_second); } -class MaatPerfIntegerScan : public testing::Test -{ -protected: - static void SetUpTestCase() { - const char *accept_tags = "{\"tags\":[{\"tag\":\"location\",\"value\":\"北京/朝阳/华严北里/甲22号\"}," - "{\"tag\":\"isp\",\"value\":\"移动\"},{\"tag\":\"location\",\"value\":\"Astana\"}]}"; - char redis_ip[64] = "127.0.0.1"; - int redis_port = 6379; - int redis_db = 0; - - logger = log_handle_create("./maat_framework_perf_gtest.log", 0); - int ret = write_config_to_redis(redis_ip, redis_port, redis_db, logger); - if (ret < 0) { - log_error(logger, MODULE_FRAMEWORK_PERF_GTEST, - "[%s:%d] write config to redis failed.", __FUNCTION__, __LINE__); - } - - struct maat_options *opts = maat_options_new(); - maat_options_set_stat_file(opts, "./stat.log"); - maat_options_set_perf_on(opts); - maat_options_set_redis(opts, redis_ip, redis_port, redis_db); - maat_options_set_logger(opts, "./maat_framework_perf_gtest.log", LOG_LEVEL_INFO); - maat_options_set_accept_tags(opts, accept_tags); - maat_options_set_caller_thread_number(opts, 5); - - _shared_maat_inst = maat_new(opts, table_info_path); - maat_options_free(opts); - if (NULL == _shared_maat_inst) { - log_error(logger, MODULE_FRAMEWORK_PERF_GTEST, - "[%s:%d] create maat instance in MaatFlagScan failed.", - __FUNCTION__, __LINE__); - } - } - - static void TearDownTestCase() { - maat_free(_shared_maat_inst); - log_handle_destroy(logger); - } - - static struct log_handle *logger; - static struct maat *_shared_maat_inst; -}; - -struct maat *MaatPerfIntegerScan::_shared_maat_inst; -struct log_handle *MaatPerfIntegerScan::logger; - -TEST_F(MaatPerfIntegerScan, MultiThread) { - const char *table_name = "INTEGER_PERF_CONFIG"; - struct maat *maat_inst = MaatPerfIntegerScan::_shared_maat_inst; - - int table_id = maat_get_table_id(maat_inst, table_name); - ASSERT_GT(table_id, 0); - - pthread_t threads[PERF_THREAD_NUM + 1]; - struct thread_param thread_params[PERF_THREAD_NUM + 1]; - int i = 0; - int *is_all_hit = NULL; - - for (i = 0; i < PERF_THREAD_NUM + 1; i++) { - thread_params[i].maat_inst = maat_inst; - thread_params[i].thread_id = i; - thread_params[i].table_name = table_name; - thread_params[i].test_count = PERF_SCAN_COUNT; - thread_params[i].time_elapse_ms = 0; - thread_params[i].logger = logger; - - if (i < PERF_THREAD_NUM) { - pthread_create(&threads[i], NULL, perf_integer_scan_thread, thread_params+i); - } else { - thread_params[i].test_count = 0; - pthread_create(&threads[i], NULL, perf_integer_update_thread, thread_params+i); - } - } - - long long time_elapse_ms = 0; - long long scan_count = 0; - long long scan_per_second = 0; - for (i = 0; i < PERF_THREAD_NUM + 1; i++) { - pthread_join(threads[i], (void **)&is_all_hit); - time_elapse_ms += thread_params[i].time_elapse_ms; - scan_count += thread_params[i].test_count; - - EXPECT_EQ(*is_all_hit, 1); - *is_all_hit = 0; - free(is_all_hit); - } - scan_per_second = scan_count * 1000 / time_elapse_ms; - - log_info(maat_inst->logger, MODULE_FRAMEWORK_PERF_GTEST, - "IntegerScan match rate on %d-threads speed %lld lookups/s/thread", - PERF_THREAD_NUM, scan_per_second); -} - -class MaatPerfFlagScan : public testing::Test -{ -protected: - static void SetUpTestCase() { - const char *accept_tags = "{\"tags\":[{\"tag\":\"location\",\"value\":\"北京/朝阳/华严北里/甲22号\"}," - "{\"tag\":\"isp\",\"value\":\"移动\"},{\"tag\":\"location\",\"value\":\"Astana\"}]}"; - char redis_ip[64] = "127.0.0.1"; - int redis_port = 6379; - int redis_db = 0; - - logger = log_handle_create("./maat_framework_perf_gtest.log", 0); - int ret = write_config_to_redis(redis_ip, redis_port, redis_db, logger); - if (ret < 0) { - log_error(logger, MODULE_FRAMEWORK_PERF_GTEST, - "[%s:%d] write config to redis failed.", __FUNCTION__, __LINE__); - } - - struct maat_options *opts = maat_options_new(); - maat_options_set_stat_file(opts, "./stat.log"); - maat_options_set_perf_on(opts); - maat_options_set_redis(opts, redis_ip, redis_port, redis_db); - maat_options_set_logger(opts, "./maat_framework_perf_gtest.log", LOG_LEVEL_INFO); - maat_options_set_accept_tags(opts, accept_tags); - maat_options_set_caller_thread_number(opts, 5); - - _shared_maat_inst = maat_new(opts, table_info_path); - maat_options_free(opts); - if (NULL == _shared_maat_inst) { - log_error(logger, MODULE_FRAMEWORK_PERF_GTEST, - "[%s:%d] create maat instance in MaatFlagScan failed.", - __FUNCTION__, __LINE__); - } - } - - static void TearDownTestCase() { - maat_free(_shared_maat_inst); - log_handle_destroy(logger); - } - - static struct log_handle *logger; - static struct maat *_shared_maat_inst; -}; - -struct maat *MaatPerfFlagScan::_shared_maat_inst; -struct log_handle *MaatPerfFlagScan::logger; - -TEST_F(MaatPerfFlagScan, MultiThread) { - const char *table_name = "FLAG_PERF_CONFIG"; - struct maat *maat_inst = MaatPerfFlagScan::_shared_maat_inst; - - int table_id = maat_get_table_id(maat_inst, table_name); - ASSERT_GT(table_id, 0); - - pthread_t threads[PERF_THREAD_NUM + 1]; - struct thread_param thread_params[PERF_THREAD_NUM + 1]; - int i = 0; - int *is_all_hit = NULL; - - for (i = 0; i < PERF_THREAD_NUM + 1; i++) { - thread_params[i].maat_inst = maat_inst; - thread_params[i].thread_id = i; - thread_params[i].table_name = table_name; - thread_params[i].test_count = PERF_SCAN_COUNT; - thread_params[i].time_elapse_ms = 0; - thread_params[i].logger = logger; - - if (i < PERF_THREAD_NUM) { - pthread_create(&threads[i], NULL, perf_flag_scan_thread, thread_params+i); - } else { - thread_params[i].test_count = 0; - pthread_create(&threads[i], NULL, perf_flag_update_thread, thread_params+i); - } - } - - long long time_elapse_ms = 0; - long long scan_count = 0; - long long scan_per_second = 0; - for (i = 0; i < PERF_THREAD_NUM + 1; i++) { - pthread_join(threads[i], (void **)&is_all_hit); - time_elapse_ms += thread_params[i].time_elapse_ms; - scan_count += thread_params[i].test_count; - - EXPECT_EQ(*is_all_hit, 1); - *is_all_hit = 0; - free(is_all_hit); - } - scan_per_second = scan_count * 1000 / time_elapse_ms; - - log_info(maat_inst->logger, MODULE_FRAMEWORK_PERF_GTEST, - "FlagScan match rate on %d-threads speed %lld lookups/s/thread", - PERF_THREAD_NUM, scan_per_second); -} - class MaatPerfFQDNPluginScan : public testing::Test { protected: @@ -1777,4 +1332,4 @@ int main(int argc, char ** argv) ret=RUN_ALL_TESTS(); return ret; -} +} \ No newline at end of file diff --git a/test/maat_input_mode_gtest.cpp b/test/maat_input_mode_gtest.cpp index 2c190e8..058d6c9 100644 --- a/test/maat_input_mode_gtest.cpp +++ b/test/maat_input_mode_gtest.cpp @@ -247,4 +247,4 @@ int main(int argc, char ** argv) log_handle_destroy(g_logger); return ret; -} +} \ No newline at end of file diff --git a/test/maat_json.json b/test/maat_json.json index b2a4eba..d4c65ba 100644 --- a/test/maat_json.json +++ b/test/maat_json.json @@ -1687,6 +1687,28 @@ } ] }, + { + "compile_id": 175, + "service": 0, + "action": 0, + "do_blacklist": 0, + "do_log": 0, + "effective_rage": 0, + "user_region": "ipv4_composition.match", + "is_valid": "yes", + "groups": [ + { + "group_name": "ipv4_composition.source", + "virtual_table": "COMPOSITION_IP_SOURCE", + "not_flag": 0 + }, + { + "group_name": "ipv4_composition.destination", + "virtual_table": "COMPOSITION_IP_DESTINATION", + "not_flag": 0 + } + ] + }, { "compile_id": 176, "service": 0, @@ -1719,6 +1741,24 @@ } ] }, + { + "compile_id": 177, + "service": 0, + "action": 0, + "do_blacklist": 0, + "do_log": 0, + "effective_rage": 0, + "user_region": "ipv4_composition.session.match", + "is_valid": "yes", + "groups": [ + { + "group_name": "ipv4_composition.session", + "virtual_table": "COMPOSITION_IP_SESSION", + "not_flag": 0, + "clause_index": 1 + } + ] + }, { "compile_id": 178, "service": 1, @@ -1779,6 +1819,47 @@ } ] }, + { + "compile_id": 180, + "service": 0, + "action": 0, + "do_blacklist": 0, + "do_log": 0, + "effective_rage": 0, + "user_region": "Hierarchy_VirtualWithTwoPhysical", + "is_valid": "yes", + "groups": [ + { + "group_name": "FQDN_OBJ1", + "virtual_table": "VIRTUAL_SSL_SNI", + "not_flag": 0, + "clause_index": 0 + }, + { + "group_name": "FQDN_CAT1", + "virtual_table": "VIRTUAL_SSL_SNI", + "not_flag": 0, + "clause_index": 0 + } + ] + }, + { + "compile_id": 181, + "service": 0, + "action": 0, + "do_blacklist": 0, + "do_log": 0, + "effective_rage": 0, + "user_region": "ipv4_composition.match", + "is_valid": "yes", + "groups": [ + { + "group_name": "IPv4-composition-source-only", + "virtual_table": "COMPOSITION_IP_SOURCE", + "not_flag": 0 + } + ] + }, { "compile_id": 182, "service": 1, @@ -1861,6 +1942,28 @@ } ] }, + { + "compile_id": 185, + "service": 0, + "action": 0, + "do_blacklist": 0, + "do_log": 0, + "effective_rage": 0, + "user_region": "ipv4_composition.NOT_match", + "is_valid": "yes", + "groups": [ + { + "group_name": "IPv4-composition-NOT-client-ip", + "virtual_table": "COMPOSITION_IP_SOURCE", + "not_flag": 0 + }, + { + "group_name": "IPv4-composition-NOT-server-ip", + "virtual_table": "COMPOSITION_IP_DESTINATION", + "not_flag": 1 + } + ] + }, { "compile_id": 186, "service": 1, @@ -2258,7 +2361,7 @@ "do_blacklist": 1, "do_log": 1, "user_region": "Something:I\\bhave\\ba\\bname,7799", - "compile_table_name": "COMPILE_FIREWALL_DEFAULT", + "compile_table_name": "COMPILE_FIREWALL", "is_valid": "yes", "groups": [ { @@ -2870,135 +2973,7 @@ "not_flag": 0 } ] - }, - { - "compile_id": 211, - "service": 0, - "action": 0, - "do_blacklist": 0, - "do_log": 0, - "effective_rage": 0, - "user_region": "ip_perf_test", - "is_valid": "yes", - "groups": [ - { - "regions": [ - { - "table_type": "ip_plus", - "table_name": "IP_PERF_CONFIG", - "table_content": { - "addr_type": "ipv4", - "addr_format": "range", - "ip1": "10.0.0.1", - "ip2": "10.0.0.6", - "port_format": "range", - "port1": "65530", - "port2": "65535", - "protocol": 6 - } - } - ], - "not_flag": 0 - } - ] - }, - { - "compile_id": 212, - "service": 1, - "action": 1, - "do_blacklist": 1, - "do_log": 1, - "user_region": "integer_perf_test", - "is_valid": "yes", - "groups": [ - { - "group_name": "Untitled", - "regions": [ - { - "table_name": "INTEGER_PERF_CONFIG", - "table_type": "interval", - "table_content": { - "low_boundary": 3000, - "up_boundary": 3000 - } - } - ] - } - ] - }, - { - "compile_id": 213, - "service": 1, - "action": 1, - "do_blacklist": 1, - "do_log": 1, - "user_region": "expr_perf_test", - "is_valid": "yes", - "groups": [ - { - "regions": [ - { - "table_name": "EXPR_LITERAL_PERF_CONFIG", - "table_type": "expr", - "table_content": { - "keywords": "today&yesterday", - "expr_type": "and", - "match_method": "sub", - "format": "uncase plain" - } - } - ] - } - ] - }, - { - "compile_id": 214, - "service": 0, - "action": 0, - "do_blacklist": 0, - "do_log": 0, - "user_region": "flag_perf_test", - "is_valid": "yes", - "groups": [ - { - "regions": [ - { - "table_type": "flag", - "table_name": "FLAG_PERF_CONFIG", - "table_content": { - "flag": 15, - "flag_mask": 15 - } - } - ] - } - ] - }, - { - "compile_id": 215, - "service": 1, - "action": 1, - "do_blacklist": 1, - "do_log": 1, - "user_region": "expr_perf_test", - "is_valid": "yes", - "groups": [ - { - "regions": [ - { - "table_name": "EXPR_REGEX_PERF_CONFIG", - "table_type": "expr", - "table_content": { - "keywords": "action=search\\&query=(.*)", - "expr_type": "regex", - "match_method": "sub", - "format": "uncase plain" - } - } - ] - } - ] - } + } ], "plugin_table": [ { @@ -3009,6 +2984,14 @@ "3\t192.168.1.1\t103\t1" ] }, + { + "table_name": "TEST_PLUGIN_TABLE", + "table_content": [ + "1\t3388\t99\t1", + "2\t3355\t66\t1", + "3\tcccc\t11\t1" + ] + }, { "table_name": "TEST_PLUGIN_EXDATA_TABLE", "table_content": [ diff --git a/test/table_info.conf b/test/table_info.conf index 1219396..07702b4 100644 --- a/test/table_info.conf +++ b/test/table_info.conf @@ -1,10 +1,12 @@ [ { "table_id":0, - "table_name":"COMPILE_DEFAULT", + "table_name":"COMPILE", + "db_tables":["COMPILE_DEFAULT", "COMPILE_ALIAS"], "table_type":"compile", "valid_column":8, "custom": { + "gc_timeout_s": 3, "compile_id":1, "tags":6, "clause_num":9 @@ -12,20 +14,22 @@ }, { "table_id":1, - "table_name":"COMPILE_ALIAS", - "table_type":"compile", - "valid_column":8, + "table_name":"GROUP2COMPILE", + "db_tables":["GROUP2COMPILE_DEFAULT", "GROUP2COMPILE_ALIAS"], + "table_type":"group2compile", + "associated_compile_table_id":0, + "valid_column":3, "custom": { - "compile_id":1, - "tags":6, - "clause_num":9 + "group_id":1, + "compile_id":2, + "not_flag":4, + "virtual_table_name":5, + "clause_index":6 } - }, + }, { "table_id":2, - "table_name":"COMPILE_CONJUNCTION", - "db_tables":["COMPILE_DEFAULT", "COMPILE_ALIAS"], - "default_compile_table":1, + "table_name":"COMPILE_FIREWALL", "table_type":"compile", "valid_column":8, "custom": { @@ -36,8 +40,7 @@ }, { "table_id":3, - "table_name":"GROUP2COMPILE", - "db_tables":["GROUP2COMPILE_DEFAULT", "GROUP2COMPILE_ALIAS"], + "table_name":"GROUP2COMPILE_FIREWALL", "table_type":"group2compile", "associated_compile_table_id":2, "valid_column":3, @@ -51,43 +54,6 @@ }, { "table_id":4, - "table_name":"COMPILE_FIREWALL_DEFAULT", - "table_type":"compile", - "valid_column":8, - "custom": { - "compile_id":1, - "tags":6, - "clause_num":9 - } - }, - { - "table_id":5, - "table_name":"COMPILE_FIREWALL_CONJUNCTION", - "db_tables":["COMPILE_FIREWALL_DEFAULT"], - "table_type":"compile", - "valid_column":8, - "custom": { - "compile_id":1, - "tags":6, - "clause_num":9 - } - }, - { - "table_id":6, - "table_name":"GROUP2COMPILE_FIREWALL", - "table_type":"group2compile", - "associated_compile_table_id":5, - "valid_column":3, - "custom": { - "group_id":1, - "compile_id":2, - "not_flag":4, - "virtual_table_name":5, - "clause_index":6 - } - }, - { - "table_id":7, "table_name":"GROUP2GROUP", "table_type":"group2group", "valid_column":4, @@ -98,33 +64,7 @@ } }, { - "table_id":8, - "table_name":"COMPILE_PLUGIN", - "db_tables":["COMPILE_DEFAULT", "COMPILE_ALIAS"], - "table_type":"plugin", - "valid_column":8, - "custom": { - "gc_timeout_s":3, - "key_type":"integer", - "key_len":8, - "key":1 - } - }, - { - "table_id":9, - "table_name":"COMPILE_FIREWALL_PLUGIN", - "db_tables":["COMPILE_FIREWALL_DEFAULT"], - "table_type":"plugin", - "valid_column":8, - "custom": { - "gc_timeout_s":3, - "key_type":"integer", - "key_len":8, - "key":1 - } - }, - { - "table_id":10, + "table_id":5, "table_name":"HTTP_REGION", "db_tables":["HTTP_URL", "HTTP_HOST"], "table_type":"expr", @@ -139,7 +79,7 @@ } }, { - "table_id":11, + "table_id":6, "table_name":"KEYWORDS_TABLE", "table_type":"expr", "valid_column":7, @@ -153,7 +93,7 @@ } }, { - "table_id":12, + "table_id":7, "table_name":"IP_CONFIG", "table_type":"ip_plus", "valid_column":11, @@ -171,7 +111,7 @@ } }, { - "table_id":13, + "table_id":8, "table_name":"CONTENT_SIZE", "table_type":"intval", "valid_column":5, @@ -183,19 +123,18 @@ } }, { - "table_id":14, + "table_id":9, "table_name":"QD_ENTRY_INFO", "table_type":"plugin", "valid_column":4, "custom": { - "gc_timeout_s":3, "key_type":"integer", "key_len":8, "key":1 } }, { - "table_id":15, + "table_id":10, "table_name":"HTTP_SIGNATURE", "table_type":"expr_plus", "valid_column":8, @@ -210,7 +149,7 @@ } }, { - "table_id":16, + "table_id":11, "table_name":"IMAGE_FP", "table_type":"expr", "valid_column":7, @@ -224,12 +163,11 @@ } }, { - "table_id":17, + "table_id":12, "table_name":"TEST_EFFECTIVE_RANGE_TABLE", "table_type":"plugin", "valid_column":4, "custom": { - "gc_timeout_s":3, "key_type":"integer", "key_len":8, "key":1, @@ -237,12 +175,11 @@ } }, { - "table_id":18, + "table_id":13, "table_name":"TEST_FOREIGN_KEY", "table_type":"plugin", "valid_column":4, "custom": { - "gc_timeout_s":3, "key_type":"pointer", "key":2, "tag":3, @@ -250,7 +187,7 @@ } }, { - "table_id":19, + "table_id":14, "table_name":"TEST_PLUGIN_EXDATA_TABLE", "table_type":"plugin", "valid_column":4, @@ -262,19 +199,18 @@ } }, { - "table_id":20, + "table_id":15, "table_name":"IR_INTERCEPT_IP", "table_type":"plugin", "valid_column":14, "custom": { - "gc_timeout_s":3, "key_type":"pointer", "key":2, "tag":18 } }, { - "table_id":21, + "table_id":16, "table_name":"APP_PAYLOAD", "table_type":"expr_plus", "valid_column":8, @@ -289,7 +225,7 @@ } }, { - "table_id":22, + "table_id":17, "table_name":"TROJAN_PAYLOAD", "table_type":"expr", "valid_column":7, @@ -304,7 +240,7 @@ } }, { - "table_id":23, + "table_id":18, "table_name":"MAIL_ADDR", "table_type":"expr", "valid_column":7, @@ -318,7 +254,7 @@ } }, { - "table_id":24, + "table_id":19, "table_name":"IP_PLUS_CONFIG", "table_type":"ip_plus", "valid_column":11, @@ -336,32 +272,32 @@ } }, { - "table_id":25, + "table_id":20, "table_name":"HTTP_RESPONSE_KEYWORDS", "table_type":"virtual", "physical_table": "KEYWORDS_TABLE" }, { - "table_id":26, + "table_id":21, "table_name":"HTTP_REQUEST_HEADER", "table_type":"virtual", "physical_table": "HTTP_SIGNATURE" }, { - "table_id":27, + "table_id":22, "table_name":"HTTP_RESPONSE_HEADER", "table_type":"virtual", "physical_table": "HTTP_SIGNATURE" }, { - "table_id":28, + "table_id":23, "table_name":"VIRTUAL_IP_PLUS_TABLE", "db_tables":["VIRTUAL_IP_PLUS_SOURCE", "VIRTUAL_IP_PLUS_DESTINATION"], "table_type":"virtual", "physical_table": "IP_PLUS_CONFIG" }, { - "table_id":29, + "table_id":24, "table_name":"TEST_IP_PLUGIN_WITH_EXDATA", "table_type":"ip_plugin", "valid_column":6, @@ -375,7 +311,7 @@ } }, { - "table_id":30, + "table_id":25, "table_name":"AS_NUMBER", "table_type":"expr", "valid_column":7, @@ -389,19 +325,19 @@ } }, { - "table_id":31, + "table_id":26, "table_name":"SOURCE_IP_ASN", "table_type":"virtual", "physical_table":"AS_NUMBER" }, { - "table_id":32, + "table_id":27, "table_name":"DESTINATION_IP_ASN", "table_type":"virtual", "physical_table":"AS_NUMBER" }, { - "table_id":33, + "table_id":28, "table_name":"GeoLocation", "table_type":"expr", "valid_column":7, @@ -415,13 +351,13 @@ } }, { - "table_id":34, + "table_id":29, "table_name":"SOURCE_IP_GEO", "table_type":"virtual", "physical_table":"GeoLocation" }, { - "table_id":35, + "table_id":30, "table_name":"INTERGER_PLUS", "table_type":"intval_plus", "valid_column":6, @@ -434,7 +370,7 @@ } }, { - "table_id":36, + "table_id":31, "table_name":"TEST_FQDN_PLUGIN_WITH_EXDATA", "table_type":"fqdn_plugin", "valid_column":5, @@ -446,7 +382,7 @@ } }, { - "table_id":37, + "table_id":32, "table_name":"APP_ID", "table_type":"intval", "valid_column":5, @@ -458,7 +394,7 @@ } }, { - "table_id":38, + "table_id":33, "table_name":"EMPTY_KEYWORD", "table_type":"expr", "valid_column":7, @@ -472,7 +408,7 @@ } }, { - "table_id":39, + "table_id":34, "table_name":"EMPTY_INTERGER", "table_type":"intval", "valid_column":5, @@ -484,7 +420,7 @@ } }, { - "table_id":40, + "table_id":35, "table_name":"TEST_BOOL_PLUGIN_WITH_EXDATA", "table_type":"bool_plugin", "valid_column":4, @@ -495,7 +431,7 @@ } }, { - "table_id":41, + "table_id":36, "table_name":"FLAG_CONFIG", "table_type":"flag", "valid_column":5, @@ -507,7 +443,7 @@ } }, { - "table_id":42, + "table_id":37, "table_name":"FLAG_PLUS_CONFIG", "table_type":"flag_plus", "valid_column":6, @@ -520,12 +456,11 @@ } }, { - "table_id":43, + "table_id":38, "table_name":"TEST_PLUGIN_LONG_KEY_TYPE_TABLE", "table_type":"plugin", "valid_column":4, "custom": { - "gc_timeout_s":3, "key_type":"integer", "key_len":8, "key":2, @@ -533,12 +468,11 @@ } }, { - "table_id":44, + "table_id":39, "table_name":"TEST_PLUGIN_INT_KEY_TYPE_TABLE", "table_type":"plugin", "valid_column":4, "custom": { - "gc_timeout_s":3, "key_type":"integer", "key_len":4, "key":2, @@ -546,91 +480,20 @@ } }, { - "table_id":45, + "table_id":40, "table_name":"TEST_PLUGIN_IP_KEY_TYPE_TABLE", "table_type":"plugin", "valid_column":4, "custom": { - "gc_timeout_s":3, "key_type":"ip_addr", "addr_type":1, "key":2 } }, { - "table_id":46, + "table_id":41, "table_name":"HTTP_URL_FILTER", "table_type":"virtual", "physical_table": "HTTP_URL" - }, - { - "table_id":47, - "table_name":"IP_PERF_CONFIG", - "table_type":"ip_plus", - "valid_column":11, - "custom": { - "item_id":1, - "group_id":2, - "addr_type":3, - "addr_format":4, - "ip1":5, - "ip2":6, - "port_format":7, - "port1":8, - "port2":9, - "protocol":10 - } - }, - { - "table_id":48, - "table_name":"INTEGER_PERF_CONFIG", - "table_type":"intval", - "valid_column":5, - "custom": { - "item_id":1, - "group_id":2, - "low_bound":3, - "up_bound":4 - } - }, - { - "table_id":49, - "table_name":"EXPR_LITERAL_PERF_CONFIG", - "table_type":"expr", - "valid_column":7, - "custom": { - "item_id":1, - "group_id":2, - "keywords":3, - "expr_type":4, - "match_method":5, - "is_hexbin":6 - } - }, - { - "table_id":50, - "table_name":"EXPR_REGEX_PERF_CONFIG", - "table_type":"expr", - "valid_column":7, - "custom": { - "item_id":1, - "group_id":2, - "keywords":3, - "expr_type":4, - "match_method":5, - "is_hexbin":6 - } - }, - { - "table_id":51, - "table_name":"FLAG_PERF_CONFIG", - "table_type":"flag", - "valid_column":5, - "custom": { - "item_id":1, - "group_id":2, - "flag":3, - "flag_mask":4 - } } ] \ No newline at end of file diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt index 7c31d6c..30ea49d 100644 --- a/vendor/CMakeLists.txt +++ b/vendor/CMakeLists.txt @@ -58,34 +58,6 @@ add_dependencies(hyperscan_runtime_static hyperscan) set_property(TARGET hyperscan_runtime_static PROPERTY IMPORTED_LOCATION ${VENDOR_BUILD}/lib64/libhs_runtime.a) set_property(TARGET hyperscan_runtime_static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${VENDOR_BUILD}/include) -#pcre-8.45 -ExternalProject_Add(pcre PREFIX pcre - URL ${CMAKE_CURRENT_SOURCE_DIR}/pcre-8.45.tar.gz - CONFIGURE_COMMAND ./configure --prefix=${VENDOR_BUILD} - BUILD_COMMAND make - INSTALL_COMMAND make install - BUILD_IN_SOURCE 1) - -ExternalProject_Get_Property(pcre INSTALL_DIR) -file(MAKE_DIRECTORY ${VENDOR_BUILD}/include) - -#rulescan 3.0.1 -ExternalProject_Add(rulescan PREFIX rulescan - URL ${CMAKE_CURRENT_SOURCE_DIR}/rulescan-3.0.1.tar.gz - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${VENDOR_BUILD} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_C_FLAGS="-fPIC" -DCMAKE_CXX_FLAGS="-fPIC") - -ExternalProject_Get_Property(rulescan INSTALL_DIR) -file(MAKE_DIRECTORY ${VENDOR_BUILD}/include) - -#merge librulescan.a and libpcre.a => librs.a -add_custom_command(OUTPUT ${VENDOR_BUILD}/lib/librs.a - COMMAND ar crsT ${VENDOR_BUILD}/lib/librs.a ${VENDOR_BUILD}/lib/libpcre.a ${VENDOR_BUILD}/lib/librulescan.a - DEPENDS pcre rulescan) -add_custom_target(_merge ALL DEPENDS ${VENDOR_BUILD}/lib/librs.a) - -add_library(rulescan_static STATIC IMPORTED GLOBAL) -set_property(TARGET rulescan_static PROPERTY IMPORTED_LOCATION ${VENDOR_BUILD}/lib/librs.a) - # hiredis-1.1.0 ExternalProject_Add(hiredis PREFIX hiredis URL ${CMAKE_CURRENT_SOURCE_DIR}/hiredis-1.1.0.tar.gz diff --git a/vendor/pcre-8.45.tar.gz b/vendor/pcre-8.45.tar.gz deleted file mode 100644 index 19f8dfe2a574f4986a1f6a6730cf7ffadc6ddc94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2287710 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

qWOn02$si$hq;3=)1*xfep83NG|#?-rUl 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


%aE1d{MVwLHw>L( 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 z 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