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.
This commit is contained in:
Markus Makela 2016-11-17 07:17:12 +02:00
parent 8ae76e3ced
commit a018e732eb
3 changed files with 111 additions and 111 deletions

View File

@ -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()

View File

@ -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;
}

View File

@ -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