From a018e732eb6c5576aa4662e57793d0a8ac1fe1fc Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 17 Nov 2016 07:17:12 +0200 Subject: [PATCH] Make the dbfwfilter rule check a separate utility The code for the utility is now stored in a separate file. This also removes the need to include testing headers from other directories. Also added a function to reload rules that uses the newly modified rule parsing mechanism. This can be used later on to update the rules at runtime. --- .../modules/filter/dbfwfilter/CMakeLists.txt | 11 +- .../filter/dbfwfilter/dbfw_rule_check.c | 55 ++++++ server/modules/filter/dbfwfilter/dbfwfilter.c | 156 ++++++------------ 3 files changed, 111 insertions(+), 111 deletions(-) create mode 100644 server/modules/filter/dbfwfilter/dbfw_rule_check.c diff --git a/server/modules/filter/dbfwfilter/CMakeLists.txt b/server/modules/filter/dbfwfilter/CMakeLists.txt index ad1b91371..8f03ad2b0 100644 --- a/server/modules/filter/dbfwfilter/CMakeLists.txt +++ b/server/modules/filter/dbfwfilter/CMakeLists.txt @@ -11,12 +11,11 @@ if(BISON_FOUND AND FLEX_FOUND) set_target_properties(dbfwfilter PROPERTIES VERSION "1.0.0") install_module(dbfwfilter core) - if(BUILD_TOOLS) - add_executable(dbfwruleparser dbfwfilter.c ${BISON_ruleparser_OUTPUTS} ${FLEX_token_OUTPUTS}) - target_compile_definitions(dbfwruleparser PUBLIC "BUILD_RULE_PARSER") - target_link_libraries(dbfwruleparser maxscale-common) - install_module(dbfwruleparser core) - endif() + # The offline rule check utility + add_executable(dbfwchk dbfw_rule_check.c ${BISON_ruleparser_OUTPUTS} ${FLEX_token_OUTPUTS}) + target_link_libraries(dbfwchk maxscale-common) + install_executable(dbfwchk core) + else() message(FATAL_ERROR "Could not find Bison or Flex: ${BISON_EXECUTABLE} ${FLEX_EXECUTABLE}") endif() diff --git a/server/modules/filter/dbfwfilter/dbfw_rule_check.c b/server/modules/filter/dbfwfilter/dbfw_rule_check.c new file mode 100644 index 000000000..13f8adf6f --- /dev/null +++ b/server/modules/filter/dbfwfilter/dbfw_rule_check.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl. + * + * Change Date: 2019-07-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include "dbfwfilter.c" + +int main(int argc, char **argv) +{ + int rval = 1; + + if (argc < 2) + { + printf("Usage: dbfw_rule_check FILE\n"); + } + else + { + mxs_log_init("dbfwfilter_rule_parser", ".", MXS_LOG_TARGET_STDOUT); + + if (access(argv[1], R_OK) == 0) + { + MXS_NOTICE("Parsing rule file: %s", argv[1]); + + RULE* rules; + HASHTABLE *users; + + if (process_rule_file(argv[1], &rules, &users)) + { + MXS_NOTICE("Rule parsing was successful."); + rval = 0; + } + else + { + MXS_ERROR("Failed to parse rules."); + } + } + else + { + MXS_ERROR("Failed to read file '%s': %d, %s", argv[1], errno, strerror(errno)); + } + + mxs_log_finish(); + + } + + return rval; +} diff --git a/server/modules/filter/dbfwfilter/dbfwfilter.c b/server/modules/filter/dbfwfilter/dbfwfilter.c index 7d9dcc8e6..42fcbada9 100644 --- a/server/modules/filter/dbfwfilter/dbfwfilter.c +++ b/server/modules/filter/dbfwfilter/dbfwfilter.c @@ -424,6 +424,18 @@ static void dbfw_user_free(void* fval) MXS_FREE(value); } +HASHTABLE *dbfw_userlist_create() +{ + HASHTABLE *ht = hashtable_alloc(100, hashtable_item_strhash, hashtable_item_strcmp); + + if (ht) + { + hashtable_memory_fns(ht, hashtable_item_strdup, NULL, hashtable_item_free, dbfw_user_free); + } + + return ht; +} + /** * Parses a string that contains an IP address and converts the last octet to '%'. * This modifies the string passed as the parameter. @@ -1204,7 +1216,7 @@ static RULE* find_rule_by_name(RULE* rules, const char* name) * @param rules List of all rules * @return True on success, false on error. */ -static bool process_user_templates(FW_INSTANCE *instance, user_template_t *templates, +static bool process_user_templates(HASHTABLE *users, user_template_t *templates, RULE* rules) { bool rval = true; @@ -1217,7 +1229,7 @@ static bool process_user_templates(FW_INSTANCE *instance, user_template_t *templ while (templates) { - DBFW_USER *user = hashtable_fetch(instance->users, templates->name); + DBFW_USER *user = hashtable_fetch(users, templates->name); if (user == NULL) { @@ -1227,7 +1239,7 @@ static bool process_user_templates(FW_INSTANCE *instance, user_template_t *templ user->rules_or = NULL; user->rules_strict_and = NULL; spinlock_init(&user->lock); - hashtable_add(instance->users, user->name, user); + hashtable_add(users, user->name, user); } else { @@ -1292,7 +1304,7 @@ static bool process_user_templates(FW_INSTANCE *instance, user_template_t *templ * @param instance Filter instance * @return True on success, false on error. */ -static bool process_rule_file(const char* filename, FW_INSTANCE* instance) +static bool process_rule_file(const char* filename, RULE** rules, HASHTABLE **users) { int rc = 1; FILE *file = fopen(filename, "r"); @@ -1318,15 +1330,18 @@ static bool process_rule_file(const char* filename, FW_INSTANCE* instance) dbfw_yy_delete_buffer(buf, scanner); dbfw_yylex_destroy(scanner); fclose(file); + HASHTABLE *new_users = dbfw_userlist_create(); - if (rc == 0 && process_user_templates(instance, pstack.templates, pstack.rule)) + if (rc == 0 && new_users && process_user_templates(new_users, pstack.templates, pstack.rule)) { - instance->rules = pstack.rule; + *rules = pstack.rule; + *users = new_users; } else { rc = 1; rule_free_all(pstack.rule); + hashtable_free(new_users); MXS_ERROR("Failed to process rule file '%s'.", filename); } @@ -1345,16 +1360,39 @@ static bool process_rule_file(const char* filename, FW_INSTANCE* instance) return rc == 0; } -HASHTABLE *create_dbfw_users() -{ - HASHTABLE *ht = hashtable_alloc(100, hashtable_item_strhash, hashtable_item_strcmp); - if (ht) +/** + * @brief Replace the rule file used by this filter instance + * + * This function does no locking. An external lock needs to protect this function + * call to prevent any connections from using the data when it is being replaced. + * + * @param filename File where the rules are located + * @param instance Filter instance + * @return True on success, false on error. If the return value is false, the + * old rules remain active. + */ +bool replace_rule_file(const char* filename, FW_INSTANCE* instance) +{ + bool rval = false; + RULE *rules; + HASHTABLE *users; + + if (process_rule_file(filename, &rules, &users)) { - hashtable_memory_fns(ht, hashtable_item_strdup, NULL, hashtable_item_free, dbfw_user_free); + /** Rules processed successfully, free the old ones */ + rule_free_all(instance->rules); + hashtable_free(instance->users); + instance->rules = rules; + instance->users = users; + rval = true; + } + else + { + MXS_ERROR("Failed to process rule file at '%s', old rules are still active.", filename); } - return ht; + return rval; } /** @@ -1382,14 +1420,6 @@ createInstance(const char *name, char **options, FILTER_PARAMETER **params) } spinlock_init(&my_instance->lock); - - if ((my_instance->users = create_dbfw_user()) == NULL) - { - MXS_ERROR("Unable to allocate hashtable."); - MXS_FREE(my_instance); - return NULL; - } - my_instance->action = FW_ACTION_BLOCK; my_instance->log_match = FW_LOG_NONE; @@ -1444,9 +1474,8 @@ createInstance(const char *name, char **options, FILTER_PARAMETER **params) err = true; } - if (err || !process_rule_file(filename, my_instance)) + if (err || !process_rule_file(filename, &my_instance->rules, &my_instance->users)) { - hashtable_free(my_instance->users); MXS_FREE(my_instance); my_instance = NULL; } @@ -2332,86 +2361,3 @@ static uint64_t getCapabilities(void) { return RCAP_TYPE_STMT_INPUT; } - -#ifdef BUILD_RULE_PARSER -// TODO: Not ok to include file from other component's test directory. -#include "../../../core/test/test_utils.h" - -int main(int argc, char** argv) -{ - char ch; - bool have_icase = false; - char *home; - char cwd[PATH_MAX]; - char* opts[2] = {NULL, NULL}; - FILTER_PARAMETER ruleparam; - FILTER_PARAMETER * paramlist[2]; - - opterr = 0; - while ((ch = getopt(argc, argv, "h?")) != -1) - { - switch (ch) - { - case '?': - case 'h': - printf("Usage: %s [OPTION]... RULEFILE\n" - "Options:\n" - "\t-?\tPrint this information\n", - argv[0]); - return 0; - default: - printf("Unknown option '%c'.\n", ch); - return 1; - } - } - - if (argc < 2) - { - printf("Usage: %s [OPTION]... RULEFILE\n" - "-?\tPrint this information\n", - argv[0]); - return 1; - } - - home = MXS_MALLOC(sizeof(char) * (PATH_MAX + 1)); - MXS_ABORT_IF_NULL(home); - if (getcwd(home, PATH_MAX) == NULL) - { - MXS_FREE(home); - home = NULL; - } - - printf("Log files written to: %s\n", home ? home : "/tpm"); - - int argc_ = 2; - char* argv_[] = - { - "log_manager", - "-o", - NULL - }; - - mxs_log_init(NULL, NULL, MXS_LOG_TARGET_DEFAULT); - - - init_test_env(home); - ruleparam.name = MXS_STRDUP_A("rules"); - ruleparam.value = MXS_STRDUP_A(argv[1]); - paramlist[0] = &ruleparam; - paramlist[1] = NULL; - - if (createInstance(opts, paramlist)) - { - printf("Rule parsing was successful.\n"); - } - else - { - printf("Failed to parse rule. Read the error log for the reason of the failure.\n"); - } - - mxs_log_flush_sync(); - - return 0; -} - -#endif