From c827bb4774bbab5e06ac2be405e2b2ec2aec3446 Mon Sep 17 00:00:00 2001 From: Dmitry Smal Date: Mon, 8 Apr 2024 11:56:18 +0300 Subject: [PATCH] option for strict sequential route matching (#605) option for strict sequential route matching --- README.md | 1 + documentation/configuration.md | 10 ++++ sources/config.c | 3 + sources/config.h | 1 + sources/config_reader.c | 9 +++ sources/router.c | 7 ++- sources/rules.c | 101 +++++++++++++++++++++++++++++++-- sources/rules.h | 3 +- 8 files changed, 126 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c9bb796c6..d6afc746e 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,7 @@ Set up your CLion to build project in container, [manual](https://github.com/shu * [include](documentation/configuration.md#include-string) * [daemonize](documentation/configuration.md#daemonize-yesno) +* [sequential\_routing](documentation/configuration.md#sequential_routing-yesno) * [priority](documentation/configuration.md#priority-integer) * [pid\_file](documentation/configuration.md#pid_file-string) * [unix\_socket\_dir](documentation/configuration.md#unix_socket_dir-string) diff --git a/documentation/configuration.md b/documentation/configuration.md index deadf5a7c..ce7b3a50a 100644 --- a/documentation/configuration.md +++ b/documentation/configuration.md @@ -16,6 +16,16 @@ By default Odyssey does not run as a daemon. Set to 'yes' to enable. `daemonize no` +#### sequential\_routing_ *yes|no* + +Try to match routes exactly in config order. + +By default, Odyssey tries to match all specific routes first, and then all default ones. +It may be confusing because auth-denying default route can be overridden with more specific auth-permitting route below in the config. +With this option set, Odyssey will match routes exactly in config order, like in HBA files. + +`sequential_routing no` + #### priority *integer* Process priority. diff --git a/sources/config.c b/sources/config.c index df5b1eb43..03b9c1e00 100644 --- a/sources/config.c +++ b/sources/config.c @@ -14,6 +14,7 @@ void od_config_init(od_config_t *config) { config->daemonize = 0; config->priority = 0; + config->sequential_routing = 0; config->log_debug = 0; config->log_to_stdout = 1; config->log_config = 0; @@ -245,6 +246,8 @@ void od_config_print(od_config_t *config, od_logger_t *logger) od_config_yes_no(config->daemonize)); od_log(logger, "config", NULL, NULL, "priority %d", config->priority); + od_log(logger, "config", NULL, NULL, "sequential_routing %s", + od_config_yes_no(config->sequential_routing)); if (config->pid_file) od_log(logger, "config", NULL, NULL, "pid_file %s", config->pid_file); diff --git a/sources/config.h b/sources/config.h index 65345ea01..f8dadbbb8 100644 --- a/sources/config.h +++ b/sources/config.h @@ -27,6 +27,7 @@ struct od_config_listen { struct od_config { int daemonize; int priority; + int sequential_routing; /* logging */ int log_to_stdout; int log_debug; diff --git a/sources/config_reader.c b/sources/config_reader.c index 201541b30..564cba0a4 100644 --- a/sources/config_reader.c +++ b/sources/config_reader.c @@ -16,6 +16,7 @@ typedef enum { OD_LINCLUDE, OD_LDAEMONIZE, OD_LPRIORITY, + OD_LSEQROUTING, OD_LLOG_TO_STDOUT, OD_LLOG_DEBUG, OD_LLOG_CONFIG, @@ -154,6 +155,7 @@ static od_keyword_t od_config_keywords[] = { od_keyword("include", OD_LINCLUDE), od_keyword("daemonize", OD_LDAEMONIZE), od_keyword("priority", OD_LPRIORITY), + od_keyword("sequential_routing", OD_LSEQROUTING), od_keyword("pid_file", OD_LPID_FILE), od_keyword("unix_socket_dir", OD_LUNIX_SOCKET_DIR), od_keyword("unix_socket_mode", OD_LUNIX_SOCKET_MODE), @@ -2274,6 +2276,13 @@ static int od_config_reader_parse(od_config_reader_t *reader, goto error; } continue; + /* sequential_routing */ + case OD_LSEQROUTING: + if (!od_config_reader_yes_no( + reader, &config->sequential_routing)) { + goto error; + } + continue; /* pid_file */ case OD_LPID_FILE: if (!od_config_reader_string(reader, diff --git a/sources/router.c b/sources/router.c index cb0b7c6fd..d3dbe150e 100644 --- a/sources/router.c +++ b/sources/router.c @@ -358,10 +358,12 @@ od_router_status_t od_router_route(od_router_t *router, od_client_t *client) int salen; struct sockaddr *saddr; int rc; + int sequential = instance->config.sequential_routing; switch (client->type) { case OD_POOL_CLIENT_INTERNAL: rule = od_rules_forward(&router->rules, startup->database.value, - startup->user.value, NULL, 1); + startup->user.value, NULL, 1, + sequential); break; case OD_POOL_CLIENT_EXTERNAL: salen = sizeof(sa); @@ -372,7 +374,8 @@ od_router_status_t od_router_route(od_router_t *router, od_client_t *client) return OD_ROUTER_ERROR; } rule = od_rules_forward(&router->rules, startup->database.value, - startup->user.value, &sa, 0); + startup->user.value, &sa, 0, + sequential); break; case OD_POOL_CLIENT_UNDEF: // create that case for correct work of '-Wswitch' flag break; diff --git a/sources/rules.c b/sources/rules.c index 3e1c53c7b..999275d7b 100644 --- a/sources/rules.c +++ b/sources/rules.c @@ -257,9 +257,10 @@ void od_rules_unref(od_rule_t *rule) od_rules_rule_free(rule); } -od_rule_t *od_rules_forward(od_rules_t *rules, char *db_name, char *user_name, - struct sockaddr_storage *user_addr, - int pool_internal) +static od_rule_t *od_rules_forward_default(od_rules_t *rules, char *db_name, + char *user_name, + struct sockaddr_storage *user_addr, + int pool_internal) { od_rule_t *rule_db_user_default = NULL; od_rule_t *rule_db_default_default = NULL; @@ -346,6 +347,64 @@ od_rule_t *od_rules_forward(od_rules_t *rules, char *db_name, char *user_name, return rule_default_default_default; } +static od_rule_t * +od_rules_forward_sequential(od_rules_t *rules, char *db_name, char *user_name, + struct sockaddr_storage *user_addr, + int pool_internal) +{ + od_list_t *i; + od_rule_t *rule_matched = NULL; + bool db_matched = false, user_matched = false, addr_matched = false; + od_list_foreach(&rules->rules, i) + { + od_rule_t *rule; + rule = od_container_of(i, od_rule_t, link); + } + od_list_foreach(&rules->rules, i) + { + od_rule_t *rule; + rule = od_container_of(i, od_rule_t, link); + if (rule->obsolete) { + continue; + } + if (pool_internal) { + if (rule->pool->routing != OD_RULE_POOL_INTERVAL) { + continue; + } + } else { + if (rule->pool->routing != + OD_RULE_POOL_CLIENT_VISIBLE) { + continue; + } + } + db_matched = rule->db_is_default || + (strcmp(rule->db_name, db_name) == 0); + user_matched = rule->user_is_default || + (strcmp(rule->user_name, user_name) == 0); + addr_matched = + rule->address_range.is_default || + od_address_validate(&rule->address_range, user_addr); + if (db_matched && user_matched && addr_matched) { + rule_matched = rule; + break; + } + } + assert(rule_matched); + return rule_matched; +} + +od_rule_t *od_rules_forward(od_rules_t *rules, char *db_name, char *user_name, + struct sockaddr_storage *user_addr, + int pool_internal, int sequential) +{ + if (sequential) { + return od_rules_forward_sequential(rules, db_name, user_name, + user_addr, pool_internal); + } + return od_rules_forward_default(rules, db_name, user_name, user_addr, + pool_internal); +} + od_rule_t *od_rules_match(od_rules_t *rules, char *db_name, char *user_name, od_address_range_t *address_range, int db_is_default, int user_is_default, int pool_internal) @@ -630,16 +689,25 @@ __attribute__((hot)) int od_rules_merge(od_rules_t *rules, od_rules_t *src, int count_mark = 0; int count_deleted = 0; int count_new = 0; + int src_length = 0; - /* mark all rules for obsoletion */ + /* set order for new rules */ od_list_t *i; + od_list_foreach(&src->rules, i) + { + od_rule_t *rule; + rule = od_container_of(i, od_rule_t, link); + rule->order = src_length; + src_length++; + } + + /* mark all rules for obsoletion */ od_list_foreach(&rules->rules, i) { od_rule_t *rule; rule = od_container_of(i, od_rule_t, link); rule->mark = 1; count_mark++; - od_hashmap_empty(rule->storage->acache); } @@ -741,6 +809,7 @@ __attribute__((hot)) int od_rules_merge(od_rules_t *rules, od_rules_t *src, if (od_rules_rule_compare(origin, rule)) { origin->mark = 0; count_mark--; + origin->order = rule->order; continue; /* select rules with changes what needed disconnect */ } else if (!od_rules_rule_compare_to_drop(origin, @@ -797,6 +866,26 @@ __attribute__((hot)) int od_rules_merge(od_rules_t *rules, od_rules_t *src, } } + /* sort rules according order, leaving obsolete at the end of the list */ + od_list_t **sorted = calloc(src_length, sizeof(od_list_t *)); + od_list_foreach_safe(&rules->rules, i, n) + { + od_rule_t *rule; + rule = od_container_of(i, od_rule_t, link); + if (rule->obsolete) { + continue; + } + assert(rule->order >= 0 && rule->order < src_length && + sorted[rule->order] == NULL); + od_list_unlink(&rule->link); + sorted[rule->order] = &rule->link; + } + for (int s = src_length - 1; s >= 0; s--) { + assert(sorted[s] != NULL); + od_list_push(&rules->rules, sorted[s]); + } + free(sorted); + return count_new + count_mark + count_deleted; } @@ -1229,7 +1318,7 @@ int od_rules_cleanup(od_rules_t *rules) od_list_init(&rules->storages); #ifdef LDAP_FOUND - /* TODO: cleanup ldap + /* TODO: cleanup ldap od_list_foreach_safe(&rules->storages, i, n) { od_ldap_endpoint_t *endp; diff --git a/sources/rules.h b/sources/rules.h index c0b10da73..4c08c5d53 100644 --- a/sources/rules.h +++ b/sources/rules.h @@ -60,6 +60,7 @@ struct od_rule { int mark; int obsolete; int refs; + int order; /* id */ char *db_name; @@ -174,7 +175,7 @@ void od_rules_unref(od_rule_t *); int od_rules_compare(od_rule_t *, od_rule_t *); od_rule_t *od_rules_forward(od_rules_t *, char *, char *, - struct sockaddr_storage *, int); + struct sockaddr_storage *, int, int); /* search rule with desored characteristik */ od_rule_t *od_rules_match(od_rules_t *rules, char *db_name, char *user_name,