MAJOR: acl: add option -m to change the pattern matching method

ACL expressions now support "-m" in addition to "-i" and "-f". This new
option is followed by the name of the pattern matching method to be used
on the extracted pattern. This makes it possible to reuse existing sample
fetch methods with other matching methods (eg: regex). A "found" matching
method ignores any pattern and only verifies that the required sample was
found (useful for cookies).
This commit is contained in:
Willy Tarreau 2013-03-31 22:13:34 +02:00
parent d76a98a5fc
commit 5adeda1f63
3 changed files with 187 additions and 0 deletions

View File

@ -8092,8 +8092,74 @@ The following ACL flags are currently supported :
-i : ignore case during matching of all subsequent patterns.
-f : load patterns from a file.
-m : changes the pattern matching method
-- : force end of flags. Useful when a string looks like one of the flags.
The "-m" flag is special. It allows the default pattern matching method to be
changed for the fetched sample. The default method depends on the keyword and
is described later in this document. When "-m" is specified and followed by a
pattern matching method name, this method is used instead. This makes it
possible to match contents in ways that were not initially planned. There are
some restrictions however. Not all methods can be used with all sample fetch
methods. Also, if "-m" is used in conjunction with "-f", it must be placed
first. The pattern matching method must be one of the following :
- "found" : only check if the requested sample could be found in the stream,
but do not compare it against any pattern. It is recommended not
to pass any pattern to avoid any confusion. This matching method
is particularly useful to detect presence of certain contents
such as headers, cookies, etc... even if they are empty and
without comparing them to anything nor counting them.
- "bool" : check the value as a boolean. It can only be applied to fetches
which return a boolean or integer value, and takes no pattern.
Value zero does not match, all other values do match.
- "int" : match the value as an integer. It can be used with integer and
boolean samples.
- "ip" : match the value as an IPv4 or IPv6 address. It is compatible
with IP addresse only.
- "bin" : match the contents against an hexadecimal string representing a
binary sequence. This may be used with binary or string samples.
- "len" : match the sample's length as an integer. This may be used with
binary or string samples.
- "str" : match the contents against a string. This may be used with
binary or string samples.
- "beg" : check that the contents begin like the provided string patterns.
This may be used with binary or string samples.
- "sub" : check that the contents contain at least one of the provided
string patterns. This may be used with binary or string samples.
- "dir" : check that a slash-delimited portion of the contents exactly
match one of the provided string patterns. This may be used with
binary or string samples.
- "dom" : check that a dot-delimited portion of the contents exactly
match one of the provided string patterns. This may be used with
binary or string samples.
- "end" : check that the contents end like the provided string patterns.
This may be used with binary or string samples.
- "reg" : match the contents against a list of regular expressions. This
may be used with binary or string samples.
For example, to quickly detect the presence of cookie "JSESSIONID" in an HTTP
request, it is possible to do :
acl jsess_present cook(JSESSIONID) -m found
In order to apply a regular expression on the 500 first bytes of data in the
buffer, one would use the following acl :
acl script_tag payload(0,500) -m reg -i <script>
The "-f" flag is special as it loads all of the lines it finds in the file
specified in argument and loads all of them before continuing. It is even
possible to pass multiple "-f" arguments if the patterns are to be loaded from

View File

