2007-05-06 22:36:48 +00:00
|
|
|
/*
|
|
|
|
* ACL management functions.
|
|
|
|
*
|
MEDIUM: samples: move payload-based fetches and ACLs to their own file
The file acl.c is a real mess, it both contains functions to parse and
process ACLs, and some sample extraction functions which act on buffers.
Some other payload analysers were arbitrarily dispatched to proto_tcp.c.
So now we're moving all payload-based fetches and ACLs to payload.c
which is capable of extracting data from buffers and rely on everything
that is protocol-independant. That way we can safely inflate this file
and only use the other ones when some fetches are really specific (eg:
HTTP, SSL, ...).
As a result of this cleanup, the following new sample fetches became
available even if they're not really useful :
always_false, always_true, rep_ssl_hello_type, rdp_cookie_cnt,
req_len, req_ssl_hello_type, req_ssl_sni, req_ssl_ver, wait_end
The function 'acl_fetch_nothing' was wrong and never used anywhere so it
was removed.
The "rdp_cookie" sample fetch used to have a mandatory argument while it
was optional in ACLs, which are supposed to iterate over RDP cookies. So
we're making it optional as a fetch too, and it will return the first one.
2013-01-07 20:59:07 +00:00
|
|
|
* Copyright 2000-2013 Willy Tarreau <w@1wt.eu>
|
2007-05-06 22:36:48 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2007-06-09 21:10:04 +00:00
|
|
|
#include <ctype.h>
|
2007-05-06 22:36:48 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <common/config.h>
|
|
|
|
#include <common/mini-clist.h>
|
|
|
|
#include <common/standard.h>
|
2010-01-29 18:29:32 +00:00
|
|
|
#include <common/uri_auth.h>
|
2007-05-06 22:36:48 +00:00
|
|
|
|
2010-05-09 21:45:24 +00:00
|
|
|
#include <types/global.h>
|
|
|
|
|
2007-05-06 22:36:48 +00:00
|
|
|
#include <proto/acl.h>
|
2012-04-19 15:16:54 +00:00
|
|
|
#include <proto/arg.h>
|
2010-01-29 18:26:18 +00:00
|
|
|
#include <proto/auth.h>
|
2012-08-24 17:22:53 +00:00
|
|
|
#include <proto/channel.h>
|
2009-07-26 17:40:40 +00:00
|
|
|
#include <proto/log.h>
|
2013-11-28 10:05:19 +00:00
|
|
|
#include <proto/pattern.h>
|
2010-05-16 20:18:27 +00:00
|
|
|
#include <proto/proxy.h>
|
2013-01-11 14:49:37 +00:00
|
|
|
#include <proto/sample.h>
|
2012-04-19 17:28:33 +00:00
|
|
|
#include <proto/stick_table.h>
|
2007-05-06 22:36:48 +00:00
|
|
|
|
[MEDIUM] acl: add tree-based lookups of exact strings
Now if some ACL patterns are loaded from a file and the operation is
an exact string match, the data will be arranged in a tree, yielding
a significant performance boost on large data sets. Note that this
only works when case is sensitive.
A new dedicated function, acl_lookup_str(), has been created for this
matching. It is called for every possible input data to test and it
looks the tree up for the data. Since the keywords are loosely typed,
we would have had to add a new columns to all keywords to adjust the
function depending on the type. Instead, we just compare on the match
function. We call acl_lookup_str() when we could use acl_match_str().
The tree lookup is performed first, then the remaining patterns are
attempted if the tree returned nothing.
A quick test shows that when matching a header against a list of 52000
network names, haproxy uses 68% of one core on a core2-duo 3.2 GHz at
42000 requests per second, versus 66% without any rule, which means
only a 2% CPU increase for 52000 rules. Doing the same test without
the tree leads to 100% CPU at 6900 requests/s. Also it was possible
to run the same test at full speed with about 50 sets of 52000 rules
without any measurable performance drop.
2010-05-10 21:42:40 +00:00
|
|
|
#include <ebsttree.h>
|
|
|
|
|
2007-05-06 22:36:48 +00:00
|
|
|
/* List head of all known ACL keywords */
|
|
|
|
static struct acl_kw_list acl_keywords = {
|
|
|
|
.list = LIST_HEAD_INIT(acl_keywords.list)
|
|
|
|
};
|
|
|
|
|
2013-11-28 21:21:02 +00:00
|
|
|
/* input values are 0 or 3, output is the same */
|
2014-01-17 14:25:13 +00:00
|
|
|
static inline enum acl_test_res pat2acl(struct pattern *pat)
|
2013-11-28 21:21:02 +00:00
|
|
|
{
|
2014-01-17 14:25:13 +00:00
|
|
|
if (pat)
|
|
|
|
return ACL_TEST_PASS;
|
|
|
|
else
|
|
|
|
return ACL_TEST_FAIL;
|
2013-11-28 21:21:02 +00:00
|
|
|
}
|
|
|
|
|
2007-05-06 22:36:48 +00:00
|
|
|
/*
|
|
|
|
* Registers the ACL keyword list <kwl> as a list of valid keywords for next
|
|
|
|
* parsing sessions.
|
|
|
|
*/
|
|
|
|
void acl_register_keywords(struct acl_kw_list *kwl)
|
|
|
|
{
|
|
|
|
LIST_ADDQ(&acl_keywords.list, &kwl->list);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unregisters the ACL keyword list <kwl> from the list of valid keywords.
|
|
|
|
*/
|
|
|
|
void acl_unregister_keywords(struct acl_kw_list *kwl)
|
|
|
|
{
|
|
|
|
LIST_DEL(&kwl->list);
|
|
|
|
LIST_INIT(&kwl->list);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return a pointer to the ACL <name> within the list starting at <head>, or
|
|
|
|
* NULL if not found.
|
|
|
|
*/
|
|
|
|
struct acl *find_acl_by_name(const char *name, struct list *head)
|
|
|
|
{
|
|
|
|
struct acl *acl;
|
|
|
|
list_for_each_entry(acl, head, list) {
|
|
|
|
if (strcmp(acl->name, name) == 0)
|
|
|
|
return acl;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return a pointer to the ACL keyword <kw>, or NULL if not found. Note that if
|
2013-12-16 21:01:06 +00:00
|
|
|
* <kw> contains an opening parenthesis or a comma, only the left part of it is
|
|
|
|
* checked.
|
2007-05-06 22:36:48 +00:00
|
|
|
*/
|
|
|
|
struct acl_keyword *find_acl_kw(const char *kw)
|
|
|
|
{
|
|
|
|
int index;
|
|
|
|
const char *kwend;
|
|
|
|
struct acl_kw_list *kwl;
|
|
|
|
|
2013-12-16 21:01:06 +00:00
|
|
|
kwend = kw;
|
|
|
|
while (*kwend && *kwend != '(' && *kwend != ',')
|
|
|
|
kwend++;
|
2007-05-06 22:36:48 +00:00
|
|
|
|
|
|
|
list_for_each_entry(kwl, &acl_keywords.list, list) {
|
|
|
|
for (index = 0; kwl->kw[index].kw != NULL; index++) {
|
|
|
|
if ((strncmp(kwl->kw[index].kw, kw, kwend - kw) == 0) &&
|
|
|
|
kwl->kw[index].kw[kwend-kw] == 0)
|
|
|
|
return &kwl->kw[index];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct acl_expr *prune_acl_expr(struct acl_expr *expr)
|
|
|
|
{
|
2012-04-19 15:16:54 +00:00
|
|
|
struct arg *arg;
|
|
|
|
|
2014-01-14 15:24:51 +00:00
|
|
|
pattern_prune(&expr->pat);
|
2012-04-19 15:16:54 +00:00
|
|
|
|
2013-11-21 09:50:10 +00:00
|
|
|
for (arg = expr->smp->arg_p; arg; arg++) {
|
2012-04-19 15:16:54 +00:00
|
|
|
if (arg->type == ARGT_STOP)
|
|
|
|
break;
|
2012-06-01 08:38:29 +00:00
|
|
|
if (arg->type == ARGT_STR || arg->unresolved) {
|
2012-04-19 15:16:54 +00:00
|
|
|
free(arg->data.str.str);
|
|
|
|
arg->data.str.str = NULL;
|
2012-06-01 08:38:29 +00:00
|
|
|
arg->unresolved = 0;
|
2012-04-19 15:16:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-21 09:50:10 +00:00
|
|
|
if (expr->smp->arg_p != empty_arg_list)
|
|
|
|
free(expr->smp->arg_p);
|
2007-05-06 22:36:48 +00:00
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
2012-04-27 10:38:15 +00:00
|
|
|
/* Parse an ACL expression starting at <args>[0], and return it. If <err> is
|
|
|
|
* not NULL, it will be filled with a pointer to an error message in case of
|
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve
While ACL args were resolved after all the config was parsed, it was not the
case with sample fetch args because they're almost everywhere now.
The issue is that ACLs now solely rely on sample fetches, so their args
resolving doesn't work anymore. And many fetches involving a server, a
proxy or a userlist don't work at all.
The real issue is that at the bottom layers we have no information about
proxies, line numbers, even ACLs in order to report understandable errors,
and that at the top layers we have no visibility over the locations where
fetches are referenced (think log node).
After failing multiple unsatisfying solutions attempts, we now have a new
concept of args list. The principle is that every proxy has a list head
which contains a number of indications such as the config keyword, the
context where it's used, the file and line number, etc... and a list of
arguments. This list head is of the same type as the elements, so it
serves as a template for adding new elements. This way, it is filled from
top to bottom by the callers with the information they have (eg: line
numbers, ACL name, ...) and the lower layers just have to duplicate it and
add an element when they face an argument they cannot resolve yet.
Then at the end of the configuration parsing, a loop passes over each
proxy's list and resolves all the args in sequence. And this way there is
all necessary information to report verbose errors.
The first immediate benefit is that for the first time we got very precise
location of issues (arg number in a keyword in its context, ...). Second,
in order to do this we had to parse log-format and unique-id-format a bit
earlier, so that was a great opportunity for doing so when the directives
are encountered (unless it's a default section). This way, the recorded
line numbers for these args are the ones of the place where the log format
is declared, not the end of the file.
Userlists report slightly more information now. They're the only remaining
ones in the ACL resolving function.
2013-04-02 14:34:32 +00:00
|
|
|
* error. This pointer must be freeable or NULL. <al> is an arg_list serving
|
|
|
|
* as a list head to report missing dependencies.
|
2012-04-27 10:38:15 +00:00
|
|
|
*
|
2007-05-06 22:36:48 +00:00
|
|
|
* Right now, the only accepted syntax is :
|
|
|
|
* <subject> [<value>...]
|
|
|
|
*/
|
2014-02-11 02:31:34 +00:00
|
|
|
struct acl_expr *parse_acl_expr(const char **args, char **err, struct arg_list *al,
|
|
|
|
const char *file, int line)
|
2007-05-06 22:36:48 +00:00
|
|
|
{
|
2013-12-13 14:12:32 +00:00
|
|
|
__label__ out_return, out_free_expr;
|
2007-05-06 22:36:48 +00:00
|
|
|
struct acl_expr *expr;
|
|
|
|
struct acl_keyword *aclkw;
|
2013-12-06 18:06:43 +00:00
|
|
|
int patflags;
|
2007-05-06 22:36:48 +00:00
|
|
|
const char *arg;
|
2013-11-21 09:50:10 +00:00
|
|
|
struct sample_expr *smp = NULL;
|
|
|
|
int idx = 0;
|
|
|
|
char *ckw = NULL;
|
|
|
|
const char *begw;
|
|
|
|
const char *endw;
|
2013-12-13 00:08:36 +00:00
|
|
|
const char *endt;
|
2013-11-21 09:50:10 +00:00
|
|
|
int cur_type;
|
2013-12-13 00:08:36 +00:00
|
|
|
int nbargs;
|
2014-01-23 16:40:34 +00:00
|
|
|
int operator = STD_OP_EQ;
|
|
|
|
int op;
|
|
|
|
int contain_colon, have_dot;
|
|
|
|
const char *dot;
|
|
|
|
signed long long value, minor;
|
|
|
|
/* The following buffer contain two numbers, a ':' separator and the final \0. */
|
|
|
|
char buffer[NB_LLMAX_STR + 1 + NB_LLMAX_STR + 1];
|
2014-02-11 10:31:40 +00:00
|
|
|
int is_loaded;
|
2014-01-20 16:01:44 +00:00
|
|
|
int unique_id;
|
|
|
|
char *error;
|
|
|
|
struct pat_ref *ref;
|
|
|
|
struct pattern_expr *pattern_expr;
|
2014-01-29 13:23:29 +00:00
|
|
|
int load_as_map = 0;
|
2014-08-29 15:36:40 +00:00
|
|
|
int acl_conv_found = 0;
|
2007-05-06 22:36:48 +00:00
|
|
|
|
2013-12-13 00:24:09 +00:00
|
|
|
/* First, we look for an ACL keyword. And if we don't find one, then
|
|
|
|
* we look for a sample fetch expression starting with a sample fetch
|
|
|
|
* keyword.
|
2013-03-31 21:14:46 +00:00
|
|
|
*/
|
2013-12-13 00:08:36 +00:00
|
|
|
|
2013-12-13 00:24:09 +00:00
|
|
|
al->ctx = ARGC_ACL; // to report errors while resolving args late
|
2013-12-13 00:08:36 +00:00
|
|
|
al->kw = *args;
|
|
|
|
al->conv = NULL;
|
|
|
|
|
2007-05-06 22:36:48 +00:00
|
|
|
aclkw = find_acl_kw(args[0]);
|
2014-03-17 17:04:27 +00:00
|
|
|
if (aclkw) {
|
2013-12-13 00:24:09 +00:00
|
|
|
/* OK we have a real ACL keyword */
|
2013-06-11 19:09:06 +00:00
|
|
|
|
2013-12-13 00:08:36 +00:00
|
|
|
/* build new sample expression for this ACL */
|
2013-12-13 00:24:09 +00:00
|
|
|
smp = calloc(1, sizeof(struct sample_expr));
|
|
|
|
if (!smp) {
|
2013-11-21 09:50:10 +00:00
|
|
|
memprintf(err, "out of memory when parsing ACL expression");
|
|
|
|
goto out_return;
|
|
|
|
}
|
2013-12-13 00:24:09 +00:00
|
|
|
LIST_INIT(&(smp->conv_exprs));
|
|
|
|
smp->fetch = aclkw->smp;
|
|
|
|
smp->arg_p = empty_arg_list;
|
2013-11-21 09:50:10 +00:00
|
|
|
|
|
|
|
/* look for the begining of the subject arguments */
|
2013-12-13 00:08:36 +00:00
|
|
|
for (arg = args[0]; *arg && *arg != '(' && *arg != ','; arg++);
|
|
|
|
|
|
|
|
endt = arg;
|
|
|
|
if (*endt == '(') {
|
|
|
|
/* look for the end of this term and skip the opening parenthesis */
|
|
|
|
endt = ++arg;
|
|
|
|
while (*endt && *endt != ')')
|
|
|
|
endt++;
|
|
|
|
if (*endt != ')') {
|
2013-12-13 00:24:09 +00:00
|
|
|
memprintf(err, "missing closing ')' after arguments to ACL keyword '%s'", aclkw->kw);
|
|
|
|
goto out_free_smp;
|
2013-12-13 00:08:36 +00:00
|
|
|
}
|
|
|
|
}
|
2013-11-21 09:50:10 +00:00
|
|
|
|
2013-12-13 00:08:36 +00:00
|
|
|
/* At this point, we have :
|
|
|
|
* - args[0] : beginning of the keyword
|
|
|
|
* - arg : end of the keyword, first character not part of keyword
|
|
|
|
* nor the opening parenthesis (so first character of args
|
|
|
|
* if present).
|
|
|
|
* - endt : end of the term (=arg or last parenthesis if args are present)
|
|
|
|
*/
|
2013-12-13 00:24:09 +00:00
|
|
|
nbargs = make_arg_list(arg, endt - arg, smp->fetch->arg_mask, &smp->arg_p,
|
2013-12-13 00:08:36 +00:00
|
|
|
err, NULL, NULL, al);
|
|
|
|
if (nbargs < 0) {
|
|
|
|
/* note that make_arg_list will have set <err> here */
|
2013-12-13 00:24:09 +00:00
|
|
|
memprintf(err, "ACL keyword '%s' : %s", aclkw->kw, *err);
|
|
|
|
goto out_free_smp;
|
2013-12-13 00:08:36 +00:00
|
|
|
}
|
2013-11-21 09:50:10 +00:00
|
|
|
|
2013-12-13 00:24:09 +00:00
|
|
|
if (!smp->arg_p) {
|
|
|
|
smp->arg_p = empty_arg_list;
|
2013-12-13 00:08:36 +00:00
|
|
|
}
|
2013-12-13 00:24:09 +00:00
|
|
|
else if (smp->fetch->val_args && !smp->fetch->val_args(smp->arg_p, err)) {
|
2013-12-13 00:08:36 +00:00
|
|
|
/* invalid keyword argument, error must have been
|
|
|
|
* set by val_args().
|
|
|
|
*/
|
2013-12-13 00:24:09 +00:00
|
|
|
memprintf(err, "in argument to '%s', %s", aclkw->kw, *err);
|
|
|
|
goto out_free_smp;
|
2013-12-13 00:08:36 +00:00
|
|
|
}
|
|
|
|
arg = endt;
|
2013-11-21 09:50:10 +00:00
|
|
|
|
2013-12-13 00:08:36 +00:00
|
|
|
/* look for the begining of the converters list. Those directly attached
|
|
|
|
* to the ACL keyword are found just after <arg> which points to the comma.
|
2014-08-29 15:36:40 +00:00
|
|
|
* If we find any converter, then we don't use the ACL keyword's match
|
2014-08-29 17:09:48 +00:00
|
|
|
* anymore but the one related to the converter's output type.
|
2013-12-13 00:08:36 +00:00
|
|
|
*/
|
2014-08-29 17:09:48 +00:00
|
|
|
cur_type = smp->fetch->out_type;
|
2013-12-13 00:08:36 +00:00
|
|
|
while (*arg) {
|
|
|
|
struct sample_conv *conv;
|
|
|
|
struct sample_conv_expr *conv_expr;
|
2013-11-21 09:50:10 +00:00
|
|
|
|
2013-12-13 00:08:36 +00:00
|
|
|
if (*arg == ')') /* skip last closing parenthesis */
|
|
|
|
arg++;
|
2012-10-19 17:49:09 +00:00
|
|
|
|
2013-12-13 00:08:36 +00:00
|
|
|
if (*arg && *arg != ',') {
|
|
|
|
if (ckw)
|
|
|
|
memprintf(err, "ACL keyword '%s' : missing comma after conv keyword '%s'.",
|
2013-12-13 00:24:09 +00:00
|
|
|
aclkw->kw, ckw);
|
2013-12-13 00:08:36 +00:00
|
|
|
else
|
|
|
|
memprintf(err, "ACL keyword '%s' : missing comma after fetch keyword.",
|
2013-12-13 00:24:09 +00:00
|
|
|
aclkw->kw);
|
|
|
|
goto out_free_smp;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
2013-12-13 00:08:36 +00:00
|
|
|
|
|
|
|
while (*arg == ',') /* then trailing commas */
|
|
|
|
arg++;
|
|
|
|
|
|
|
|
begw = arg; /* start of conv keyword */
|
|
|
|
|
|
|
|
if (!*begw)
|
|
|
|
/* none ? end of converters */
|
|
|
|
break;
|
|
|
|
|
|
|
|
for (endw = begw; *endw && *endw != '(' && *endw != ','; endw++);
|
|
|
|
|
|
|
|
free(ckw);
|
|
|
|
ckw = my_strndup(begw, endw - begw);
|
|
|
|
|
|
|
|
conv = find_sample_conv(begw, endw - begw);
|
|
|
|
if (!conv) {
|
|
|
|
/* Unknown converter method */
|
|
|
|
memprintf(err, "ACL keyword '%s' : unknown conv method '%s'.",
|
2013-12-13 00:24:09 +00:00
|
|
|
aclkw->kw, ckw);
|
|
|
|
goto out_free_smp;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
2013-11-21 09:50:10 +00:00
|
|
|
|
2013-12-13 00:08:36 +00:00
|
|
|
arg = endw;
|
|
|
|
if (*arg == '(') {
|
|
|
|
/* look for the end of this term */
|
|
|
|
while (*arg && *arg != ')')
|
2013-11-21 09:50:10 +00:00
|
|
|
arg++;
|
2013-12-13 00:08:36 +00:00
|
|
|
if (*arg != ')') {
|
|
|
|
memprintf(err, "ACL keyword '%s' : syntax error: missing ')' after conv keyword '%s'.",
|
2013-12-13 00:24:09 +00:00
|
|
|
aclkw->kw, ckw);
|
|
|
|
goto out_free_smp;
|
2013-11-21 09:50:10 +00:00
|
|
|
}
|
2013-12-13 00:08:36 +00:00
|
|
|
}
|
2013-11-21 09:50:10 +00:00
|
|
|
|
2013-12-13 00:08:36 +00:00
|
|
|
if (conv->in_type >= SMP_TYPES || conv->out_type >= SMP_TYPES) {
|
|
|
|
memprintf(err, "ACL keyword '%s' : returns type of conv method '%s' is unknown.",
|
2013-12-13 00:24:09 +00:00
|
|
|
aclkw->kw, ckw);
|
|
|
|
goto out_free_smp;
|
2013-12-13 00:08:36 +00:00
|
|
|
}
|
2013-11-21 09:50:10 +00:00
|
|
|
|
2013-12-13 00:08:36 +00:00
|
|
|
/* If impossible type conversion */
|
2014-08-29 17:09:48 +00:00
|
|
|
if (!sample_casts[cur_type][conv->in_type]) {
|
2013-12-13 00:08:36 +00:00
|
|
|
memprintf(err, "ACL keyword '%s' : conv method '%s' cannot be applied.",
|
2013-12-13 00:24:09 +00:00
|
|
|
aclkw->kw, ckw);
|
|
|
|
goto out_free_smp;
|
2013-12-13 00:08:36 +00:00
|
|
|
}
|
2013-11-21 09:50:10 +00:00
|
|
|
|
2014-08-29 17:09:48 +00:00
|
|
|
cur_type = conv->out_type;
|
2013-12-13 00:08:36 +00:00
|
|
|
conv_expr = calloc(1, sizeof(struct sample_conv_expr));
|
|
|
|
if (!conv_expr)
|
2013-12-13 00:24:09 +00:00
|
|
|
goto out_free_smp;
|
2013-11-21 09:50:10 +00:00
|
|
|
|
2013-12-13 00:24:09 +00:00
|
|
|
LIST_ADDQ(&(smp->conv_exprs), &(conv_expr->list));
|
2013-12-13 00:08:36 +00:00
|
|
|
conv_expr->conv = conv;
|
2014-08-29 15:36:40 +00:00
|
|
|
acl_conv_found = 1;
|
2013-11-21 09:50:10 +00:00
|
|
|
|
2013-12-13 00:08:36 +00:00
|
|
|
if (arg != endw) {
|
|
|
|
int err_arg;
|
2013-11-21 09:50:10 +00:00
|
|
|
|
2013-12-13 00:08:36 +00:00
|
|
|
if (!conv->arg_mask) {
|
|
|
|
memprintf(err, "ACL keyword '%s' : conv method '%s' does not support any args.",
|
2013-12-13 00:24:09 +00:00
|
|
|
aclkw->kw, ckw);
|
|
|
|
goto out_free_smp;
|
2013-11-21 09:50:10 +00:00
|
|
|
}
|
|
|
|
|
2013-12-13 00:24:09 +00:00
|
|
|
al->kw = smp->fetch->kw;
|
2013-12-13 00:08:36 +00:00
|
|
|
al->conv = conv_expr->conv->kw;
|
2013-12-13 00:30:22 +00:00
|
|
|
if (make_arg_list(endw + 1, arg - endw - 1, conv->arg_mask, &conv_expr->arg_p, err, NULL, &err_arg, al) < 0) {
|
2013-12-13 00:08:36 +00:00
|
|
|
memprintf(err, "ACL keyword '%s' : invalid arg %d in conv method '%s' : %s.",
|
2013-12-13 00:30:22 +00:00
|
|
|
aclkw->kw, err_arg+1, ckw, *err);
|
2013-12-13 00:24:09 +00:00
|
|
|
goto out_free_smp;
|
2013-11-21 09:50:10 +00:00
|
|
|
}
|
|
|
|
|
2013-12-13 00:08:36 +00:00
|
|
|
if (!conv_expr->arg_p)
|
|
|
|
conv_expr->arg_p = empty_arg_list;
|
2013-11-21 09:50:10 +00:00
|
|
|
|
2014-02-11 13:00:19 +00:00
|
|
|
if (conv->val_args && !conv->val_args(conv_expr->arg_p, conv, file, line, err)) {
|
2013-12-13 00:08:36 +00:00
|
|
|
memprintf(err, "ACL keyword '%s' : invalid args in conv method '%s' : %s.",
|
2013-12-13 00:30:22 +00:00
|
|
|
aclkw->kw, ckw, *err);
|
2013-12-13 00:24:09 +00:00
|
|
|
goto out_free_smp;
|
2013-11-21 09:50:10 +00:00
|
|
|
}
|
|
|
|
}
|
2013-12-13 00:08:36 +00:00
|
|
|
else if (ARGM(conv->arg_mask)) {
|
|
|
|
memprintf(err, "ACL keyword '%s' : missing args for conv method '%s'.",
|
2013-12-13 00:24:09 +00:00
|
|
|
aclkw->kw, ckw);
|
|
|
|
goto out_free_smp;
|
2013-12-13 00:08:36 +00:00
|
|
|
}
|
2012-04-19 16:42:05 +00:00
|
|
|
}
|
2007-05-06 22:36:48 +00:00
|
|
|
}
|
2013-12-13 00:24:09 +00:00
|
|
|
else {
|
|
|
|
/* This is not an ACL keyword, so we hope this is a sample fetch
|
|
|
|
* keyword that we're going to transparently use as an ACL. If
|
|
|
|
* so, we retrieve a completely parsed expression with args and
|
|
|
|
* convs already done.
|
|
|
|
*/
|
2014-02-11 13:00:19 +00:00
|
|
|
smp = sample_parse_expr((char **)args, &idx, file, line, err, al);
|
2013-12-13 00:24:09 +00:00
|
|
|
if (!smp) {
|
|
|
|
memprintf(err, "%s in ACL expression '%s'", *err, *args);
|
|
|
|
goto out_return;
|
|
|
|
}
|
2014-08-29 17:09:48 +00:00
|
|
|
cur_type = smp_expr_output_type(smp);
|
2013-12-13 00:24:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
expr = (struct acl_expr *)calloc(1, sizeof(*expr));
|
|
|
|
if (!expr) {
|
|
|
|
memprintf(err, "out of memory when parsing ACL expression");
|
|
|
|
goto out_return;
|
|
|
|
}
|
|
|
|
|
2014-02-11 10:31:40 +00:00
|
|
|
pattern_init_head(&expr->pat);
|
2013-12-13 00:24:09 +00:00
|
|
|
|
2014-08-29 17:09:48 +00:00
|
|
|
expr->pat.expect_type = cur_type;
|
|
|
|
expr->smp = smp;
|
|
|
|
expr->kw = smp->fetch->kw;
|
|
|
|
smp = NULL; /* don't free it anymore */
|
|
|
|
|
|
|
|
if (aclkw && !acl_conv_found) {
|
|
|
|
expr->kw = aclkw->kw;
|
|
|
|
expr->pat.parse = aclkw->parse ? aclkw->parse : pat_parse_fcts[aclkw->match_type];
|
|
|
|
expr->pat.index = aclkw->index ? aclkw->index : pat_index_fcts[aclkw->match_type];
|
|
|
|
expr->pat.match = aclkw->match ? aclkw->match : pat_match_fcts[aclkw->match_type];
|
|
|
|
expr->pat.delete = aclkw->delete ? aclkw->delete : pat_delete_fcts[aclkw->match_type];
|
|
|
|
expr->pat.prune = aclkw->prune ? aclkw->prune : pat_prune_fcts[aclkw->match_type];
|
2014-03-05 15:07:08 +00:00
|
|
|
}
|
|
|
|
|
2013-12-13 00:24:09 +00:00
|
|
|
if (!expr->pat.parse) {
|
2014-08-29 17:09:48 +00:00
|
|
|
/* Parse/index/match functions depend on the expression type,
|
|
|
|
* so we have to map them now. Some types can be automatically
|
|
|
|
* converted.
|
|
|
|
*/
|
|
|
|
switch (cur_type) {
|
2013-12-13 00:24:09 +00:00
|
|
|
case SMP_T_BOOL:
|
|
|
|
expr->pat.parse = pat_parse_fcts[PAT_MATCH_BOOL];
|
2013-12-13 14:12:32 +00:00
|
|
|
expr->pat.index = pat_index_fcts[PAT_MATCH_BOOL];
|
2013-12-13 00:24:09 +00:00
|
|
|
expr->pat.match = pat_match_fcts[PAT_MATCH_BOOL];
|
2014-01-15 10:38:49 +00:00
|
|
|
expr->pat.delete = pat_delete_fcts[PAT_MATCH_BOOL];
|
2014-01-14 15:24:51 +00:00
|
|
|
expr->pat.prune = pat_prune_fcts[PAT_MATCH_BOOL];
|
2014-01-27 13:19:53 +00:00
|
|
|
expr->pat.expect_type = pat_match_types[PAT_MATCH_BOOL];
|
2013-12-13 00:24:09 +00:00
|
|
|
break;
|
|
|
|
case SMP_T_SINT:
|
|
|
|
case SMP_T_UINT:
|
|
|
|
expr->pat.parse = pat_parse_fcts[PAT_MATCH_INT];
|
2013-12-13 14:12:32 +00:00
|
|
|
expr->pat.index = pat_index_fcts[PAT_MATCH_INT];
|
2013-12-13 00:24:09 +00:00
|
|
|
expr->pat.match = pat_match_fcts[PAT_MATCH_INT];
|
2014-01-15 10:38:49 +00:00
|
|
|
expr->pat.delete = pat_delete_fcts[PAT_MATCH_INT];
|
2014-01-14 15:24:51 +00:00
|
|
|
expr->pat.prune = pat_prune_fcts[PAT_MATCH_INT];
|
2014-01-27 13:19:53 +00:00
|
|
|
expr->pat.expect_type = pat_match_types[PAT_MATCH_INT];
|
2013-12-13 00:24:09 +00:00
|
|
|
break;
|
|
|
|
case SMP_T_IPV4:
|
|
|
|
case SMP_T_IPV6:
|
|
|
|
expr->pat.parse = pat_parse_fcts[PAT_MATCH_IP];
|
2013-12-13 14:12:32 +00:00
|
|
|
expr->pat.index = pat_index_fcts[PAT_MATCH_IP];
|
2013-12-13 00:24:09 +00:00
|
|
|
expr->pat.match = pat_match_fcts[PAT_MATCH_IP];
|
2014-01-15 10:38:49 +00:00
|
|
|
expr->pat.delete = pat_delete_fcts[PAT_MATCH_IP];
|
2014-01-14 15:24:51 +00:00
|
|
|
expr->pat.prune = pat_prune_fcts[PAT_MATCH_IP];
|
2014-01-27 13:19:53 +00:00
|
|
|
expr->pat.expect_type = pat_match_types[PAT_MATCH_IP];
|
2013-12-13 00:24:09 +00:00
|
|
|
break;
|
2014-05-11 13:15:00 +00:00
|
|
|
case SMP_T_STR:
|
|
|
|
expr->pat.parse = pat_parse_fcts[PAT_MATCH_STR];
|
|
|
|
expr->pat.index = pat_index_fcts[PAT_MATCH_STR];
|
|
|
|
expr->pat.match = pat_match_fcts[PAT_MATCH_STR];
|
|
|
|
expr->pat.delete = pat_delete_fcts[PAT_MATCH_STR];
|
|
|
|
expr->pat.prune = pat_prune_fcts[PAT_MATCH_STR];
|
|
|
|
expr->pat.expect_type = pat_match_types[PAT_MATCH_STR];
|
|
|
|
break;
|
2013-12-13 00:24:09 +00:00
|
|
|
}
|
|
|
|
}
|
2007-05-06 22:36:48 +00:00
|
|
|
|
2013-11-04 17:09:12 +00:00
|
|
|
/* Additional check to protect against common mistakes */
|
2013-11-28 10:41:23 +00:00
|
|
|
if (expr->pat.parse && cur_type != SMP_T_BOOL && !*args[1]) {
|
2013-11-04 17:09:12 +00:00
|
|
|
Warning("parsing acl keyword '%s' :\n"
|
|
|
|
" no pattern to match against were provided, so this ACL will never match.\n"
|
|
|
|
" If this is what you intended, please add '--' to get rid of this warning.\n"
|
|
|
|
" If you intended to match only for existence, please use '-m found'.\n"
|
|
|
|
" If you wanted to force an int to match as a bool, please use '-m bool'.\n"
|
|
|
|
"\n",
|
|
|
|
args[0]);
|
|
|
|
}
|
|
|
|
|
2007-05-06 22:36:48 +00:00
|
|
|
args++;
|
2007-06-17 06:20:33 +00:00
|
|
|
|
|
|
|
/* check for options before patterns. Supported options are :
|
|
|
|
* -i : ignore case for all patterns by default
|
|
|
|
* -f : read patterns from those files
|
2013-03-31 20:13:34 +00:00
|
|
|
* -m : force matching method (must be used before -f)
|
2014-01-29 13:23:29 +00:00
|
|
|
* -M : load the file as map file
|
2014-01-20 16:01:44 +00:00
|
|
|
* -u : force the unique id of the acl
|
2007-06-17 06:20:33 +00:00
|
|
|
* -- : everything after this is not an option
|
|
|
|
*/
|
|
|
|
patflags = 0;
|
2014-02-11 10:31:40 +00:00
|
|
|
is_loaded = 0;
|
2014-01-20 16:01:44 +00:00
|
|
|
unique_id = -1;
|
2007-06-17 06:20:33 +00:00
|
|
|
while (**args == '-') {
|
2014-05-11 07:43:46 +00:00
|
|
|
if (strcmp(*args, "-i") == 0)
|
2014-04-28 09:18:57 +00:00
|
|
|
patflags |= PAT_MF_IGNORE_CASE;
|
2014-05-11 07:43:46 +00:00
|
|
|
else if (strcmp(*args, "-n") == 0)
|
2014-04-28 09:18:57 +00:00
|
|
|
patflags |= PAT_MF_NO_DNS;
|
2014-05-11 07:43:46 +00:00
|
|
|
else if (strcmp(*args, "-u") == 0) {
|
2014-01-20 16:01:44 +00:00
|
|
|
unique_id = strtol(args[1], &error, 10);
|
|
|
|
if (*error != '\0') {
|
|
|
|
memprintf(err, "the argument of -u must be an integer");
|
|
|
|
goto out_free_expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if this id is really unique. */
|
|
|
|
if (pat_ref_lookupid(unique_id)) {
|
|
|
|
memprintf(err, "the id is already used");
|
|
|
|
goto out_free_expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
args++;
|
|
|
|
}
|
2014-05-11 07:43:46 +00:00
|
|
|
else if (strcmp(*args, "-f") == 0) {
|
2013-11-28 10:41:23 +00:00
|
|
|
if (!expr->pat.parse) {
|
2013-06-11 19:09:06 +00:00
|
|
|
memprintf(err, "matching method must be specified first (using '-m') when using a sample fetch of this type ('%s')", expr->kw);
|
2013-03-31 21:14:46 +00:00
|
|
|
goto out_free_expr;
|
|
|
|
}
|
|
|
|
|
2014-02-11 15:19:46 +00:00
|
|
|
if (!pattern_read_from_file(&expr->pat, PAT_REF_ACL, args[1], patflags, load_as_map, err, file, line))
|
2010-05-09 21:45:24 +00:00
|
|
|
goto out_free_expr;
|
2014-02-11 10:31:40 +00:00
|
|
|
is_loaded = 1;
|
2010-05-09 21:45:24 +00:00
|
|
|
args++;
|
|
|
|
}
|
2014-05-11 07:43:46 +00:00
|
|
|
else if (strcmp(*args, "-m") == 0) {
|
2013-03-31 20:13:34 +00:00
|
|
|
int idx;
|
|
|
|
|
2014-02-11 10:31:40 +00:00
|
|
|
if (is_loaded) {
|
2013-03-31 20:13:34 +00:00
|
|
|
memprintf(err, "'-m' must only be specified before patterns and files in parsing ACL expression");
|
|
|
|
goto out_free_expr;
|
|
|
|
}
|
|
|
|
|
2013-11-28 21:24:25 +00:00
|
|
|
idx = pat_find_match_name(args[1]);
|
2013-03-31 20:13:34 +00:00
|
|
|
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 */
|
2013-12-06 14:36:54 +00:00
|
|
|
if (!sample_casts[cur_type][pat_match_types[idx]]) {
|
2013-03-31 20:59:32 +00:00
|
|
|
memprintf(err, "matching method '%s' cannot be used with fetch keyword '%s'", args[1], expr->kw);
|
2013-03-31 20:13:34 +00:00
|
|
|
goto out_free_expr;
|
|
|
|
}
|
2013-12-06 14:36:54 +00:00
|
|
|
expr->pat.parse = pat_parse_fcts[idx];
|
2013-12-13 14:12:32 +00:00
|
|
|
expr->pat.index = pat_index_fcts[idx];
|
2013-12-06 14:36:54 +00:00
|
|
|
expr->pat.match = pat_match_fcts[idx];
|
2014-01-15 10:38:49 +00:00
|
|
|
expr->pat.delete = pat_delete_fcts[idx];
|
2014-01-14 15:24:51 +00:00
|
|
|
expr->pat.prune = pat_prune_fcts[idx];
|
2014-01-27 13:19:53 +00:00
|
|
|
expr->pat.expect_type = pat_match_types[idx];
|
2013-03-31 20:13:34 +00:00
|
|
|
args++;
|
|
|
|
}
|
2014-05-11 07:43:46 +00:00
|
|
|
else if (strcmp(*args, "-M") == 0) {
|
2014-01-29 13:23:29 +00:00
|
|
|
load_as_map = 1;
|
|
|
|
}
|
2014-05-11 07:43:46 +00:00
|
|
|
else if (strcmp(*args, "--") == 0) {
|
2007-06-17 06:20:33 +00:00
|
|
|
args++;
|
|
|
|
break;
|
|
|
|
}
|
2014-05-11 07:43:46 +00:00
|
|
|
else {
|
|
|
|
memprintf(err, "'%s' is not a valid ACL option. Please use '--' before any pattern beginning with a '-'", args[0]);
|
|
|
|
goto out_free_expr;
|
2007-06-17 06:20:33 +00:00
|
|
|
break;
|
2014-05-11 07:43:46 +00:00
|
|
|
}
|
2007-06-17 06:20:33 +00:00
|
|
|
args++;
|
|
|
|
}
|
|
|
|
|
2013-11-28 10:41:23 +00:00
|
|
|
if (!expr->pat.parse) {
|
2013-06-11 19:09:06 +00:00
|
|
|
memprintf(err, "matching method must be specified first (using '-m') when using a sample fetch of this type ('%s')", expr->kw);
|
2013-03-31 21:14:46 +00:00
|
|
|
goto out_free_expr;
|
|
|
|
}
|
|
|
|
|
2014-02-11 02:31:34 +00:00
|
|
|
/* Create displayed reference */
|
|
|
|
snprintf(trash.str, trash.size, "acl '%s' file '%s' line %d", expr->kw, file, line);
|
|
|
|
trash.str[trash.size - 1] = '\0';
|
|
|
|
|
2014-01-20 16:01:44 +00:00
|
|
|
/* Create new patern reference. */
|
2014-02-11 02:31:34 +00:00
|
|
|
ref = pat_ref_newid(unique_id, trash.str, PAT_REF_ACL);
|
2014-01-20 16:01:44 +00:00
|
|
|
if (!ref) {
|
|
|
|
memprintf(err, "memory error");
|
|
|
|
goto out_free_expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create new pattern expression associated to this reference. */
|
2014-11-24 10:14:42 +00:00
|
|
|
pattern_expr = pattern_new_expr(&expr->pat, ref, err, NULL);
|
2014-01-20 16:01:44 +00:00
|
|
|
if (!pattern_expr)
|
|
|
|
goto out_free_expr;
|
|
|
|
|
2014-04-28 09:18:57 +00:00
|
|
|
/* Copy the pattern matching and indexing flags. */
|
|
|
|
pattern_expr->mflags = patflags;
|
|
|
|
|
2007-06-17 06:20:33 +00:00
|
|
|
/* now parse all patterns */
|
2014-01-23 16:40:34 +00:00
|
|
|
while (**args) {
|
|
|
|
arg = *args;
|
|
|
|
|
|
|
|
/* Compatibility layer. Each pattern can parse only one string per pattern,
|
|
|
|
* but the pat_parser_int() and pat_parse_dotted_ver() parsers were need
|
|
|
|
* optionnaly two operators. The first operator is the match method: eq,
|
|
|
|
* le, lt, ge and gt. pat_parse_int() and pat_parse_dotted_ver() functions
|
|
|
|
* can have a compatibility syntax based on ranges:
|
|
|
|
*
|
|
|
|
* pat_parse_int():
|
|
|
|
*
|
|
|
|
* "eq x" -> "x" or "x:x"
|
|
|
|
* "le x" -> ":x"
|
|
|
|
* "lt x" -> ":y" (with y = x - 1)
|
|
|
|
* "ge x" -> "x:"
|
|
|
|
* "gt x" -> "y:" (with y = x + 1)
|
|
|
|
*
|
|
|
|
* pat_parse_dotted_ver():
|
|
|
|
*
|
|
|
|
* "eq x.y" -> "x.y" or "x.y:x.y"
|
|
|
|
* "le x.y" -> ":x.y"
|
|
|
|
* "lt x.y" -> ":w.z" (with w.z = x.y - 1)
|
|
|
|
* "ge x.y" -> "x.y:"
|
|
|
|
* "gt x.y" -> "w.z:" (with w.z = x.y + 1)
|
|
|
|
*
|
|
|
|
* If y is not present, assume that is "0".
|
|
|
|
*
|
|
|
|
* The syntax eq, le, lt, ge and gt are proper to the acl syntax. The
|
|
|
|
* following block of code detect the operator, and rewrite each value
|
|
|
|
* in parsable string.
|
|
|
|
*/
|
|
|
|
if (expr->pat.parse == pat_parse_int ||
|
|
|
|
expr->pat.parse == pat_parse_dotted_ver) {
|
|
|
|
/* Check for operator. If the argument is operator, memorise it and
|
|
|
|
* continue to the next argument.
|
|
|
|
*/
|
|
|
|
op = get_std_op(arg);
|
|
|
|
if (op != -1) {
|
|
|
|
operator = op;
|
|
|
|
args++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if the pattern contain ':' or '-' character. */
|
|
|
|
contain_colon = (strchr(arg, ':') || strchr(arg, '-'));
|
|
|
|
|
|
|
|
/* If the pattern contain ':' or '-' character, give it to the parser as is.
|
|
|
|
* If no contain ':' and operator is STD_OP_EQ, give it to the parser as is.
|
|
|
|
* In other case, try to convert the value according with the operator.
|
|
|
|
*/
|
|
|
|
if (!contain_colon && operator != STD_OP_EQ) {
|
|
|
|
/* Search '.' separator. */
|
|
|
|
dot = strchr(arg, '.');
|
|
|
|
if (!dot) {
|
|
|
|
have_dot = 0;
|
|
|
|
minor = 0;
|
|
|
|
dot = arg + strlen(arg);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
have_dot = 1;
|
|
|
|
|
|
|
|
/* convert the integer minor part for the pat_parse_dotted_ver() function. */
|
|
|
|
if (expr->pat.parse == pat_parse_dotted_ver && have_dot) {
|
|
|
|
if (strl2llrc(dot+1, strlen(dot+1), &minor) != 0) {
|
|
|
|
memprintf(err, "'%s' is neither a number nor a supported operator", arg);
|
2013-12-13 14:12:32 +00:00
|
|
|
goto out_free_expr;
|
2014-01-23 16:40:34 +00:00
|
|
|
}
|
|
|
|
if (minor >= 65536) {
|
|
|
|
memprintf(err, "'%s' contains too large a minor value", arg);
|
2013-12-13 14:12:32 +00:00
|
|
|
goto out_free_expr;
|
2014-01-23 16:40:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* convert the integer value for the pat_parse_int() function, and the
|
|
|
|
* integer major part for the pat_parse_dotted_ver() function.
|
|
|
|
*/
|
|
|
|
if (strl2llrc(arg, dot - arg, &value) != 0) {
|
|
|
|
memprintf(err, "'%s' is neither a number nor a supported operator", arg);
|
2013-12-13 14:12:32 +00:00
|
|
|
goto out_free_expr;
|
2014-01-23 16:40:34 +00:00
|
|
|
}
|
|
|
|
if (expr->pat.parse == pat_parse_dotted_ver) {
|
|
|
|
if (value >= 65536) {
|
|
|
|
memprintf(err, "'%s' contains too large a major value", arg);
|
2013-12-13 14:12:32 +00:00
|
|
|
goto out_free_expr;
|
2014-01-23 16:40:34 +00:00
|
|
|
}
|
|
|
|
value = (value << 16) | (minor & 0xffff);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (operator) {
|
|
|
|
|
|
|
|
case STD_OP_EQ: /* this case is not possible. */
|
|
|
|
memprintf(err, "internal error");
|
2013-12-13 14:12:32 +00:00
|
|
|
goto out_free_expr;
|
2014-01-23 16:40:34 +00:00
|
|
|
|
|
|
|
case STD_OP_GT:
|
|
|
|
value++; /* gt = ge + 1 */
|
|
|
|
|
|
|
|
case STD_OP_GE:
|
|
|
|
if (expr->pat.parse == pat_parse_int)
|
|
|
|
snprintf(buffer, NB_LLMAX_STR+NB_LLMAX_STR+2, "%lld:", value);
|
|
|
|
else
|
|
|
|
snprintf(buffer, NB_LLMAX_STR+NB_LLMAX_STR+2, "%lld.%lld:",
|
|
|
|
value >> 16, value & 0xffff);
|
|
|
|
arg = buffer;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STD_OP_LT:
|
|
|
|
value--; /* lt = le - 1 */
|
|
|
|
|
|
|
|
case STD_OP_LE:
|
|
|
|
if (expr->pat.parse == pat_parse_int)
|
|
|
|
snprintf(buffer, NB_LLMAX_STR+NB_LLMAX_STR+2, ":%lld", value);
|
|
|
|
else
|
|
|
|
snprintf(buffer, NB_LLMAX_STR+NB_LLMAX_STR+2, ":%lld.%lld",
|
|
|
|
value >> 16, value & 0xffff);
|
|
|
|
arg = buffer;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-20 16:01:44 +00:00
|
|
|
/* Add sample to the reference, and try to compile it fior each pattern
|
|
|
|
* using this value.
|
|
|
|
*/
|
2014-04-28 09:18:57 +00:00
|
|
|
if (!pat_ref_add(ref, arg, NULL, err))
|
2013-12-13 14:12:32 +00:00
|
|
|
goto out_free_expr;
|
2014-01-23 16:40:34 +00:00
|
|
|
args++;
|
|
|
|
}
|
2007-05-06 22:36:48 +00:00
|
|
|
|
|
|
|
return expr;
|
|
|
|
|
|
|
|
out_free_expr:
|
|
|
|
prune_acl_expr(expr);
|
|
|
|
free(expr);
|
2013-11-21 09:50:10 +00:00
|
|
|
free(ckw);
|
2013-12-13 00:24:09 +00:00
|
|
|
out_free_smp:
|
|
|
|
free(smp);
|
2007-05-06 22:36:48 +00:00
|
|
|
out_return:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 21:53:44 +00:00
|
|
|
/* Purge everything in the acl <acl>, then return <acl>. */
|
|
|
|
struct acl *prune_acl(struct acl *acl) {
|
|
|
|
|
|
|
|
struct acl_expr *expr, *exprb;
|
|
|
|
|
|
|
|
free(acl->name);
|
|
|
|
|
|
|
|
list_for_each_entry_safe(expr, exprb, &acl->expr, list) {
|
|
|
|
LIST_DEL(&expr->list);
|
|
|
|
prune_acl_expr(expr);
|
|
|
|
free(expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return acl;
|
|
|
|
}
|
|
|
|
|
2007-05-06 22:36:48 +00:00
|
|
|
/* Parse an ACL with the name starting at <args>[0], and with a list of already
|
|
|
|
* known ACLs in <acl>. If the ACL was not in the list, it will be added.
|
2010-03-15 15:13:29 +00:00
|
|
|
* A pointer to that ACL is returned. If the ACL has an empty name, then it's
|
2012-04-27 10:38:15 +00:00
|
|
|
* an anonymous one and it won't be merged with any other one. If <err> is not
|
|
|
|
* NULL, it will be filled with an appropriate error. This pointer must be
|
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve
While ACL args were resolved after all the config was parsed, it was not the
case with sample fetch args because they're almost everywhere now.
The issue is that ACLs now solely rely on sample fetches, so their args
resolving doesn't work anymore. And many fetches involving a server, a
proxy or a userlist don't work at all.
The real issue is that at the bottom layers we have no information about
proxies, line numbers, even ACLs in order to report understandable errors,
and that at the top layers we have no visibility over the locations where
fetches are referenced (think log node).
After failing multiple unsatisfying solutions attempts, we now have a new
concept of args list. The principle is that every proxy has a list head
which contains a number of indications such as the config keyword, the
context where it's used, the file and line number, etc... and a list of
arguments. This list head is of the same type as the elements, so it
serves as a template for adding new elements. This way, it is filled from
top to bottom by the callers with the information they have (eg: line
numbers, ACL name, ...) and the lower layers just have to duplicate it and
add an element when they face an argument they cannot resolve yet.
Then at the end of the configuration parsing, a loop passes over each
proxy's list and resolves all the args in sequence. And this way there is
all necessary information to report verbose errors.
The first immediate benefit is that for the first time we got very precise
location of issues (arg number in a keyword in its context, ...). Second,
in order to do this we had to parse log-format and unique-id-format a bit
earlier, so that was a great opportunity for doing so when the directives
are encountered (unless it's a default section). This way, the recorded
line numbers for these args are the ones of the place where the log format
is declared, not the end of the file.
Userlists report slightly more information now. They're the only remaining
ones in the ACL resolving function.
2013-04-02 14:34:32 +00:00
|
|
|
* freeable or NULL. <al> is the arg_list serving as a head for unresolved
|
|
|
|
* dependencies.
|
2007-05-06 22:36:48 +00:00
|
|
|
*
|
|
|
|
* args syntax: <aclname> <acl_expr>
|
|
|
|
*/
|
2014-02-11 02:31:34 +00:00
|
|
|
struct acl *parse_acl(const char **args, struct list *known_acl, char **err, struct arg_list *al,
|
|
|
|
const char *file, int line)
|
2007-05-06 22:36:48 +00:00
|
|
|
{
|
|
|
|
__label__ out_return, out_free_acl_expr, out_free_name;
|
|
|
|
struct acl *cur_acl;
|
|
|
|
struct acl_expr *acl_expr;
|
|
|
|
char *name;
|
2012-04-27 10:38:15 +00:00
|
|
|
const char *pos;
|
2007-05-06 22:36:48 +00:00
|
|
|
|
2012-04-27 10:38:15 +00:00
|
|
|
if (**args && (pos = invalid_char(*args))) {
|
2012-09-20 17:43:14 +00:00
|
|
|
memprintf(err, "invalid character in ACL name : '%c'", *pos);
|
2007-12-02 17:45:09 +00:00
|
|
|
goto out_return;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
2007-12-02 17:45:09 +00:00
|
|
|
|
2014-02-11 02:31:34 +00:00
|
|
|
acl_expr = parse_acl_expr(args + 1, err, al, file, line);
|
2012-04-27 10:38:15 +00:00
|
|
|
if (!acl_expr) {
|
|
|
|
/* parse_acl_expr will have filled <err> here */
|
2007-05-06 22:36:48 +00:00
|
|
|
goto out_return;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
2007-05-06 22:36:48 +00:00
|
|
|
|
2009-07-26 17:40:40 +00:00
|
|
|
/* Check for args beginning with an opening parenthesis just after the
|
|
|
|
* subject, as this is almost certainly a typo. Right now we can only
|
|
|
|
* emit a warning, so let's do so.
|
|
|
|
*/
|
2009-10-04 22:23:35 +00:00
|
|
|
if (!strchr(args[1], '(') && *args[2] == '(')
|
2009-07-26 17:40:40 +00:00
|
|
|
Warning("parsing acl '%s' :\n"
|
|
|
|
" matching '%s' for pattern '%s' is likely a mistake and probably\n"
|
|
|
|
" not what you want. Maybe you need to remove the extraneous space before '('.\n"
|
|
|
|
" If you are really sure this is not an error, please insert '--' between the\n"
|
|
|
|
" match and the pattern to make this warning message disappear.\n",
|
|
|
|
args[0], args[1], args[2]);
|
|
|
|
|
2010-03-15 15:13:29 +00:00
|
|
|
if (*args[0])
|
|
|
|
cur_acl = find_acl_by_name(args[0], known_acl);
|
|
|
|
else
|
|
|
|
cur_acl = NULL;
|
|
|
|
|
2007-05-06 22:36:48 +00:00
|
|
|
if (!cur_acl) {
|
|
|
|
name = strdup(args[0]);
|
2012-04-27 10:38:15 +00:00
|
|
|
if (!name) {
|
2012-09-20 17:43:14 +00:00
|
|
|
memprintf(err, "out of memory when parsing ACL");
|
2007-05-06 22:36:48 +00:00
|
|
|
goto out_free_acl_expr;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
2007-05-06 22:36:48 +00:00
|
|
|
cur_acl = (struct acl *)calloc(1, sizeof(*cur_acl));
|
2012-04-27 10:38:15 +00:00
|
|
|
if (cur_acl == NULL) {
|
2012-09-20 17:43:14 +00:00
|
|
|
memprintf(err, "out of memory when parsing ACL");
|
2007-05-06 22:36:48 +00:00
|
|
|
goto out_free_name;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
2007-05-06 22:36:48 +00:00
|
|
|
|
|
|
|
LIST_INIT(&cur_acl->expr);
|
|
|
|
LIST_ADDQ(known_acl, &cur_acl->list);
|
|
|
|
cur_acl->name = name;
|
|
|
|
}
|
|
|
|
|
2013-03-25 07:12:18 +00:00
|
|
|
/* We want to know what features the ACL needs (typically HTTP parsing),
|
|
|
|
* and where it may be used. If an ACL relies on multiple matches, it is
|
|
|
|
* OK if at least one of them may match in the context where it is used.
|
|
|
|
*/
|
2013-11-21 09:50:10 +00:00
|
|
|
cur_acl->use |= acl_expr->smp->fetch->use;
|
|
|
|
cur_acl->val |= acl_expr->smp->fetch->val;
|
2007-05-06 22:36:48 +00:00
|
|
|
LIST_ADDQ(&cur_acl->expr, &acl_expr->list);
|
|
|
|
return cur_acl;
|
|
|
|
|
|
|
|
out_free_name:
|
|
|
|
free(name);
|
|
|
|
out_free_acl_expr:
|
|
|
|
prune_acl_expr(acl_expr);
|
|
|
|
free(acl_expr);
|
|
|
|
out_return:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2007-06-17 09:54:31 +00:00
|
|
|
/* Some useful ACLs provided by default. Only those used are allocated. */
|
|
|
|
|
|
|
|
const struct {
|
|
|
|
const char *name;
|
|
|
|
const char *expr[4]; /* put enough for longest expression */
|
|
|
|
} default_acl_list[] = {
|
2008-07-20 08:39:22 +00:00
|
|
|
{ .name = "TRUE", .expr = {"always_true",""}},
|
|
|
|
{ .name = "FALSE", .expr = {"always_false",""}},
|
2007-06-17 09:54:31 +00:00
|
|
|
{ .name = "LOCALHOST", .expr = {"src","127.0.0.1/8",""}},
|
2009-07-10 22:06:00 +00:00
|
|
|
{ .name = "HTTP", .expr = {"req_proto_http",""}},
|
2007-06-17 09:54:31 +00:00
|
|
|
{ .name = "HTTP_1.0", .expr = {"req_ver","1.0",""}},
|
|
|
|
{ .name = "HTTP_1.1", .expr = {"req_ver","1.1",""}},
|
|
|
|
{ .name = "METH_CONNECT", .expr = {"method","CONNECT",""}},
|
|
|
|
{ .name = "METH_GET", .expr = {"method","GET","HEAD",""}},
|
|
|
|
{ .name = "METH_HEAD", .expr = {"method","HEAD",""}},
|
|
|
|
{ .name = "METH_OPTIONS", .expr = {"method","OPTIONS",""}},
|
|
|
|
{ .name = "METH_POST", .expr = {"method","POST",""}},
|
|
|
|
{ .name = "METH_TRACE", .expr = {"method","TRACE",""}},
|
|
|
|
{ .name = "HTTP_URL_ABS", .expr = {"url_reg","^[^/:]*://",""}},
|
|
|
|
{ .name = "HTTP_URL_SLASH", .expr = {"url_beg","/",""}},
|
|
|
|
{ .name = "HTTP_URL_STAR", .expr = {"url","*",""}},
|
|
|
|
{ .name = "HTTP_CONTENT", .expr = {"hdr_val(content-length)","gt","0",""}},
|
2009-06-30 15:54:00 +00:00
|
|
|
{ .name = "RDP_COOKIE", .expr = {"req_rdp_cookie_cnt","gt","0",""}},
|
2008-07-20 07:29:50 +00:00
|
|
|
{ .name = "REQ_CONTENT", .expr = {"req_len","gt","0",""}},
|
2008-07-20 09:18:28 +00:00
|
|
|
{ .name = "WAIT_END", .expr = {"wait_end",""}},
|
2007-06-17 09:54:31 +00:00
|
|
|
{ .name = NULL, .expr = {""}}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Find a default ACL from the default_acl list, compile it and return it.
|
|
|
|
* If the ACL is not found, NULL is returned. In theory, it cannot fail,
|
|
|
|
* except when default ACLs are broken, in which case it will return NULL.
|
2012-04-27 10:38:15 +00:00
|
|
|
* If <known_acl> is not NULL, the ACL will be queued at its tail. If <err> is
|
|
|
|
* not NULL, it will be filled with an error message if an error occurs. This
|
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve
While ACL args were resolved after all the config was parsed, it was not the
case with sample fetch args because they're almost everywhere now.
The issue is that ACLs now solely rely on sample fetches, so their args
resolving doesn't work anymore. And many fetches involving a server, a
proxy or a userlist don't work at all.
The real issue is that at the bottom layers we have no information about
proxies, line numbers, even ACLs in order to report understandable errors,
and that at the top layers we have no visibility over the locations where
fetches are referenced (think log node).
After failing multiple unsatisfying solutions attempts, we now have a new
concept of args list. The principle is that every proxy has a list head
which contains a number of indications such as the config keyword, the
context where it's used, the file and line number, etc... and a list of
arguments. This list head is of the same type as the elements, so it
serves as a template for adding new elements. This way, it is filled from
top to bottom by the callers with the information they have (eg: line
numbers, ACL name, ...) and the lower layers just have to duplicate it and
add an element when they face an argument they cannot resolve yet.
Then at the end of the configuration parsing, a loop passes over each
proxy's list and resolves all the args in sequence. And this way there is
all necessary information to report verbose errors.
The first immediate benefit is that for the first time we got very precise
location of issues (arg number in a keyword in its context, ...). Second,
in order to do this we had to parse log-format and unique-id-format a bit
earlier, so that was a great opportunity for doing so when the directives
are encountered (unless it's a default section). This way, the recorded
line numbers for these args are the ones of the place where the log format
is declared, not the end of the file.
Userlists report slightly more information now. They're the only remaining
ones in the ACL resolving function.
2013-04-02 14:34:32 +00:00
|
|
|
* pointer must be freeable or NULL. <al> is an arg_list serving as a list head
|
|
|
|
* to report missing dependencies.
|
2007-06-17 09:54:31 +00:00
|
|
|
*/
|
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve
While ACL args were resolved after all the config was parsed, it was not the
case with sample fetch args because they're almost everywhere now.
The issue is that ACLs now solely rely on sample fetches, so their args
resolving doesn't work anymore. And many fetches involving a server, a
proxy or a userlist don't work at all.
The real issue is that at the bottom layers we have no information about
proxies, line numbers, even ACLs in order to report understandable errors,
and that at the top layers we have no visibility over the locations where
fetches are referenced (think log node).
After failing multiple unsatisfying solutions attempts, we now have a new
concept of args list. The principle is that every proxy has a list head
which contains a number of indications such as the config keyword, the
context where it's used, the file and line number, etc... and a list of
arguments. This list head is of the same type as the elements, so it
serves as a template for adding new elements. This way, it is filled from
top to bottom by the callers with the information they have (eg: line
numbers, ACL name, ...) and the lower layers just have to duplicate it and
add an element when they face an argument they cannot resolve yet.
Then at the end of the configuration parsing, a loop passes over each
proxy's list and resolves all the args in sequence. And this way there is
all necessary information to report verbose errors.
The first immediate benefit is that for the first time we got very precise
location of issues (arg number in a keyword in its context, ...). Second,
in order to do this we had to parse log-format and unique-id-format a bit
earlier, so that was a great opportunity for doing so when the directives
are encountered (unless it's a default section). This way, the recorded
line numbers for these args are the ones of the place where the log format
is declared, not the end of the file.
Userlists report slightly more information now. They're the only remaining
ones in the ACL resolving function.
2013-04-02 14:34:32 +00:00
|
|
|
static struct acl *find_acl_default(const char *acl_name, struct list *known_acl,
|
2014-02-11 02:31:34 +00:00
|
|
|
char **err, struct arg_list *al,
|
|
|
|
const char *file, int line)
|
2007-06-17 09:54:31 +00:00
|
|
|
{
|
|
|
|
__label__ out_return, out_free_acl_expr, out_free_name;
|
|
|
|
struct acl *cur_acl;
|
|
|
|
struct acl_expr *acl_expr;
|
|
|
|
char *name;
|
|
|
|
int index;
|
|
|
|
|
|
|
|
for (index = 0; default_acl_list[index].name != NULL; index++) {
|
|
|
|
if (strcmp(acl_name, default_acl_list[index].name) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-04-27 10:38:15 +00:00
|
|
|
if (default_acl_list[index].name == NULL) {
|
2012-09-20 17:43:14 +00:00
|
|
|
memprintf(err, "no such ACL : '%s'", acl_name);
|
2007-06-17 09:54:31 +00:00
|
|
|
return NULL;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
2007-06-17 09:54:31 +00:00
|
|
|
|
2014-02-11 02:31:34 +00:00
|
|
|
acl_expr = parse_acl_expr((const char **)default_acl_list[index].expr, err, al, file, line);
|
2012-04-27 10:38:15 +00:00
|
|
|
if (!acl_expr) {
|
|
|
|
/* parse_acl_expr must have filled err here */
|
2007-06-17 09:54:31 +00:00
|
|
|
goto out_return;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
2007-06-17 09:54:31 +00:00
|
|
|
|
|
|
|
name = strdup(acl_name);
|
2012-04-27 10:38:15 +00:00
|
|
|
if (!name) {
|
2012-09-20 17:43:14 +00:00
|
|
|
memprintf(err, "out of memory when building default ACL '%s'", acl_name);
|
2007-06-17 09:54:31 +00:00
|
|
|
goto out_free_acl_expr;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
|
|
|
|
2007-06-17 09:54:31 +00:00
|
|
|
cur_acl = (struct acl *)calloc(1, sizeof(*cur_acl));
|
2012-04-27 10:38:15 +00:00
|
|
|
if (cur_acl == NULL) {
|
2012-09-20 17:43:14 +00:00
|
|
|
memprintf(err, "out of memory when building default ACL '%s'", acl_name);
|
2007-06-17 09:54:31 +00:00
|
|
|
goto out_free_name;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
2007-06-17 09:54:31 +00:00
|
|
|
|
|
|
|
cur_acl->name = name;
|
2013-11-21 09:50:10 +00:00
|
|
|
cur_acl->use |= acl_expr->smp->fetch->use;
|
|
|
|
cur_acl->val |= acl_expr->smp->fetch->val;
|
2007-06-17 09:54:31 +00:00
|
|
|
LIST_INIT(&cur_acl->expr);
|
|
|
|
LIST_ADDQ(&cur_acl->expr, &acl_expr->list);
|
|
|
|
if (known_acl)
|
|
|
|
LIST_ADDQ(known_acl, &cur_acl->list);
|
|
|
|
|
|
|
|
return cur_acl;
|
|
|
|
|
|
|
|
out_free_name:
|
|
|
|
free(name);
|
|
|
|
out_free_acl_expr:
|
|
|
|
prune_acl_expr(acl_expr);
|
|
|
|
free(acl_expr);
|
|
|
|
out_return:
|
|
|
|
return NULL;
|
|
|
|
}
|
2007-05-06 22:36:48 +00:00
|
|
|
|
|
|
|
/* Purge everything in the acl_cond <cond>, then return <cond>. */
|
|
|
|
struct acl_cond *prune_acl_cond(struct acl_cond *cond)
|
|
|
|
{
|
|
|
|
struct acl_term_suite *suite, *tmp_suite;
|
|
|
|
struct acl_term *term, *tmp_term;
|
|
|
|
|
|
|
|
/* iterate through all term suites and free all terms and all suites */
|
|
|
|
list_for_each_entry_safe(suite, tmp_suite, &cond->suites, list) {
|
|
|
|
list_for_each_entry_safe(term, tmp_term, &suite->terms, list)
|
|
|
|
free(term);
|
|
|
|
free(suite);
|
|
|
|
}
|
|
|
|
return cond;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse an ACL condition starting at <args>[0], relying on a list of already
|
|
|
|
* known ACLs passed in <known_acl>. The new condition is returned (or NULL in
|
2012-04-27 10:38:15 +00:00
|
|
|
* case of low memory). Supports multiple conditions separated by "or". If
|
|
|
|
* <err> is not NULL, it will be filled with a pointer to an error message in
|
|
|
|
* case of error, that the caller is responsible for freeing. The initial
|
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve
While ACL args were resolved after all the config was parsed, it was not the
case with sample fetch args because they're almost everywhere now.
The issue is that ACLs now solely rely on sample fetches, so their args
resolving doesn't work anymore. And many fetches involving a server, a
proxy or a userlist don't work at all.
The real issue is that at the bottom layers we have no information about
proxies, line numbers, even ACLs in order to report understandable errors,
and that at the top layers we have no visibility over the locations where
fetches are referenced (think log node).
After failing multiple unsatisfying solutions attempts, we now have a new
concept of args list. The principle is that every proxy has a list head
which contains a number of indications such as the config keyword, the
context where it's used, the file and line number, etc... and a list of
arguments. This list head is of the same type as the elements, so it
serves as a template for adding new elements. This way, it is filled from
top to bottom by the callers with the information they have (eg: line
numbers, ACL name, ...) and the lower layers just have to duplicate it and
add an element when they face an argument they cannot resolve yet.
Then at the end of the configuration parsing, a loop passes over each
proxy's list and resolves all the args in sequence. And this way there is
all necessary information to report verbose errors.
The first immediate benefit is that for the first time we got very precise
location of issues (arg number in a keyword in its context, ...). Second,
in order to do this we had to parse log-format and unique-id-format a bit
earlier, so that was a great opportunity for doing so when the directives
are encountered (unless it's a default section). This way, the recorded
line numbers for these args are the ones of the place where the log format
is declared, not the end of the file.
Userlists report slightly more information now. They're the only remaining
ones in the ACL resolving function.
2013-04-02 14:34:32 +00:00
|
|
|
* location must either be freeable or NULL. The list <al> serves as a list head
|
|
|
|
* for unresolved dependencies.
|
2007-05-06 22:36:48 +00:00
|
|
|
*/
|
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve
While ACL args were resolved after all the config was parsed, it was not the
case with sample fetch args because they're almost everywhere now.
The issue is that ACLs now solely rely on sample fetches, so their args
resolving doesn't work anymore. And many fetches involving a server, a
proxy or a userlist don't work at all.
The real issue is that at the bottom layers we have no information about
proxies, line numbers, even ACLs in order to report understandable errors,
and that at the top layers we have no visibility over the locations where
fetches are referenced (think log node).
After failing multiple unsatisfying solutions attempts, we now have a new
concept of args list. The principle is that every proxy has a list head
which contains a number of indications such as the config keyword, the
context where it's used, the file and line number, etc... and a list of
arguments. This list head is of the same type as the elements, so it
serves as a template for adding new elements. This way, it is filled from
top to bottom by the callers with the information they have (eg: line
numbers, ACL name, ...) and the lower layers just have to duplicate it and
add an element when they face an argument they cannot resolve yet.
Then at the end of the configuration parsing, a loop passes over each
proxy's list and resolves all the args in sequence. And this way there is
all necessary information to report verbose errors.
The first immediate benefit is that for the first time we got very precise
location of issues (arg number in a keyword in its context, ...). Second,
in order to do this we had to parse log-format and unique-id-format a bit
earlier, so that was a great opportunity for doing so when the directives
are encountered (unless it's a default section). This way, the recorded
line numbers for these args are the ones of the place where the log format
is declared, not the end of the file.
Userlists report slightly more information now. They're the only remaining
ones in the ACL resolving function.
2013-04-02 14:34:32 +00:00
|
|
|
struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl,
|
2014-02-11 02:31:34 +00:00
|
|
|
enum acl_cond_pol pol, char **err, struct arg_list *al,
|
|
|
|
const char *file, int line)
|
2007-05-06 22:36:48 +00:00
|
|
|
{
|
|
|
|
__label__ out_return, out_free_suite, out_free_term;
|
2007-06-16 17:35:18 +00:00
|
|
|
int arg, neg;
|
2007-05-06 22:36:48 +00:00
|
|
|
const char *word;
|
|
|
|
struct acl *cur_acl;
|
|
|
|
struct acl_term *cur_term;
|
|
|
|
struct acl_term_suite *cur_suite;
|
|
|
|
struct acl_cond *cond;
|
2013-03-25 07:12:18 +00:00
|
|
|
unsigned int suite_val;
|
2007-05-06 22:36:48 +00:00
|
|
|
|
|
|
|
cond = (struct acl_cond *)calloc(1, sizeof(*cond));
|
2012-04-27 10:38:15 +00:00
|
|
|
if (cond == NULL) {
|
2012-09-20 17:43:14 +00:00
|
|
|
memprintf(err, "out of memory when parsing condition");
|
2007-05-06 22:36:48 +00:00
|
|
|
goto out_return;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
2007-05-06 22:36:48 +00:00
|
|
|
|
|
|
|
LIST_INIT(&cond->list);
|
|
|
|
LIST_INIT(&cond->suites);
|
|
|
|
cond->pol = pol;
|
2013-03-25 07:12:18 +00:00
|
|
|
cond->val = 0;
|
2007-05-06 22:36:48 +00:00
|
|
|
|
|
|
|
cur_suite = NULL;
|
2013-03-25 07:12:18 +00:00
|
|
|
suite_val = ~0U;
|
2007-06-16 17:35:18 +00:00
|
|
|
neg = 0;
|
2007-05-06 22:36:48 +00:00
|
|
|
for (arg = 0; *args[arg]; arg++) {
|
|
|
|
word = args[arg];
|
|
|
|
|
|
|
|
/* remove as many exclamation marks as we can */
|
|
|
|
while (*word == '!') {
|
|
|
|
neg = !neg;
|
|
|
|
word++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* an empty word is allowed because we cannot force the user to
|
|
|
|
* always think about not leaving exclamation marks alone.
|
|
|
|
*/
|
|
|
|
if (!*word)
|
|
|
|
continue;
|
|
|
|
|
2007-06-17 09:54:31 +00:00
|
|
|
if (strcasecmp(word, "or") == 0 || strcmp(word, "||") == 0) {
|
2007-05-06 22:36:48 +00:00
|
|
|
/* new term suite */
|
2013-03-25 07:12:18 +00:00
|
|
|
cond->val |= suite_val;
|
|
|
|
suite_val = ~0U;
|
2007-05-06 22:36:48 +00:00
|
|
|
cur_suite = NULL;
|
|
|
|
neg = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2010-02-01 12:05:50 +00:00
|
|
|
if (strcmp(word, "{") == 0) {
|
|
|
|
/* we may have a complete ACL expression between two braces,
|
|
|
|
* find the last one.
|
|
|
|
*/
|
|
|
|
int arg_end = arg + 1;
|
|
|
|
const char **args_new;
|
|
|
|
|
|
|
|
while (*args[arg_end] && strcmp(args[arg_end], "}") != 0)
|
|
|
|
arg_end++;
|
|
|
|
|
2012-04-27 10:38:15 +00:00
|
|
|
if (!*args[arg_end]) {
|
2012-09-20 17:43:14 +00:00
|
|
|
memprintf(err, "missing closing '}' in condition");
|
2010-02-01 12:05:50 +00:00
|
|
|
goto out_free_suite;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
2010-02-01 12:05:50 +00:00
|
|
|
|
|
|
|
args_new = calloc(1, (arg_end - arg + 1) * sizeof(*args_new));
|
2012-04-27 10:38:15 +00:00
|
|
|
if (!args_new) {
|
2012-09-20 17:43:14 +00:00
|
|
|
memprintf(err, "out of memory when parsing condition");
|
2007-06-17 09:54:31 +00:00
|
|
|
goto out_free_suite;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
2010-02-01 12:05:50 +00:00
|
|
|
|
2010-03-15 15:13:29 +00:00
|
|
|
args_new[0] = "";
|
2010-02-01 12:05:50 +00:00
|
|
|
memcpy(args_new + 1, args + arg + 1, (arg_end - arg) * sizeof(*args_new));
|
|
|
|
args_new[arg_end - arg] = "";
|
2014-02-11 02:31:34 +00:00
|
|
|
cur_acl = parse_acl(args_new, known_acl, err, al, file, line);
|
2010-02-01 12:05:50 +00:00
|
|
|
free(args_new);
|
|
|
|
|
2012-04-27 10:38:15 +00:00
|
|
|
if (!cur_acl) {
|
|
|
|
/* note that parse_acl() must have filled <err> here */
|
2010-02-01 12:05:50 +00:00
|
|
|
goto out_free_suite;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
2013-03-25 07:12:18 +00:00
|
|
|
word = args[arg + 1];
|
2010-02-01 12:05:50 +00:00
|
|
|
arg = arg_end;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* search for <word> in the known ACL names. If we do not find
|
|
|
|
* it, let's look for it in the default ACLs, and if found, add
|
|
|
|
* it to the list of ACLs of this proxy. This makes it possible
|
|
|
|
* to override them.
|
|
|
|
*/
|
|
|
|
cur_acl = find_acl_by_name(word, known_acl);
|
|
|
|
if (cur_acl == NULL) {
|
2014-02-11 02:31:34 +00:00
|
|
|
cur_acl = find_acl_default(word, known_acl, err, al, file, line);
|
2012-04-27 10:38:15 +00:00
|
|
|
if (cur_acl == NULL) {
|
|
|
|
/* note that find_acl_default() must have filled <err> here */
|
2010-02-01 12:05:50 +00:00
|
|
|
goto out_free_suite;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
2010-02-01 12:05:50 +00:00
|
|
|
}
|
2007-06-17 09:54:31 +00:00
|
|
|
}
|
2007-05-06 22:36:48 +00:00
|
|
|
|
|
|
|
cur_term = (struct acl_term *)calloc(1, sizeof(*cur_term));
|
2012-04-27 10:38:15 +00:00
|
|
|
if (cur_term == NULL) {
|
2012-09-20 17:43:14 +00:00
|
|
|
memprintf(err, "out of memory when parsing condition");
|
2007-05-06 22:36:48 +00:00
|
|
|
goto out_free_suite;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
2007-05-06 22:36:48 +00:00
|
|
|
|
|
|
|
cur_term->acl = cur_acl;
|
|
|
|
cur_term->neg = neg;
|
2013-03-25 07:12:18 +00:00
|
|
|
|
|
|
|
/* Here it is a bit complex. The acl_term_suite is a conjunction
|
|
|
|
* of many terms. It may only be used if all of its terms are
|
|
|
|
* usable at the same time. So the suite's validity domain is an
|
|
|
|
* AND between all ACL keywords' ones. But, the global condition
|
|
|
|
* is valid if at least one term suite is OK. So it's an OR between
|
|
|
|
* all of their validity domains. We could emit a warning as soon
|
|
|
|
* as suite_val is null because it means that the last ACL is not
|
|
|
|
* compatible with the previous ones. Let's remain simple for now.
|
|
|
|
*/
|
|
|
|
cond->use |= cur_acl->use;
|
|
|
|
suite_val &= cur_acl->val;
|
2007-05-06 22:36:48 +00:00
|
|
|
|
|
|
|
if (!cur_suite) {
|
|
|
|
cur_suite = (struct acl_term_suite *)calloc(1, sizeof(*cur_suite));
|
2013-01-23 23:25:39 +00:00
|
|
|
if (cur_suite == NULL) {
|
2012-09-20 17:43:14 +00:00
|
|
|
memprintf(err, "out of memory when parsing condition");
|
2007-05-06 22:36:48 +00:00
|
|
|
goto out_free_term;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
2007-05-06 22:36:48 +00:00
|
|
|
LIST_INIT(&cur_suite->terms);
|
|
|
|
LIST_ADDQ(&cond->suites, &cur_suite->list);
|
|
|
|
}
|
|
|
|
LIST_ADDQ(&cur_suite->terms, &cur_term->list);
|
2007-06-16 17:35:18 +00:00
|
|
|
neg = 0;
|
2007-05-06 22:36:48 +00:00
|
|
|
}
|
|
|
|
|
2013-03-25 07:12:18 +00:00
|
|
|
cond->val |= suite_val;
|
2007-05-06 22:36:48 +00:00
|
|
|
return cond;
|
|
|
|
|
|
|
|
out_free_term:
|
|
|
|
free(cur_term);
|
|
|
|
out_free_suite:
|
|
|
|
prune_acl_cond(cond);
|
|
|
|
free(cond);
|
|
|
|
out_return:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-01-28 15:48:33 +00:00
|
|
|
/* Builds an ACL condition starting at the if/unless keyword. The complete
|
|
|
|
* condition is returned. NULL is returned in case of error or if the first
|
|
|
|
* word is neither "if" nor "unless". It automatically sets the file name and
|
2013-03-24 06:22:08 +00:00
|
|
|
* the line number in the condition for better error reporting, and sets the
|
|
|
|
* HTTP intiailization requirements in the proxy. If <err> is not NULL, it will
|
2012-04-27 10:38:15 +00:00
|
|
|
* be filled with a pointer to an error message in case of error, that the
|
|
|
|
* caller is responsible for freeing. The initial location must either be
|
|
|
|
* freeable or NULL.
|
2010-01-28 15:48:33 +00:00
|
|
|
*/
|
2012-04-27 10:38:15 +00:00
|
|
|
struct acl_cond *build_acl_cond(const char *file, int line, struct proxy *px, const char **args, char **err)
|
2010-01-28 15:48:33 +00:00
|
|
|
{
|
2013-11-28 21:21:02 +00:00
|
|
|
enum acl_cond_pol pol = ACL_COND_NONE;
|
2010-01-28 15:48:33 +00:00
|
|
|
struct acl_cond *cond = NULL;
|
|
|
|
|
2012-04-27 10:38:15 +00:00
|
|
|
if (err)
|
|
|
|
*err = NULL;
|
|
|
|
|
2010-01-28 15:48:33 +00:00
|
|
|
if (!strcmp(*args, "if")) {
|
|
|
|
pol = ACL_COND_IF;
|
|
|
|
args++;
|
|
|
|
}
|
|
|
|
else if (!strcmp(*args, "unless")) {
|
|
|
|
pol = ACL_COND_UNLESS;
|
|
|
|
args++;
|
|
|
|
}
|
2012-04-27 10:38:15 +00:00
|
|
|
else {
|
2012-09-20 17:43:14 +00:00
|
|
|
memprintf(err, "conditions must start with either 'if' or 'unless'");
|
2010-01-28 15:48:33 +00:00
|
|
|
return NULL;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
2010-01-28 15:48:33 +00:00
|
|
|
|
2014-02-11 02:31:34 +00:00
|
|
|
cond = parse_acl_cond(args, &px->acl, pol, err, &px->conf.args, file, line);
|
2012-04-27 10:38:15 +00:00
|
|
|
if (!cond) {
|
|
|
|
/* note that parse_acl_cond must have filled <err> here */
|
2010-01-28 15:48:33 +00:00
|
|
|
return NULL;
|
2012-04-27 10:38:15 +00:00
|
|
|
}
|
2010-01-28 15:48:33 +00:00
|
|
|
|
|
|
|
cond->file = file;
|
|
|
|
cond->line = line;
|
2013-03-25 07:12:18 +00:00
|
|
|
px->http_needed |= !!(cond->use & SMP_USE_HTTP_ANY);
|
2010-01-28 15:48:33 +00:00
|
|
|
return cond;
|
|
|
|
}
|
|
|
|
|
2013-11-28 17:22:00 +00:00
|
|
|
/* Execute condition <cond> and return either ACL_TEST_FAIL, ACL_TEST_MISS or
|
|
|
|
* ACL_TEST_PASS depending on the test results. ACL_TEST_MISS may only be
|
2012-04-25 08:13:36 +00:00
|
|
|
* returned if <opt> does not contain SMP_OPT_FINAL, indicating that incomplete
|
2013-11-28 17:22:00 +00:00
|
|
|
* data is being examined. The function automatically sets SMP_OPT_ITERATE. This
|
|
|
|
* function only computes the condition, it does not apply the polarity required
|
|
|
|
* by IF/UNLESS, it's up to the caller to do this using something like this :
|
2008-07-09 14:18:21 +00:00
|
|
|
*
|
|
|
|
* res = acl_pass(res);
|
2013-11-28 17:22:00 +00:00
|
|
|
* if (res == ACL_TEST_MISS)
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 21:54:42 +00:00
|
|
|
* return 0;
|
2008-07-09 14:18:21 +00:00
|
|
|
* if (cond->pol == ACL_COND_UNLESS)
|
|
|
|
* res = !res;
|
2007-05-06 22:36:48 +00:00
|
|
|
*/
|
2015-04-03 23:47:55 +00:00
|
|
|
enum acl_test_res acl_exec_cond(struct acl_cond *cond, struct proxy *px, struct session *sess, struct stream *strm, unsigned int opt)
|
2007-05-06 22:36:48 +00:00
|
|
|
{
|
|
|
|
__label__ fetch_next;
|
|
|
|
struct acl_term_suite *suite;
|
|
|
|
struct acl_term *term;
|
|
|
|
struct acl_expr *expr;
|
|
|
|
struct acl *acl;
|
2012-04-23 14:16:37 +00:00
|
|
|
struct sample smp;
|
2013-11-28 21:21:02 +00:00
|
|
|
enum acl_test_res acl_res, suite_res, cond_res;
|
2007-05-06 22:36:48 +00:00
|
|
|
|
2012-04-26 09:44:02 +00:00
|
|
|
/* ACLs are iterated over all values, so let's always set the flag to
|
|
|
|
* indicate this to the fetch functions.
|
|
|
|
*/
|
|
|
|
opt |= SMP_OPT_ITERATE;
|
|
|
|
|
2008-07-09 14:18:21 +00:00
|
|
|
/* We're doing a logical OR between conditions so we initialize to FAIL.
|
|
|
|
* The MISS status is propagated down from the suites.
|
|
|
|
*/
|
2013-11-28 17:22:00 +00:00
|
|
|
cond_res = ACL_TEST_FAIL;
|
2007-05-06 22:36:48 +00:00
|
|
|
list_for_each_entry(suite, &cond->suites, list) {
|
2008-07-09 14:18:21 +00:00
|
|
|
/* Evaluate condition suite <suite>. We stop at the first term
|
2013-11-28 21:21:02 +00:00
|
|
|
* which returns ACL_TEST_FAIL. The MISS status is still propagated
|
2008-07-09 14:18:21 +00:00
|
|
|
* in case of uncertainty in the result.
|
2007-05-06 22:36:48 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* we're doing a logical AND between terms, so we must set the
|
|
|
|
* initial value to PASS.
|
|
|
|
*/
|
2013-11-28 17:22:00 +00:00
|
|
|
suite_res = ACL_TEST_PASS;
|
2007-05-06 22:36:48 +00:00
|
|
|
list_for_each_entry(term, &suite->terms, list) {
|
|
|
|
acl = term->acl;
|
|
|
|
|
|
|
|
/* FIXME: use cache !
|
|
|
|
* check acl->cache_idx for this.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* ACL result not cached. Let's scan all the expressions
|
|
|
|
* and use the first one to match.
|
|
|
|
*/
|
2013-11-28 17:22:00 +00:00
|
|
|
acl_res = ACL_TEST_FAIL;
|
2007-05-06 22:36:48 +00:00
|
|
|
list_for_each_entry(expr, &acl->expr, list) {
|
2007-06-10 08:06:18 +00:00
|
|
|
/* we need to reset context and flags */
|
2012-04-23 14:16:37 +00:00
|
|
|
memset(&smp, 0, sizeof(smp));
|
2007-05-06 22:36:48 +00:00
|
|
|
fetch_next:
|
2015-04-03 23:47:55 +00:00
|
|
|
if (!sample_process(px, sess, strm, opt, expr->smp, &smp)) {
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 21:54:42 +00:00
|
|
|
/* maybe we could not fetch because of missing data */
|
2012-04-25 08:13:36 +00:00
|
|
|
if (smp.flags & SMP_F_MAY_CHANGE && !(opt & SMP_OPT_FINAL))
|
2013-11-28 17:22:00 +00:00
|
|
|
acl_res |= ACL_TEST_MISS;
|
2007-05-06 22:36:48 +00:00
|
|
|
continue;
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 21:54:42 +00:00
|
|
|
}
|
2007-05-06 22:36:48 +00:00
|
|
|
|
2014-01-17 14:25:13 +00:00
|
|
|
acl_res |= pat2acl(pattern_exec_match(&expr->pat, &smp, 0));
|
2007-05-06 22:36:48 +00:00
|
|
|
/*
|
2008-07-09 14:18:21 +00:00
|
|
|
* OK now acl_res holds the result of this expression
|
2013-11-28 17:22:00 +00:00
|
|
|
* as one of ACL_TEST_FAIL, ACL_TEST_MISS or ACL_TEST_PASS.
|
2007-05-06 22:36:48 +00:00
|
|
|
*
|
2008-07-09 14:18:21 +00:00
|
|
|
* Then if (!MISS) we can cache the result, and put
|
2012-04-23 14:16:37 +00:00
|
|
|
* (smp.flags & SMP_F_VOLATILE) in the cache flags.
|
2007-05-06 22:36:48 +00:00
|
|
|
*
|
|
|
|
* FIXME: implement cache.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2008-07-09 14:18:21 +00:00
|
|
|
/* we're ORing these terms, so a single PASS is enough */
|
2013-11-28 17:22:00 +00:00
|
|
|
if (acl_res == ACL_TEST_PASS)
|
2007-05-06 22:36:48 +00:00
|
|
|
break;
|
|
|
|
|
2012-04-23 14:16:37 +00:00
|
|
|
if (smp.flags & SMP_F_NOT_LAST)
|
2007-05-06 22:36:48 +00:00
|
|
|
goto fetch_next;
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 21:54:42 +00:00
|
|
|
|
|
|
|
/* sometimes we know the fetched data is subject to change
|
|
|
|
* later and give another chance for a new match (eg: request
|
|
|
|
* size, time, ...)
|
|
|
|
*/
|
2012-04-25 08:13:36 +00:00
|
|
|
if (smp.flags & SMP_F_MAY_CHANGE && !(opt & SMP_OPT_FINAL))
|
2013-11-28 17:22:00 +00:00
|
|
|
acl_res |= ACL_TEST_MISS;
|
2007-05-06 22:36:48 +00:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Here we have the result of an ACL (cached or not).
|
|
|
|
* ACLs are combined, negated or not, to form conditions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (term->neg)
|
2008-07-09 14:18:21 +00:00
|
|
|
acl_res = acl_neg(acl_res);
|
2007-05-06 22:36:48 +00:00
|
|
|
|
|
|
|
suite_res &= acl_res;
|
2008-07-09 14:18:21 +00:00
|
|
|
|
2013-10-30 18:30:32 +00:00
|
|
|
/* we're ANDing these terms, so a single FAIL or MISS is enough */
|
2013-11-28 17:22:00 +00:00
|
|
|
if (suite_res != ACL_TEST_PASS)
|
2007-05-06 22:36:48 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
cond_res |= suite_res;
|
2008-07-09 14:18:21 +00:00
|
|
|
|
|
|
|
/* we're ORing these terms, so a single PASS is enough */
|
2013-11-28 17:22:00 +00:00
|
|
|
if (cond_res == ACL_TEST_PASS)
|
2007-05-06 22:36:48 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-07-09 14:18:21 +00:00
|
|
|
return cond_res;
|
2007-05-06 22:36:48 +00:00
|
|
|
}
|
|
|
|
|
2013-03-25 07:12:18 +00:00
|
|
|
/* Returns a pointer to the first ACL conflicting with usage at place <where>
|
|
|
|
* which is one of the SMP_VAL_* bits indicating a check place, or NULL if
|
|
|
|
* no conflict is found. Only full conflicts are detected (ACL is not usable).
|
|
|
|
* Use the next function to check for useless keywords.
|
2008-07-27 20:02:32 +00:00
|
|
|
*/
|
2013-03-25 07:12:18 +00:00
|
|
|
const struct acl *acl_cond_conflicts(const struct acl_cond *cond, unsigned int where)
|
2008-07-27 20:02:32 +00:00
|
|
|
{
|
|
|
|
struct acl_term_suite *suite;
|
|
|
|
struct acl_term *term;
|
|
|
|
struct acl *acl;
|
|
|
|
|
|
|
|
list_for_each_entry(suite, &cond->suites, list) {
|
|
|
|
list_for_each_entry(term, &suite->terms, list) {
|
|
|
|
acl = term->acl;
|
2013-03-25 07:12:18 +00:00
|
|
|
if (!(acl->val & where))
|
2008-07-27 20:02:32 +00:00
|
|
|
return acl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-03-25 07:12:18 +00:00
|
|
|
/* Returns a pointer to the first ACL and its first keyword to conflict with
|
|
|
|
* usage at place <where> which is one of the SMP_VAL_* bits indicating a check
|
|
|
|
* place. Returns true if a conflict is found, with <acl> and <kw> set (if non
|
|
|
|
* null), or false if not conflict is found. The first useless keyword is
|
|
|
|
* returned.
|
|
|
|
*/
|
2013-03-31 20:59:32 +00:00
|
|
|
int acl_cond_kw_conflicts(const struct acl_cond *cond, unsigned int where, struct acl const **acl, char const **kw)
|
2013-03-25 07:12:18 +00:00
|
|
|
{
|
|
|
|
struct acl_term_suite *suite;
|
|
|
|
struct acl_term *term;
|
|
|
|
struct acl_expr *expr;
|
|
|
|
|
|
|
|
list_for_each_entry(suite, &cond->suites, list) {
|
|
|
|
list_for_each_entry(term, &suite->terms, list) {
|
|
|
|
list_for_each_entry(expr, &term->acl->expr, list) {
|
2013-11-21 09:50:10 +00:00
|
|
|
if (!(expr->smp->fetch->val & where)) {
|
2013-03-25 07:12:18 +00:00
|
|
|
if (acl)
|
|
|
|
*acl = term->acl;
|
|
|
|
if (kw)
|
|
|
|
*kw = expr->kw;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-01-29 18:26:18 +00:00
|
|
|
/*
|
|
|
|
* Find targets for userlist and groups in acl. Function returns the number
|
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve
While ACL args were resolved after all the config was parsed, it was not the
case with sample fetch args because they're almost everywhere now.
The issue is that ACLs now solely rely on sample fetches, so their args
resolving doesn't work anymore. And many fetches involving a server, a
proxy or a userlist don't work at all.
The real issue is that at the bottom layers we have no information about
proxies, line numbers, even ACLs in order to report understandable errors,
and that at the top layers we have no visibility over the locations where
fetches are referenced (think log node).
After failing multiple unsatisfying solutions attempts, we now have a new
concept of args list. The principle is that every proxy has a list head
which contains a number of indications such as the config keyword, the
context where it's used, the file and line number, etc... and a list of
arguments. This list head is of the same type as the elements, so it
serves as a template for adding new elements. This way, it is filled from
top to bottom by the callers with the information they have (eg: line
numbers, ACL name, ...) and the lower layers just have to duplicate it and
add an element when they face an argument they cannot resolve yet.
Then at the end of the configuration parsing, a loop passes over each
proxy's list and resolves all the args in sequence. And this way there is
all necessary information to report verbose errors.
The first immediate benefit is that for the first time we got very precise
location of issues (arg number in a keyword in its context, ...). Second,
in order to do this we had to parse log-format and unique-id-format a bit
earlier, so that was a great opportunity for doing so when the directives
are encountered (unless it's a default section). This way, the recorded
line numbers for these args are the ones of the place where the log format
is declared, not the end of the file.
Userlists report slightly more information now. They're the only remaining
ones in the ACL resolving function.
2013-04-02 14:34:32 +00:00
|
|
|
* of errors or OK if everything is fine. It must be called only once sample
|
|
|
|
* fetch arguments have been resolved (after smp_resolve_args()).
|
2010-01-29 18:26:18 +00:00
|
|
|
*/
|
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve
While ACL args were resolved after all the config was parsed, it was not the
case with sample fetch args because they're almost everywhere now.
The issue is that ACLs now solely rely on sample fetches, so their args
resolving doesn't work anymore. And many fetches involving a server, a
proxy or a userlist don't work at all.
The real issue is that at the bottom layers we have no information about
proxies, line numbers, even ACLs in order to report understandable errors,
and that at the top layers we have no visibility over the locations where
fetches are referenced (think log node).
After failing multiple unsatisfying solutions attempts, we now have a new
concept of args list. The principle is that every proxy has a list head
which contains a number of indications such as the config keyword, the
context where it's used, the file and line number, etc... and a list of
arguments. This list head is of the same type as the elements, so it
serves as a template for adding new elements. This way, it is filled from
top to bottom by the callers with the information they have (eg: line
numbers, ACL name, ...) and the lower layers just have to duplicate it and
add an element when they face an argument they cannot resolve yet.
Then at the end of the configuration parsing, a loop passes over each
proxy's list and resolves all the args in sequence. And this way there is
all necessary information to report verbose errors.
The first immediate benefit is that for the first time we got very precise
location of issues (arg number in a keyword in its context, ...). Second,
in order to do this we had to parse log-format and unique-id-format a bit
earlier, so that was a great opportunity for doing so when the directives
are encountered (unless it's a default section). This way, the recorded
line numbers for these args are the ones of the place where the log format
is declared, not the end of the file.
Userlists report slightly more information now. They're the only remaining
ones in the ACL resolving function.
2013-04-02 14:34:32 +00:00
|
|
|
int acl_find_targets(struct proxy *p)
|
2010-01-29 18:26:18 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
struct acl *acl;
|
|
|
|
struct acl_expr *expr;
|
2013-12-13 11:12:18 +00:00
|
|
|
struct pattern_list *pattern;
|
2010-01-29 18:26:18 +00:00
|
|
|
int cfgerr = 0;
|
2014-01-20 13:29:33 +00:00
|
|
|
struct pattern_expr_list *pexp;
|
2010-01-29 18:26:18 +00:00
|
|
|
|
|
|
|
list_for_each_entry(acl, &p->acl, list) {
|
|
|
|
list_for_each_entry(expr, &acl->expr, list) {
|
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve
While ACL args were resolved after all the config was parsed, it was not the
case with sample fetch args because they're almost everywhere now.
The issue is that ACLs now solely rely on sample fetches, so their args
resolving doesn't work anymore. And many fetches involving a server, a
proxy or a userlist don't work at all.
The real issue is that at the bottom layers we have no information about
proxies, line numbers, even ACLs in order to report understandable errors,
and that at the top layers we have no visibility over the locations where
fetches are referenced (think log node).
After failing multiple unsatisfying solutions attempts, we now have a new
concept of args list. The principle is that every proxy has a list head
which contains a number of indications such as the config keyword, the
context where it's used, the file and line number, etc... and a list of
arguments. This list head is of the same type as the elements, so it
serves as a template for adding new elements. This way, it is filled from
top to bottom by the callers with the information they have (eg: line
numbers, ACL name, ...) and the lower layers just have to duplicate it and
add an element when they face an argument they cannot resolve yet.
Then at the end of the configuration parsing, a loop passes over each
proxy's list and resolves all the args in sequence. And this way there is
all necessary information to report verbose errors.
The first immediate benefit is that for the first time we got very precise
location of issues (arg number in a keyword in its context, ...). Second,
in order to do this we had to parse log-format and unique-id-format a bit
earlier, so that was a great opportunity for doing so when the directives
are encountered (unless it's a default section). This way, the recorded
line numbers for these args are the ones of the place where the log format
is declared, not the end of the file.
Userlists report slightly more information now. They're the only remaining
ones in the ACL resolving function.
2013-04-02 14:34:32 +00:00
|
|
|
if (!strcmp(expr->kw, "http_auth_group")) {
|
|
|
|
/* Note: the ARGT_USR argument may only have been resolved earlier
|
|
|
|
* by smp_resolve_args().
|
|
|
|
*/
|
2013-11-21 09:50:10 +00:00
|
|
|
if (expr->smp->arg_p->unresolved) {
|
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve
While ACL args were resolved after all the config was parsed, it was not the
case with sample fetch args because they're almost everywhere now.
The issue is that ACLs now solely rely on sample fetches, so their args
resolving doesn't work anymore. And many fetches involving a server, a
proxy or a userlist don't work at all.
The real issue is that at the bottom layers we have no information about
proxies, line numbers, even ACLs in order to report understandable errors,
and that at the top layers we have no visibility over the locations where
fetches are referenced (think log node).
After failing multiple unsatisfying solutions attempts, we now have a new
concept of args list. The principle is that every proxy has a list head
which contains a number of indications such as the config keyword, the
context where it's used, the file and line number, etc... and a list of
arguments. This list head is of the same type as the elements, so it
serves as a template for adding new elements. This way, it is filled from
top to bottom by the callers with the information they have (eg: line
numbers, ACL name, ...) and the lower layers just have to duplicate it and
add an element when they face an argument they cannot resolve yet.
Then at the end of the configuration parsing, a loop passes over each
proxy's list and resolves all the args in sequence. And this way there is
all necessary information to report verbose errors.
The first immediate benefit is that for the first time we got very precise
location of issues (arg number in a keyword in its context, ...). Second,
in order to do this we had to parse log-format and unique-id-format a bit
earlier, so that was a great opportunity for doing so when the directives
are encountered (unless it's a default section). This way, the recorded
line numbers for these args are the ones of the place where the log format
is declared, not the end of the file.
Userlists report slightly more information now. They're the only remaining
ones in the ACL resolving function.
2013-04-02 14:34:32 +00:00
|
|
|
Alert("Internal bug in proxy %s: %sacl %s %s() makes use of unresolved userlist '%s'. Please report this.\n",
|
2013-11-21 09:50:10 +00:00
|
|
|
p->id, *acl->name ? "" : "anonymous ", acl->name, expr->kw, expr->smp->arg_p->data.str.str);
|
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve
While ACL args were resolved after all the config was parsed, it was not the
case with sample fetch args because they're almost everywhere now.
The issue is that ACLs now solely rely on sample fetches, so their args
resolving doesn't work anymore. And many fetches involving a server, a
proxy or a userlist don't work at all.
The real issue is that at the bottom layers we have no information about
proxies, line numbers, even ACLs in order to report understandable errors,
and that at the top layers we have no visibility over the locations where
fetches are referenced (think log node).
After failing multiple unsatisfying solutions attempts, we now have a new
concept of args list. The principle is that every proxy has a list head
which contains a number of indications such as the config keyword, the
context where it's used, the file and line number, etc... and a list of
arguments. This list head is of the same type as the elements, so it
serves as a template for adding new elements. This way, it is filled from
top to bottom by the callers with the information they have (eg: line
numbers, ACL name, ...) and the lower layers just have to duplicate it and
add an element when they face an argument they cannot resolve yet.
Then at the end of the configuration parsing, a loop passes over each
proxy's list and resolves all the args in sequence. And this way there is
all necessary information to report verbose errors.
The first immediate benefit is that for the first time we got very precise
location of issues (arg number in a keyword in its context, ...). Second,
in order to do this we had to parse log-format and unique-id-format a bit
earlier, so that was a great opportunity for doing so when the directives
are encountered (unless it's a default section). This way, the recorded
line numbers for these args are the ones of the place where the log format
is declared, not the end of the file.
Userlists report slightly more information now. They're the only remaining
ones in the ACL resolving function.
2013-04-02 14:34:32 +00:00
|
|
|
cfgerr++;
|
2012-06-01 08:38:29 +00:00
|
|
|
continue;
|
2012-04-19 17:28:33 +00:00
|
|
|
}
|
2010-01-29 18:26:18 +00:00
|
|
|
|
2014-02-11 10:31:40 +00:00
|
|
|
if (LIST_ISEMPTY(&expr->pat.head)) {
|
2010-01-29 18:26:18 +00:00
|
|
|
Alert("proxy %s: acl %s %s(): no groups specified.\n",
|
2013-03-31 20:59:32 +00:00
|
|
|
p->id, acl->name, expr->kw);
|
2010-01-29 18:26:18 +00:00
|
|
|
cfgerr++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-01-22 17:38:02 +00:00
|
|
|
/* For each pattern, check if the group exists. */
|
2014-01-20 13:29:33 +00:00
|
|
|
list_for_each_entry(pexp, &expr->pat.head, list) {
|
|
|
|
if (LIST_ISEMPTY(&pexp->expr->patterns)) {
|
2014-02-11 10:31:40 +00:00
|
|
|
Alert("proxy %s: acl %s %s(): no groups specified.\n",
|
|
|
|
p->id, acl->name, expr->kw);
|
2010-01-29 18:26:18 +00:00
|
|
|
cfgerr++;
|
2014-02-11 10:31:40 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-01-20 13:29:33 +00:00
|
|
|
list_for_each_entry(pattern, &pexp->expr->patterns, list) {
|
|
|
|
/* this keyword only has one argument */
|
2014-02-11 10:31:40 +00:00
|
|
|
if (!check_group(expr->smp->arg_p->data.usr, pattern->pat.ptr.str)) {
|
|
|
|
Alert("proxy %s: acl %s %s(): invalid group '%s'.\n",
|
|
|
|
p->id, acl->name, expr->kw, pattern->pat.ptr.str);
|
|
|
|
cfgerr++;
|
|
|
|
}
|
2010-01-29 18:26:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return cfgerr;
|
|
|
|
}
|
2008-07-27 20:02:32 +00:00
|
|
|
|
2013-01-11 14:49:37 +00:00
|
|
|
/* initializes ACLs by resolving the sample fetch names they rely upon.
|
|
|
|
* Returns 0 on success, otherwise an error.
|
|
|
|
*/
|
|
|
|
int init_acl()
|
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
int index;
|
|
|
|
const char *name;
|
|
|
|
struct acl_kw_list *kwl;
|
|
|
|
struct sample_fetch *smp;
|
|
|
|
|
|
|
|
list_for_each_entry(kwl, &acl_keywords.list, list) {
|
|
|
|
for (index = 0; kwl->kw[index].kw != NULL; index++) {
|
|
|
|
name = kwl->kw[index].fetch_kw;
|
|
|
|
if (!name)
|
|
|
|
name = kwl->kw[index].kw;
|
|
|
|
|
|
|
|
smp = find_sample_fetch(name, strlen(name));
|
|
|
|
if (!smp) {
|
|
|
|
Alert("Critical internal error: ACL keyword '%s' relies on sample fetch '%s' which was not registered!\n",
|
|
|
|
kwl->kw[index].kw, name);
|
|
|
|
err++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
kwl->kw[index].smp = smp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
MEDIUM: samples: move payload-based fetches and ACLs to their own file
The file acl.c is a real mess, it both contains functions to parse and
process ACLs, and some sample extraction functions which act on buffers.
Some other payload analysers were arbitrarily dispatched to proto_tcp.c.
So now we're moving all payload-based fetches and ACLs to payload.c
which is capable of extracting data from buffers and rely on everything
that is protocol-independant. That way we can safely inflate this file
and only use the other ones when some fetches are really specific (eg:
HTTP, SSL, ...).
As a result of this cleanup, the following new sample fetches became
available even if they're not really useful :
always_false, always_true, rep_ssl_hello_type, rdp_cookie_cnt,
req_len, req_ssl_hello_type, req_ssl_sni, req_ssl_ver, wait_end
The function 'acl_fetch_nothing' was wrong and never used anywhere so it
was removed.
The "rdp_cookie" sample fetch used to have a mandatory argument while it
was optional in ACLs, which are supposed to iterate over RDP cookies. So
we're making it optional as a fetch too, and it will return the first one.
2013-01-07 20:59:07 +00:00
|
|
|
|
2007-05-06 22:36:48 +00:00
|
|
|
/************************************************************************/
|
MEDIUM: samples: move payload-based fetches and ACLs to their own file
The file acl.c is a real mess, it both contains functions to parse and
process ACLs, and some sample extraction functions which act on buffers.
Some other payload analysers were arbitrarily dispatched to proto_tcp.c.
So now we're moving all payload-based fetches and ACLs to payload.c
which is capable of extracting data from buffers and rely on everything
that is protocol-independant. That way we can safely inflate this file
and only use the other ones when some fetches are really specific (eg:
HTTP, SSL, ...).
As a result of this cleanup, the following new sample fetches became
available even if they're not really useful :
always_false, always_true, rep_ssl_hello_type, rdp_cookie_cnt,
req_len, req_ssl_hello_type, req_ssl_sni, req_ssl_ver, wait_end
The function 'acl_fetch_nothing' was wrong and never used anywhere so it
was removed.
The "rdp_cookie" sample fetch used to have a mandatory argument while it
was optional in ACLs, which are supposed to iterate over RDP cookies. So
we're making it optional as a fetch too, and it will return the first one.
2013-01-07 20:59:07 +00:00
|
|
|
/* All supported sample and ACL keywords must be declared here. */
|
2007-05-06 22:36:48 +00:00
|
|
|
/************************************************************************/
|
|
|
|
|
2012-04-19 16:42:05 +00:00
|
|
|
/* Note: must not be declared <const> as its list will be overwritten.
|
|
|
|
* Please take care of keeping this list alphabetically sorted.
|
|
|
|
*/
|
2013-06-21 21:16:39 +00:00
|
|
|
static struct acl_kw_list acl_kws = {ILH, {
|
MEDIUM: samples: move payload-based fetches and ACLs to their own file
The file acl.c is a real mess, it both contains functions to parse and
process ACLs, and some sample extraction functions which act on buffers.
Some other payload analysers were arbitrarily dispatched to proto_tcp.c.
So now we're moving all payload-based fetches and ACLs to payload.c
which is capable of extracting data from buffers and rely on everything
that is protocol-independant. That way we can safely inflate this file
and only use the other ones when some fetches are really specific (eg:
HTTP, SSL, ...).
As a result of this cleanup, the following new sample fetches became
available even if they're not really useful :
always_false, always_true, rep_ssl_hello_type, rdp_cookie_cnt,
req_len, req_ssl_hello_type, req_ssl_sni, req_ssl_ver, wait_end
The function 'acl_fetch_nothing' was wrong and never used anywhere so it
was removed.
The "rdp_cookie" sample fetch used to have a mandatory argument while it
was optional in ACLs, which are supposed to iterate over RDP cookies. So
we're making it optional as a fetch too, and it will return the first one.
2013-01-07 20:59:07 +00:00
|
|
|
{ /* END */ },
|
2007-05-06 22:36:48 +00:00
|
|
|
}};
|
|
|
|
|
|
|
|
__attribute__((constructor))
|
|
|
|
static void __acl_init(void)
|
|
|
|
{
|
|
|
|
acl_register_keywords(&acl_kws);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Local variables:
|
|
|
|
* c-indent-level: 8
|
|
|
|
* c-basic-offset: 8
|
|
|
|
* End:
|
|
|
|
*/
|