mirror of
http://git.haproxy.org/git/haproxy.git
synced 2026-02-13 00:49:09 +02:00
action_suggest() will return a pointer to an action whose keyword more or less ressembles the passed argument. It also accepts to be more tolerant against prefixes (since actions taking arguments are handled as prefixes). This will be used to suggest approaching words.
262 lines
8.6 KiB
C
262 lines
8.6 KiB
C
/*
|
|
* Action management functions.
|
|
*
|
|
* Copyright 2017 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*
|
|
*/
|
|
|
|
#include <haproxy/action.h>
|
|
#include <haproxy/api.h>
|
|
#include <haproxy/errors.h>
|
|
#include <haproxy/list.h>
|
|
#include <haproxy/obj_type.h>
|
|
#include <haproxy/pool.h>
|
|
#include <haproxy/proxy.h>
|
|
#include <haproxy/stick_table.h>
|
|
#include <haproxy/task.h>
|
|
#include <haproxy/tools.h>
|
|
|
|
|
|
/* Find and check the target table used by an action track-sc*. This
|
|
* function should be called during the configuration validity check.
|
|
*
|
|
* The function returns 1 in success case, otherwise, it returns 0 and err is
|
|
* filled.
|
|
*/
|
|
int check_trk_action(struct act_rule *rule, struct proxy *px, char **err)
|
|
{
|
|
struct stktable *target;
|
|
|
|
if (rule->arg.trk_ctr.table.n)
|
|
target = stktable_find_by_name(rule->arg.trk_ctr.table.n);
|
|
else
|
|
target = px->table;
|
|
|
|
if (!target) {
|
|
memprintf(err, "unable to find table '%s' referenced by track-sc%d",
|
|
rule->arg.trk_ctr.table.n ? rule->arg.trk_ctr.table.n : px->id,
|
|
rule->action);
|
|
return 0;
|
|
}
|
|
|
|
if (!stktable_compatible_sample(rule->arg.trk_ctr.expr, target->type)) {
|
|
memprintf(err, "stick-table '%s' uses a type incompatible with the 'track-sc%d' rule",
|
|
rule->arg.trk_ctr.table.n ? rule->arg.trk_ctr.table.n : px->id,
|
|
rule->action);
|
|
return 0;
|
|
}
|
|
else if (target->proxy && (px->bind_proc & ~target->proxy->bind_proc)) {
|
|
memprintf(err, "stick-table '%s' referenced by 'track-sc%d' rule not present on all processes covered by proxy '%s'",
|
|
target->id, rule->action, px->id);
|
|
return 0;
|
|
}
|
|
else {
|
|
if (!in_proxies_list(target->proxies_list, px)) {
|
|
px->next_stkt_ref = target->proxies_list;
|
|
target->proxies_list = px;
|
|
}
|
|
free(rule->arg.trk_ctr.table.n);
|
|
rule->arg.trk_ctr.table.t = target;
|
|
/* Note: if we decide to enhance the track-sc syntax, we may be
|
|
* able to pass a list of counters to track and allocate them
|
|
* right here using stktable_alloc_data_type().
|
|
*/
|
|
}
|
|
|
|
if (rule->from == ACT_F_TCP_REQ_CNT && (px->cap & PR_CAP_FE)) {
|
|
if (!px->tcp_req.inspect_delay && !(rule->arg.trk_ctr.expr->fetch->val & SMP_VAL_FE_SES_ACC)) {
|
|
ha_warning("config : %s '%s' : a 'tcp-request content track-sc*' rule explicitly depending on request"
|
|
" contents without any 'tcp-request inspect-delay' setting."
|
|
" This means that this rule will randomly find its contents. This can be fixed by"
|
|
" setting the tcp-request inspect-delay.\n",
|
|
proxy_type_str(px), px->id);
|
|
}
|
|
|
|
/* The following warning is emitted because HTTP multiplexers are able to catch errors
|
|
* or timeouts at the session level, before instantiating any stream.
|
|
* Thus the tcp-request content ruleset will not be evaluated in such case. It means,
|
|
* http_req and http_err counters will not be incremented as expected, even if the tracked
|
|
* counter does not use the request content. To track invalid requests it should be
|
|
* performed at the session level using a tcp-request session rule.
|
|
*/
|
|
if (px->mode == PR_MODE_HTTP &&
|
|
!(rule->arg.trk_ctr.expr->fetch->use & (SMP_USE_L6REQ|SMP_USE_HRQHV|SMP_USE_HRQHP|SMP_USE_HRQBO)) &&
|
|
(!rule->cond || !(rule->cond->use & (SMP_USE_L6REQ|SMP_USE_HRQHV|SMP_USE_HRQHP|SMP_USE_HRQBO)))) {
|
|
ha_warning("config : %s '%s' : a 'tcp-request content track-sc*' rule not depending on request"
|
|
" contents for an HTTP frontend should be executed at the session level, using a"
|
|
" 'tcp-request session' rule (mandatory to track invalid HTTP requests).\n",
|
|
proxy_type_str(px), px->id);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* check a capture rule. This function should be called during the configuration
|
|
* validity check.
|
|
*
|
|
* The function returns 1 in success case, otherwise, it returns 0 and err is
|
|
* filled.
|
|
*/
|
|
int check_capture(struct act_rule *rule, struct proxy *px, char **err)
|
|
{
|
|
if (rule->from == ACT_F_TCP_REQ_CNT && (px->cap & PR_CAP_FE) && !px->tcp_req.inspect_delay &&
|
|
!(rule->arg.trk_ctr.expr->fetch->val & SMP_VAL_FE_SES_ACC)) {
|
|
ha_warning("config : %s '%s' : a 'tcp-request capture' rule explicitly depending on request"
|
|
" contents without any 'tcp-request inspect-delay' setting."
|
|
" This means that this rule will randomly find its contents. This can be fixed by"
|
|
" setting the tcp-request inspect-delay.\n",
|
|
proxy_type_str(px), px->id);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int act_resolution_cb(struct resolv_requester *requester, struct dns_counters *counters)
|
|
{
|
|
struct stream *stream;
|
|
|
|
if (requester->resolution == NULL)
|
|
return 0;
|
|
|
|
stream = objt_stream(requester->owner);
|
|
if (stream == NULL)
|
|
return 0;
|
|
|
|
task_wakeup(stream->task, TASK_WOKEN_MSG);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int act_resolution_error_cb(struct resolv_requester *requester, int error_code)
|
|
{
|
|
struct stream *stream;
|
|
|
|
if (requester->resolution == NULL)
|
|
return 0;
|
|
|
|
stream = objt_stream(requester->owner);
|
|
if (stream == NULL)
|
|
return 0;
|
|
|
|
task_wakeup(stream->task, TASK_WOKEN_MSG);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Parse a set-timeout rule statement. It first checks if the timeout name is
|
|
* valid and returns it in <name>. Then the timeout is parsed as a plain value
|
|
* and * returned in <out_timeout>. If there is a parsing error, the value is
|
|
* reparsed as an expression and returned in <expr>.
|
|
*
|
|
* Returns -1 if the name is invalid or neither a time or an expression can be
|
|
* parsed, or if the timeout value is 0.
|
|
*/
|
|
int cfg_parse_rule_set_timeout(const char **args, int idx, int *out_timeout,
|
|
enum act_timeout_name *name,
|
|
struct sample_expr **expr, char **err,
|
|
const char *file, int line, struct arg_list *al)
|
|
{
|
|
const char *res;
|
|
const char *timeout_name = args[idx++];
|
|
|
|
if (strcmp(timeout_name, "server") == 0) {
|
|
*name = ACT_TIMEOUT_SERVER;
|
|
}
|
|
else if (strcmp(timeout_name, "tunnel") == 0) {
|
|
*name = ACT_TIMEOUT_TUNNEL;
|
|
}
|
|
else {
|
|
memprintf(err,
|
|
"'set-timeout' rule supports 'server'/'tunnel' (got '%s')",
|
|
timeout_name);
|
|
return -1;
|
|
}
|
|
|
|
res = parse_time_err(args[idx], (unsigned int *)out_timeout, TIME_UNIT_MS);
|
|
if (res == PARSE_TIME_OVER) {
|
|
memprintf(err, "timer overflow in argument '%s' to rule 'set-timeout %s' (maximum value is 2147483647 ms or ~24.8 days)",
|
|
args[idx], timeout_name);
|
|
return -1;
|
|
}
|
|
else if (res == PARSE_TIME_UNDER) {
|
|
memprintf(err, "timer underflow in argument '%s' to rule 'set-timeout %s' (minimum value is 1 ms)",
|
|
args[idx], timeout_name);
|
|
return -1;
|
|
}
|
|
/* res not NULL, parsing error */
|
|
else if (res) {
|
|
*expr = sample_parse_expr((char **)args, &idx, file, line, err, al, NULL);
|
|
if (!*expr) {
|
|
memprintf(err, "unexpected character '%c' in rule 'set-timeout %s'", *res, timeout_name);
|
|
return -1;
|
|
}
|
|
}
|
|
/* res NULL, parsing ok but value is 0 */
|
|
else if (!(*out_timeout)) {
|
|
memprintf(err, "null value is not valid for a 'set-timeout %s' rule",
|
|
timeout_name);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* tries to find in list <keywords> a similar looking action as the one in
|
|
* <word>, and returns it otherwise NULL. <word> may be NULL or empty. An
|
|
* optional array of extra words to compare may be passed in <extra>, but it
|
|
* must then be terminated by a NULL entry. If unused it may be NULL.
|
|
*/
|
|
const char *action_suggest(const char *word, const struct list *keywords, const char **extra)
|
|
{
|
|
uint8_t word_sig[1024];
|
|
uint8_t list_sig[1024];
|
|
const struct action_kw_list *kwl;
|
|
const struct action_kw *best_kw = NULL;
|
|
const char *best_ptr = NULL;
|
|
int dist, best_dist = INT_MAX;
|
|
int index;
|
|
|
|
if (!word || !*word)
|
|
return NULL;
|
|
|
|
make_word_fingerprint(word_sig, word);
|
|
list_for_each_entry(kwl, keywords, list) {
|
|
for (index = 0; kwl->kw[index].kw != NULL; index++) {
|
|
make_word_fingerprint(list_sig, kwl->kw[index].kw);
|
|
dist = word_fingerprint_distance(word_sig, list_sig);
|
|
if (dist < best_dist) {
|
|
best_dist = dist;
|
|
best_kw = &kwl->kw[index];
|
|
best_ptr = best_kw->kw;
|
|
}
|
|
}
|
|
}
|
|
|
|
while (extra && *extra) {
|
|
make_word_fingerprint(list_sig, *extra);
|
|
dist = word_fingerprint_distance(word_sig, list_sig);
|
|
if (dist < best_dist) {
|
|
best_dist = dist;
|
|
best_kw = NULL;
|
|
best_ptr = *extra;
|
|
}
|
|
extra++;
|
|
}
|
|
|
|
/* eliminate too different ones, with more tolerance for prefixes
|
|
* when they're known to exist (not from extra list).
|
|
*/
|
|
if (best_ptr &&
|
|
(best_dist > (2 + (best_kw && best_kw->match_pfx)) * strlen(word) ||
|
|
best_dist > (2 + (best_kw && best_kw->match_pfx)) * strlen(best_ptr)))
|
|
best_ptr = NULL;
|
|
|
|
return best_ptr;
|
|
}
|