@ -79,6 +79,25 @@ enum {
ACL_PAT_F_TREE = 1 << 3, /* some patterns are arranged in a tree */
};
/* ACL match methods */
enum {
ACL_MATCH_FOUND, /* just ensure that fetch found the sample */
ACL_MATCH_BOOL, /* match fetch's integer value as boolean */
ACL_MATCH_INT, /* unsigned integer (int) */
ACL_MATCH_IP, /* IPv4/IPv6 address (IP) */
ACL_MATCH_BIN, /* hex string (bin) */
ACL_MATCH_LEN, /* string length (str -> int) */
ACL_MATCH_STR, /* exact string match (str) */
ACL_MATCH_BEG, /* beginning of string (str) */
ACL_MATCH_SUB, /* substring (str) */
ACL_MATCH_DIR, /* directory-like sub-string (str) */
ACL_MATCH_DOM, /* domain-like sub-string (str) */
ACL_MATCH_END, /* end of string (str) */
ACL_MATCH_REG, /* regex (str -> reg) */
/* keep this one last */
ACL_MATCH_NUM
};
/* How to store a time range and the valid days in 29 bits */
struct acl_time {
int dow:7; /* 1 bit per day of week: 0-6 */

102
src/acl.c
View File

@ -37,6 +37,64 @@ static struct acl_kw_list acl_keywords = {
.list = LIST_HEAD_INIT(acl_keywords.list)
};
static char *acl_match_names[ACL_MATCH_NUM] = {
[ACL_MATCH_FOUND] = "found",
[ACL_MATCH_BOOL] = "bool",
[ACL_MATCH_INT] = "int",
[ACL_MATCH_IP] = "ip",
[ACL_MATCH_BIN] = "bin",
[ACL_MATCH_LEN] = "len",
[ACL_MATCH_STR] = "str",
[ACL_MATCH_BEG] = "beg",
[ACL_MATCH_SUB] = "sub",
[ACL_MATCH_DIR] = "dir",
[ACL_MATCH_DOM] = "dom",
[ACL_MATCH_END] = "end",
[ACL_MATCH_REG] = "reg",
};
static int (*acl_parse_fcts[ACL_MATCH_NUM])(const char **, struct acl_pattern *, int *, char **) = {
[ACL_MATCH_FOUND] = acl_parse_nothing,
[ACL_MATCH_BOOL] = acl_parse_nothing,
[ACL_MATCH_INT] = acl_parse_int,
[ACL_MATCH_IP] = acl_parse_ip,
[ACL_MATCH_BIN] = acl_parse_bin,
[ACL_MATCH_LEN] = acl_parse_int,
[ACL_MATCH_STR] = acl_parse_str,
[ACL_MATCH_BEG] = acl_parse_str,
[ACL_MATCH_SUB] = acl_parse_str,
[ACL_MATCH_DIR] = acl_parse_str,
[ACL_MATCH_DOM] = acl_parse_str,
[ACL_MATCH_END] = acl_parse_str,
[ACL_MATCH_REG] = acl_parse_reg,
};
static int (*acl_match_fcts[ACL_MATCH_NUM])(struct sample *, struct acl_pattern *) = {
[ACL_MATCH_FOUND] = NULL,
[ACL_MATCH_BOOL] = acl_match_nothing,
[ACL_MATCH_INT] = acl_match_int,
[ACL_MATCH_IP] = acl_match_ip,
[ACL_MATCH_BIN] = acl_match_bin,
[ACL_MATCH_LEN] = acl_match_len,
[ACL_MATCH_STR] = acl_match_str,
[ACL_MATCH_BEG] = acl_match_beg,
[ACL_MATCH_SUB] = acl_match_sub,
[ACL_MATCH_DIR] = acl_match_dir,
[ACL_MATCH_DOM] = acl_match_dom,
[ACL_MATCH_END] = acl_match_end,
[ACL_MATCH_REG] = acl_match_reg,
};
/* return the ACL_MATCH_* index for match name "name", or < 0 if not found */
static int acl_find_match_name(const char *name)
{
int i;
for (i = 0; i < ACL_MATCH_NUM; i++)
if (strcmp(name, acl_match_names[i]) == 0)
return i;
return -1;
}
/*
* These functions are exported and may be used by any other component.
@ -1082,6 +1140,7 @@ struct acl_expr *parse_acl_expr(const char **args, char **err)
/* check for options before patterns. Supported options are :
* -i : ignore case for all patterns by default
* -f : read patterns from those files
* -m : force matching method (must be used before -f)
* -- : everything after this is not an option
*/
patflags = 0;
@ -1093,6 +1152,45 @@ struct acl_expr *parse_acl_expr(const char **args, char **err)
goto out_free_expr;
args++;
}
else if ((*args)[1] == 'm') {
int idx;
if (!LIST_ISEMPTY(&expr->patterns) || !eb_is_empty(&expr->pattern_tree)) {
memprintf(err, "'-m' must only be specified before patterns and files in parsing ACL expression");
goto out_free_expr;
}
idx = acl_find_match_name(args[1]);
if (idx < 0) {
memprintf(err, "unknown matching method '%s' when parsing ACL expression", args[1]);
goto out_free_expr;
}
/* Note: -m found is always valid, bool/int are compatible, str/bin/reg/len are compatible */
if (idx == ACL_MATCH_FOUND || /* -m found */
((idx == ACL_MATCH_BOOL || idx == ACL_MATCH_INT) && /* -m bool/int */
(expr->smp->out_type == SMP_T_BOOL ||
expr->smp->out_type == SMP_T_UINT ||
expr->smp->out_type == SMP_T_SINT)) ||
(idx == ACL_MATCH_IP && /* -m ip */
(expr->smp->out_type == SMP_T_IPV4 ||
expr->smp->out_type == SMP_T_IPV6)) ||
((idx == ACL_MATCH_BIN || idx == ACL_MATCH_LEN || idx == ACL_MATCH_STR ||
idx == ACL_MATCH_BEG || idx == ACL_MATCH_SUB || idx == ACL_MATCH_DIR ||
idx == ACL_MATCH_DOM || idx == ACL_MATCH_END || idx == ACL_MATCH_REG) && /* strings */
(expr->smp->out_type == SMP_T_STR ||
expr->smp->out_type == SMP_T_BIN ||
expr->smp->out_type == SMP_T_CSTR ||
expr->smp->out_type == SMP_T_CBIN))) {
expr->parse = acl_parse_fcts[idx];
expr->match = acl_match_fcts[idx];
}
else {
memprintf(err, "matching method '%s' cannot be used with fetch keyword '%s'", args[1], expr->kw->kw);
goto out_free_expr;
}
args++;
}
else if ((*args)[1] == '-') {
args++;
break;
@ -1600,6 +1698,10 @@ int acl_exec_cond(struct acl_cond *cond, struct proxy *px, struct session *l4, v
else
acl_res |= ACL_PAT_FAIL;
}
else if (!expr->match) {
/* just check for existence */
acl_res |= ACL_PAT_PASS;
}
else {
if (!eb_is_empty(&expr->pattern_tree)) {
/* a tree is present, let's check what type it is */