diff --git a/Makefile b/Makefile index 4c30cc06c..41b3e0ffe 100644 --- a/Makefile +++ b/Makefile @@ -637,7 +637,7 @@ OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocol.o \ src/stream_interface.o src/dumpstats.o src/proto_tcp.o \ src/session.o src/hdr_idx.o src/ev_select.o src/signal.o \ src/acl.o src/sample.o src/memory.o src/freq_ctr.o src/auth.o \ - src/compression.o src/payload.o src/hash.o + src/compression.o src/payload.o src/hash.o src/pattern.o EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o \ $(EBTREE_DIR)/eb32tree.o $(EBTREE_DIR)/eb64tree.o \ diff --git a/include/proto/acl.h b/include/proto/acl.h index 5e2e9ee28..e9add28a0 100644 --- a/include/proto/acl.h +++ b/include/proto/acl.h @@ -112,21 +112,6 @@ const struct acl *acl_cond_conflicts(const struct acl_cond *cond, unsigned int w */ int acl_cond_kw_conflicts(const struct acl_cond *cond, unsigned int where, struct acl const **acl, char const **kw); -/* parse the with compliant parser. is a context for - * the current parsed acl. It must initialized at NULL: - * - * struct acl_pattern *pattern = NULL - * acl_register_pattern(..., &pattern, ...); - * - * patflag are a combination of 'ACL_PAT_F_*' flags pattern compatible. see - * . - * - * The function returns 1 if the processing is ok, return -1 if the parser - * fails, with message filled. It returns -2 in "out of memory" - * error case. - */ -int acl_register_pattern(struct acl_expr *expr, char *text, struct sample_storage *smp, struct acl_pattern **pattern, int patflags, char **err); - /* * Find targets for userlist and groups in acl. Function returns the number * of errors or OK if everything is fine. @@ -138,13 +123,6 @@ int acl_find_targets(struct proxy *p); */ struct acl *find_acl_by_name(const char *name, struct list *head); -/* This function execute the match part of the acl. It's applying - * acl on sample . is filled only if the pointer - * is not NULL. The function return ACL_PAT_FAIL, ACL_PAT_MISS or - * ACL_PAT_PASS - */ -inline int acl_exec_match(struct acl_expr *expr, struct sample *smp, struct sample_storage **sample); - /* * Registers the ACL keyword list as a list of valid keywords for next * parsing sessions. @@ -162,90 +140,6 @@ void acl_unregister_keywords(struct acl_kw_list *kwl); int init_acl(); -/* - * - * The following functions are general purpose ACL matching functions. - * - */ - - -/* ignore the current line */ -int acl_parse_nothing(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); - -/* NB: For two strings to be identical, it is required that their lengths match */ -int acl_match_str(struct sample *smp, struct acl_pattern *pattern); - -/* NB: For two binary buffers to be identical, it is required that their lengths match */ -int acl_match_bin(struct sample *smp, struct acl_pattern *pattern); - -/* Checks that the length of the pattern in is included between min and max */ -int acl_match_len(struct sample *smp, struct acl_pattern *pattern); - -/* Checks that the integer in is included between min and max */ -int acl_match_int(struct sample *smp, struct acl_pattern *pattern); - -/* Parse an integer. It is put both in min and max. */ -int acl_parse_int(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); - -/* Parse an version. It is put both in min and max. */ -int acl_parse_dotted_ver(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); - -/* Parse a range of integers delimited by either ':' or '-'. If only one - * integer is read, it is set as both min and max. - */ -int acl_parse_range(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); - -/* Parse a string. It is allocated and duplicated. */ -int acl_parse_str(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); - -/* Parse a hexa binary definition. It is allocated and duplicated. */ -int acl_parse_bin(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); - -/* Parse and concatenate strings into one. It is allocated and duplicated. */ -int acl_parse_strcat(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); - -/* Parse a regex. It is allocated. */ -int acl_parse_reg(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); - -/* Parse an IP address and an optional mask in the form addr[/mask]. - * The addr may either be an IPv4 address or a hostname. The mask - * may either be a dotted mask or a number of bits. Returns 1 if OK, - * otherwise 0. - */ -int acl_parse_ip(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); - -/* always return false */ -int acl_match_nothing(struct sample *smp, struct acl_pattern *pattern); - -/* Checks that the pattern matches the end of the tested string. */ -int acl_match_end(struct sample *smp, struct acl_pattern *pattern); - -/* Checks that the pattern matches the beginning of the tested string. */ -int acl_match_beg(struct sample *smp, struct acl_pattern *pattern); - -/* Checks that the pattern is included inside the tested string. */ -int acl_match_sub(struct sample *smp, struct acl_pattern *pattern); - -/* Checks that the pattern is included inside the tested string, but enclosed - * between slashes or at the beginning or end of the string. Slashes at the - * beginning or end of the pattern are ignored. - */ -int acl_match_dir(struct sample *smp, struct acl_pattern *pattern); - -/* Checks that the pattern is included inside the tested string, but enclosed - * between dots or at the beginning or end of the string. Dots at the beginning - * or end of the pattern are ignored. - */ -int acl_match_dom(struct sample *smp, struct acl_pattern *pattern); - -/* Check that the IPv4 address in matches the IP/mask in pattern */ -int acl_match_ip(struct sample *smp, struct acl_pattern *pattern); - -/* Executes a regex. It temporarily changes the data to add a trailing zero, - * and restores the previous character when leaving. - */ -int acl_match_reg(struct sample *smp, struct acl_pattern *pattern); - #endif /* _PROTO_ACL_H */ /* diff --git a/include/proto/pattern.h b/include/proto/pattern.h new file mode 100644 index 000000000..9898eb3e7 --- /dev/null +++ b/include/proto/pattern.h @@ -0,0 +1,136 @@ +/* + * include/proto/pattern.h + * This file provides structures and types for pattern matching. + * + * Copyright (C) 2000-2013 Willy Tarreau - w@1wt.eu + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, version 2.1 + * exclusively. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _PROTO_PATTERN_H +#define _PROTO_PATTERN_H + +/* parse the with compliant parser. is a context for + * the current parsed acl. It must initialized at NULL: + * + * struct acl_pattern *pattern = NULL + * acl_register_pattern(..., &pattern, ...); + * + * patflag are a combination of 'ACL_PAT_F_*' flags pattern compatible. see + * . + * + * The function returns 1 if the processing is ok, return -1 if the parser + * fails, with message filled. It returns -2 in "out of memory" + * error case. + */ +int acl_register_pattern(struct acl_expr *expr, char *text, struct sample_storage *smp, struct acl_pattern **pattern, int patflags, char **err); + +/* This function executes a pattern match on a sample. It applies pattern + * to sample . If is not NULL, a pointer to an optional sample + * associated to the matching patterned will be put there. The function returns + * ACL_PAT_FAIL, ACL_PAT_MISS or ACL_PAT_PASS. + */ +inline int acl_exec_match(struct acl_expr *expr, struct sample *smp, struct sample_storage **sample); + +/* + * + * The following functions are general purpose pattern matching functions. + * + */ + + +/* ignore the current line */ +int acl_parse_nothing(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); + +/* NB: For two strings to be identical, it is required that their lengths match */ +int acl_match_str(struct sample *smp, struct acl_pattern *pattern); + +/* NB: For two binary buffers to be identical, it is required that their lengths match */ +int acl_match_bin(struct sample *smp, struct acl_pattern *pattern); + +/* Checks that the length of the pattern in is included between min and max */ +int acl_match_len(struct sample *smp, struct acl_pattern *pattern); + +/* Checks that the integer in is included between min and max */ +int acl_match_int(struct sample *smp, struct acl_pattern *pattern); + +/* Parse an integer. It is put both in min and max. */ +int acl_parse_int(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); + +/* Parse an version. It is put both in min and max. */ +int acl_parse_dotted_ver(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); + +/* Parse a range of integers delimited by either ':' or '-'. If only one + * integer is read, it is set as both min and max. + */ +int acl_parse_range(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); + +/* Parse a string. It is allocated and duplicated. */ +int acl_parse_str(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); + +/* Parse a hexa binary definition. It is allocated and duplicated. */ +int acl_parse_bin(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); + +/* Parse and concatenate strings into one. It is allocated and duplicated. */ +int acl_parse_strcat(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); + +/* Parse a regex. It is allocated. */ +int acl_parse_reg(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); + +/* Parse an IP address and an optional mask in the form addr[/mask]. + * The addr may either be an IPv4 address or a hostname. The mask + * may either be a dotted mask or a number of bits. Returns 1 if OK, + * otherwise 0. + */ +int acl_parse_ip(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); + +/* always return false */ +int acl_match_nothing(struct sample *smp, struct acl_pattern *pattern); + +/* Checks that the pattern matches the end of the tested string. */ +int acl_match_end(struct sample *smp, struct acl_pattern *pattern); + +/* Checks that the pattern matches the beginning of the tested string. */ +int acl_match_beg(struct sample *smp, struct acl_pattern *pattern); + +/* Checks that the pattern is included inside the tested string. */ +int acl_match_sub(struct sample *smp, struct acl_pattern *pattern); + +/* Checks that the pattern is included inside the tested string, but enclosed + * between slashes or at the beginning or end of the string. Slashes at the + * beginning or end of the pattern are ignored. + */ +int acl_match_dir(struct sample *smp, struct acl_pattern *pattern); + +/* Checks that the pattern is included inside the tested string, but enclosed + * between dots or at the beginning or end of the string. Dots at the beginning + * or end of the pattern are ignored. + */ +int acl_match_dom(struct sample *smp, struct acl_pattern *pattern); + +/* Check that the IPv4 address in matches the IP/mask in pattern */ +int acl_match_ip(struct sample *smp, struct acl_pattern *pattern); + +/* Executes a regex. It temporarily changes the data to add a trailing zero, + * and restores the previous character when leaving. + */ +int acl_match_reg(struct sample *smp, struct acl_pattern *pattern); + +int acl_read_patterns_from_file(struct acl_expr *expr, const char *filename, int patflags, char **err); +void free_pattern(struct acl_pattern *pat); +void free_pattern_list(struct list *head); +void free_pattern_tree(struct eb_root *root); + +#endif diff --git a/include/types/acl.h b/include/types/acl.h index 322e74b97..e479f9bc6 100644 --- a/include/types/acl.h +++ b/include/types/acl.h @@ -28,8 +28,8 @@ #include #include +#include #include -#include #include #include @@ -55,12 +55,6 @@ * */ -enum { - ACL_PAT_FAIL = 0, /* test failed */ - ACL_PAT_MISS = 1, /* test may pass with more info */ - ACL_PAT_PASS = 3, /* test passed */ -}; - /* Condition polarity. It makes it easier for any option to choose between * IF/UNLESS if it can store that information within the condition itself. * Those should be interpreted as "IF/UNLESS result == PASS". @@ -71,90 +65,6 @@ enum { ACL_COND_UNLESS, /* negative condition (after 'unless') */ }; -/* possible flags for expressions or patterns */ -enum { - ACL_PAT_F_IGNORE_CASE = 1 << 0, /* ignore case */ - ACL_PAT_F_FROM_FILE = 1 << 1, /* pattern comes from a file */ - ACL_PAT_F_TREE_OK = 1 << 2, /* the pattern parser is allowed to build a tree */ - ACL_PAT_F_TREE = 1 << 3, /* some patterns are arranged in a tree */ -}; - -/* ACL match methods */ -enum { - ACL_MATCH_FOUND, /* just ensure that fetch found the sample */ - ACL_MATCH_BOOL, /* match fetch's integer value as boolean */ - ACL_MATCH_INT, /* unsigned integer (int) */ - ACL_MATCH_IP, /* IPv4/IPv6 address (IP) */ - ACL_MATCH_BIN, /* hex string (bin) */ - ACL_MATCH_LEN, /* string length (str -> int) */ - ACL_MATCH_STR, /* exact string match (str) */ - ACL_MATCH_BEG, /* beginning of string (str) */ - ACL_MATCH_SUB, /* substring (str) */ - ACL_MATCH_DIR, /* directory-like sub-string (str) */ - ACL_MATCH_DOM, /* domain-like sub-string (str) */ - ACL_MATCH_END, /* end of string (str) */ - ACL_MATCH_REG, /* regex (str -> reg) */ - /* keep this one last */ - ACL_MATCH_NUM -}; - -/* How to store a time range and the valid days in 29 bits */ -struct acl_time { - int dow:7; /* 1 bit per day of week: 0-6 */ - int h1:5, m1:6; /* 0..24:0..60. Use 0:0 for all day. */ - int h2:5, m2:6; /* 0..24:0..60. Use 24:0 for all day. */ -}; - -/* This contain each tree indexed entry. This struct permit to associate - * "sample" with a tree entry. It is used with maps. - */ -struct acl_idx_elt { - struct sample_storage *smp; - struct ebmb_node node; -}; - -/* This describes one ACL pattern, which might be a single value or a tree of - * values. All patterns for a single ACL expression are linked together. Some - * of them might have a type (eg: IP). Right now, the types are shared with - * the samples, though it is possible that in the future this will change to - * accommodate for other types (eg: meth, regex). Unsigned and constant types - * are preferred when there is a doubt. - */ -struct acl_pattern { - struct list list; /* chaining */ - int type; /* type of the ACL pattern (SMP_T_*) */ - union { - int i; /* integer value */ - struct { - signed long long min, max; - int min_set :1; - int max_set :1; - } range; /* integer range */ - struct { - struct in_addr addr; - struct in_addr mask; - } ipv4; /* IPv4 address */ - struct { - struct in6_addr addr; - unsigned char mask; /* number of bits */ - } ipv6; /* IPv6 address/mask */ - struct acl_time time; /* valid hours and days */ - unsigned int group_mask; - struct eb_root *tree; /* tree storing all values if any */ - } val; /* direct value */ - union { - void *ptr; /* any data */ - char *str; /* any string */ - regex *reg; /* a compiled regex */ - } ptr; /* indirect values, allocated */ - void(*freeptrbuf)(void *ptr); /* a destructor able to free objects from the ptr */ - int len; /* data length when required */ - int flags; /* expr or pattern flags. */ - struct sample_storage *smp; /* used to store a pointer to sample value associated - with the match. It is used with maps */ - -}; - /* some dummy declarations to silent the compiler */ struct proxy; struct session; @@ -242,10 +152,6 @@ struct acl_cond { int line; /* line in the config file where the condition is declared */ }; -extern char *acl_match_names[ACL_MATCH_NUM]; -extern int (*acl_parse_fcts[ACL_MATCH_NUM])(const char **, struct acl_pattern *, struct sample_storage *, int *, char **); -extern int (*acl_match_fcts[ACL_MATCH_NUM])(struct sample *, struct acl_pattern *); - #endif /* _TYPES_ACL_H */ /* diff --git a/include/types/pattern.h b/include/types/pattern.h new file mode 100644 index 000000000..68ed4621b --- /dev/null +++ b/include/types/pattern.h @@ -0,0 +1,128 @@ +/* + * include/types/pattern.h + * This file provides structures and types for ACLs. + * + * Copyright (C) 2000-2012 Willy Tarreau - w@1wt.eu + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, version 2.1 + * exclusively. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _TYPES_PATTERN_H +#define _TYPES_PATTERN_H + +#include +#include +#include +#include + +#include + +#include + +enum { + ACL_PAT_FAIL = 0, /* test failed */ + ACL_PAT_MISS = 1, /* test may pass with more info */ + ACL_PAT_PASS = 3, /* test passed */ +}; + +/* possible flags for expressions or patterns */ +enum { + ACL_PAT_F_IGNORE_CASE = 1 << 0, /* ignore case */ + ACL_PAT_F_FROM_FILE = 1 << 1, /* pattern comes from a file */ + ACL_PAT_F_TREE_OK = 1 << 2, /* the pattern parser is allowed to build a tree */ + ACL_PAT_F_TREE = 1 << 3, /* some patterns are arranged in a tree */ +}; + +/* ACL match methods */ +enum { + ACL_MATCH_FOUND, /* just ensure that fetch found the sample */ + ACL_MATCH_BOOL, /* match fetch's integer value as boolean */ + ACL_MATCH_INT, /* unsigned integer (int) */ + ACL_MATCH_IP, /* IPv4/IPv6 address (IP) */ + ACL_MATCH_BIN, /* hex string (bin) */ + ACL_MATCH_LEN, /* string length (str -> int) */ + ACL_MATCH_STR, /* exact string match (str) */ + ACL_MATCH_BEG, /* beginning of string (str) */ + ACL_MATCH_SUB, /* substring (str) */ + ACL_MATCH_DIR, /* directory-like sub-string (str) */ + ACL_MATCH_DOM, /* domain-like sub-string (str) */ + ACL_MATCH_END, /* end of string (str) */ + ACL_MATCH_REG, /* regex (str -> reg) */ + /* keep this one last */ + ACL_MATCH_NUM +}; + +/* How to store a time range and the valid days in 29 bits */ +struct acl_time { + int dow:7; /* 1 bit per day of week: 0-6 */ + int h1:5, m1:6; /* 0..24:0..60. Use 0:0 for all day. */ + int h2:5, m2:6; /* 0..24:0..60. Use 24:0 for all day. */ +}; + +/* This contain each tree indexed entry. This struct permit to associate + * "sample" with a tree entry. It is used with maps. + */ +struct acl_idx_elt { + struct sample_storage *smp; + struct ebmb_node node; +}; + +/* This describes one ACL pattern, which might be a single value or a tree of + * values. All patterns for a single ACL expression are linked together. Some + * of them might have a type (eg: IP). Right now, the types are shared with + * the samples, though it is possible that in the future this will change to + * accommodate for other types (eg: meth, regex). Unsigned and constant types + * are preferred when there is a doubt. + */ +struct acl_pattern { + struct list list; /* chaining */ + int type; /* type of the ACL pattern (SMP_T_*) */ + union { + int i; /* integer value */ + struct { + signed long long min, max; + int min_set :1; + int max_set :1; + } range; /* integer range */ + struct { + struct in_addr addr; + struct in_addr mask; + } ipv4; /* IPv4 address */ + struct { + struct in6_addr addr; + unsigned char mask; /* number of bits */ + } ipv6; /* IPv6 address/mask */ + struct acl_time time; /* valid hours and days */ + unsigned int group_mask; + struct eb_root *tree; /* tree storing all values if any */ + } val; /* direct value */ + union { + void *ptr; /* any data */ + char *str; /* any string */ + regex *reg; /* a compiled regex */ + } ptr; /* indirect values, allocated */ + void(*freeptrbuf)(void *ptr); /* a destructor able to free objects from the ptr */ + int len; /* data length when required */ + int flags; /* expr or pattern flags. */ + struct sample_storage *smp; /* used to store a pointer to sample value associated + with the match. It is used with maps */ + +}; + +extern char *acl_match_names[ACL_MATCH_NUM]; +extern int (*acl_parse_fcts[ACL_MATCH_NUM])(const char **, struct acl_pattern *, struct sample_storage *, int *, char **); +extern int (*acl_match_fcts[ACL_MATCH_NUM])(struct sample *, struct acl_pattern *); + +#endif /* _TYPES_PATTERN_H */ diff --git a/src/acl.c b/src/acl.c index ca24693df..1bb3e9f80 100644 --- a/src/acl.c +++ b/src/acl.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -37,54 +38,6 @@ static struct acl_kw_list acl_keywords = { .list = LIST_HEAD_INIT(acl_keywords.list) }; -char *acl_match_names[ACL_MATCH_NUM] = { - [ACL_MATCH_FOUND] = "found", - [ACL_MATCH_BOOL] = "bool", - [ACL_MATCH_INT] = "int", - [ACL_MATCH_IP] = "ip", - [ACL_MATCH_BIN] = "bin", - [ACL_MATCH_LEN] = "len", - [ACL_MATCH_STR] = "str", - [ACL_MATCH_BEG] = "beg", - [ACL_MATCH_SUB] = "sub", - [ACL_MATCH_DIR] = "dir", - [ACL_MATCH_DOM] = "dom", - [ACL_MATCH_END] = "end", - [ACL_MATCH_REG] = "reg", -}; - -int (*acl_parse_fcts[ACL_MATCH_NUM])(const char **, struct acl_pattern *, struct sample_storage *, int *, char **) = { - [ACL_MATCH_FOUND] = acl_parse_nothing, - [ACL_MATCH_BOOL] = acl_parse_nothing, - [ACL_MATCH_INT] = acl_parse_int, - [ACL_MATCH_IP] = acl_parse_ip, - [ACL_MATCH_BIN] = acl_parse_bin, - [ACL_MATCH_LEN] = acl_parse_int, - [ACL_MATCH_STR] = acl_parse_str, - [ACL_MATCH_BEG] = acl_parse_str, - [ACL_MATCH_SUB] = acl_parse_str, - [ACL_MATCH_DIR] = acl_parse_str, - [ACL_MATCH_DOM] = acl_parse_str, - [ACL_MATCH_END] = acl_parse_str, - [ACL_MATCH_REG] = acl_parse_reg, -}; - -int (*acl_match_fcts[ACL_MATCH_NUM])(struct sample *, struct acl_pattern *) = { - [ACL_MATCH_FOUND] = NULL, - [ACL_MATCH_BOOL] = acl_match_nothing, - [ACL_MATCH_INT] = acl_match_int, - [ACL_MATCH_IP] = acl_match_ip, - [ACL_MATCH_BIN] = acl_match_bin, - [ACL_MATCH_LEN] = acl_match_len, - [ACL_MATCH_STR] = acl_match_str, - [ACL_MATCH_BEG] = acl_match_beg, - [ACL_MATCH_SUB] = acl_match_sub, - [ACL_MATCH_DIR] = acl_match_dir, - [ACL_MATCH_DOM] = acl_match_dom, - [ACL_MATCH_END] = acl_match_end, - [ACL_MATCH_REG] = acl_match_reg, -}; - /* return the ACL_MATCH_* index for match name "name", or < 0 if not found */ static int acl_find_match_name(const char *name) { @@ -96,707 +49,6 @@ static int acl_find_match_name(const char *name) return -1; } -/* - * These functions are exported and may be used by any other component. - */ - -/* ignore the current line */ -int acl_parse_nothing(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) -{ - return 1; -} - -/* always return false */ -int acl_match_nothing(struct sample *smp, struct acl_pattern *pattern) -{ - return ACL_PAT_FAIL; -} - - -/* NB: For two strings to be identical, it is required that their lengths match */ -int acl_match_str(struct sample *smp, struct acl_pattern *pattern) -{ - int icase; - - if (pattern->len != smp->data.str.len) - return ACL_PAT_FAIL; - - icase = pattern->flags & ACL_PAT_F_IGNORE_CASE; - if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0) || - (!icase && strncmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0)) - return ACL_PAT_PASS; - return ACL_PAT_FAIL; -} - -/* NB: For two binaries buf to be identical, it is required that their lengths match */ -int acl_match_bin(struct sample *smp, struct acl_pattern *pattern) -{ - if (pattern->len != smp->data.str.len) - return ACL_PAT_FAIL; - - if (memcmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0) - return ACL_PAT_PASS; - return ACL_PAT_FAIL; -} - -/* Lookup a string in the expression's pattern tree. The node is returned if it - * exists, otherwise NULL. - */ -static void *acl_lookup_str(struct sample *smp, struct acl_expr *expr) -{ - /* data are stored in a tree */ - struct ebmb_node *node; - char prev; - - /* we may have to force a trailing zero on the test pattern */ - prev = smp->data.str.str[smp->data.str.len]; - if (prev) - smp->data.str.str[smp->data.str.len] = '\0'; - node = ebst_lookup(&expr->pattern_tree, smp->data.str.str); - if (prev) - smp->data.str.str[smp->data.str.len] = prev; - return node; -} - -/* Executes a regex. It temporarily changes the data to add a trailing zero, - * and restores the previous character when leaving. - */ -int acl_match_reg(struct sample *smp, struct acl_pattern *pattern) -{ - if (regex_exec(pattern->ptr.reg, smp->data.str.str, smp->data.str.len) == 0) - return ACL_PAT_PASS; - return ACL_PAT_FAIL; -} - -/* Checks that the pattern matches the beginning of the tested string. */ -int acl_match_beg(struct sample *smp, struct acl_pattern *pattern) -{ - int icase; - - if (pattern->len > smp->data.str.len) - return ACL_PAT_FAIL; - - icase = pattern->flags & ACL_PAT_F_IGNORE_CASE; - if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str, pattern->len) != 0) || - (!icase && strncmp(pattern->ptr.str, smp->data.str.str, pattern->len) != 0)) - return ACL_PAT_FAIL; - return ACL_PAT_PASS; -} - -/* Checks that the pattern matches the end of the tested string. */ -int acl_match_end(struct sample *smp, struct acl_pattern *pattern) -{ - int icase; - - if (pattern->len > smp->data.str.len) - return ACL_PAT_FAIL; - icase = pattern->flags & ACL_PAT_F_IGNORE_CASE; - if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str + smp->data.str.len - pattern->len, pattern->len) != 0) || - (!icase && strncmp(pattern->ptr.str, smp->data.str.str + smp->data.str.len - pattern->len, pattern->len) != 0)) - return ACL_PAT_FAIL; - return ACL_PAT_PASS; -} - -/* Checks that the pattern is included inside the tested string. - * NB: Suboptimal, should be rewritten using a Boyer-Moore method. - */ -int acl_match_sub(struct sample *smp, struct acl_pattern *pattern) -{ - int icase; - char *end; - char *c; - - if (pattern->len > smp->data.str.len) - return ACL_PAT_FAIL; - - end = smp->data.str.str + smp->data.str.len - pattern->len; - icase = pattern->flags & ACL_PAT_F_IGNORE_CASE; - if (icase) { - for (c = smp->data.str.str; c <= end; c++) { - if (tolower(*c) != tolower(*pattern->ptr.str)) - continue; - if (strncasecmp(pattern->ptr.str, c, pattern->len) == 0) - return ACL_PAT_PASS; - } - } else { - for (c = smp->data.str.str; c <= end; c++) { - if (*c != *pattern->ptr.str) - continue; - if (strncmp(pattern->ptr.str, c, pattern->len) == 0) - return ACL_PAT_PASS; - } - } - return ACL_PAT_FAIL; -} - -/* Background: Fast way to find a zero byte in a word - * http://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord - * hasZeroByte = (v - 0x01010101UL) & ~v & 0x80808080UL; - * - * To look for 4 different byte values, xor the word with those bytes and - * then check for zero bytes: - * - * v = (((unsigned char)c * 0x1010101U) ^ delimiter) - * where is the 4 byte values to look for (as an uint) - * and is the character that is being tested - */ -static inline unsigned int is_delimiter(unsigned char c, unsigned int mask) -{ - mask ^= (c * 0x01010101); /* propagate the char to all 4 bytes */ - return (mask - 0x01010101) & ~mask & 0x80808080U; -} - -static inline unsigned int make_4delim(unsigned char d1, unsigned char d2, unsigned char d3, unsigned char d4) -{ - return d1 << 24 | d2 << 16 | d3 << 8 | d4; -} - -/* This one is used by other real functions. It checks that the pattern is - * included inside the tested string, but enclosed between the specified - * delimiters or at the beginning or end of the string. The delimiters are - * provided as an unsigned int made by make_4delim() and match up to 4 different - * delimiters. Delimiters are stripped at the beginning and end of the pattern. - */ -static int match_word(struct sample *smp, struct acl_pattern *pattern, unsigned int delimiters) -{ - int may_match, icase; - char *c, *end; - char *ps; - int pl; - - pl = pattern->len; - ps = pattern->ptr.str; - - while (pl > 0 && is_delimiter(*ps, delimiters)) { - pl--; - ps++; - } - - while (pl > 0 && is_delimiter(ps[pl - 1], delimiters)) - pl--; - - if (pl > smp->data.str.len) - return ACL_PAT_FAIL; - - may_match = 1; - icase = pattern->flags & ACL_PAT_F_IGNORE_CASE; - end = smp->data.str.str + smp->data.str.len - pl; - for (c = smp->data.str.str; c <= end; c++) { - if (is_delimiter(*c, delimiters)) { - may_match = 1; - continue; - } - - if (!may_match) - continue; - - if (icase) { - if ((tolower(*c) == tolower(*ps)) && - (strncasecmp(ps, c, pl) == 0) && - (c == end || is_delimiter(c[pl], delimiters))) - return ACL_PAT_PASS; - } else { - if ((*c == *ps) && - (strncmp(ps, c, pl) == 0) && - (c == end || is_delimiter(c[pl], delimiters))) - return ACL_PAT_PASS; - } - may_match = 0; - } - return ACL_PAT_FAIL; -} - -/* Checks that the pattern is included inside the tested string, but enclosed - * between the delimiters '?' or '/' or at the beginning or end of the string. - * Delimiters at the beginning or end of the pattern are ignored. - */ -int acl_match_dir(struct sample *smp, struct acl_pattern *pattern) -{ - return match_word(smp, pattern, make_4delim('/', '?', '?', '?')); -} - -/* Checks that the pattern is included inside the tested string, but enclosed - * between the delmiters '/', '?', '.' or ":" or at the beginning or end of - * the string. Delimiters at the beginning or end of the pattern are ignored. - */ -int acl_match_dom(struct sample *smp, struct acl_pattern *pattern) -{ - return match_word(smp, pattern, make_4delim('/', '?', '.', ':')); -} - -/* Checks that the integer in is included between min and max */ -int acl_match_int(struct sample *smp, struct acl_pattern *pattern) -{ - if ((!pattern->val.range.min_set || pattern->val.range.min <= smp->data.uint) && - (!pattern->val.range.max_set || smp->data.uint <= pattern->val.range.max)) - return ACL_PAT_PASS; - return ACL_PAT_FAIL; -} - -/* Checks that the length of the pattern in is included between min and max */ -int acl_match_len(struct sample *smp, struct acl_pattern *pattern) -{ - if ((!pattern->val.range.min_set || pattern->val.range.min <= smp->data.str.len) && - (!pattern->val.range.max_set || smp->data.str.len <= pattern->val.range.max)) - return ACL_PAT_PASS; - return ACL_PAT_FAIL; -} - -int acl_match_ip(struct sample *smp, struct acl_pattern *pattern) -{ - unsigned int v4; /* in network byte order */ - struct in6_addr *v6; - int bits, pos; - struct in6_addr tmp6; - - if (pattern->type == SMP_T_IPV4) { - if (smp->type == SMP_T_IPV4) { - v4 = smp->data.ipv4.s_addr; - } - else if (smp->type == SMP_T_IPV6) { - /* v4 match on a V6 sample. We want to check at least for - * the following forms : - * - ::ffff:ip:v4 (ipv4 mapped) - * - ::0000:ip:v4 (old ipv4 mapped) - * - 2002:ip:v4:: (6to4) - */ - if (*(uint32_t*)&smp->data.ipv6.s6_addr[0] == 0 && - *(uint32_t*)&smp->data.ipv6.s6_addr[4] == 0 && - (*(uint32_t*)&smp->data.ipv6.s6_addr[8] == 0 || - *(uint32_t*)&smp->data.ipv6.s6_addr[8] == htonl(0xFFFF))) { - v4 = *(uint32_t*)&smp->data.ipv6.s6_addr[12]; - } - else if (*(uint16_t*)&smp->data.ipv6.s6_addr[0] == htons(0x2002)) { - v4 = htonl((ntohs(*(uint16_t*)&smp->data.ipv6.s6_addr[2]) << 16) + - ntohs(*(uint16_t*)&smp->data.ipv6.s6_addr[4])); - } - else - return ACL_PAT_FAIL; - } - else - return ACL_PAT_FAIL; - - if (((v4 ^ pattern->val.ipv4.addr.s_addr) & pattern->val.ipv4.mask.s_addr) == 0) - return ACL_PAT_PASS; - else - return ACL_PAT_FAIL; - } - else if (pattern->type == SMP_T_IPV6) { - if (smp->type == SMP_T_IPV4) { - /* Convert the IPv4 sample address to IPv4 with the - * mapping method using the ::ffff: prefix. - */ - memset(&tmp6, 0, 10); - *(uint16_t*)&tmp6.s6_addr[10] = htons(0xffff); - *(uint32_t*)&tmp6.s6_addr[12] = smp->data.ipv4.s_addr; - v6 = &tmp6; - } - else if (smp->type == SMP_T_IPV6) { - v6 = &smp->data.ipv6; - } - else { - return ACL_PAT_FAIL; - } - - bits = pattern->val.ipv6.mask; - for (pos = 0; bits > 0; pos += 4, bits -= 32) { - v4 = *(uint32_t*)&v6->s6_addr[pos] ^ *(uint32_t*)&pattern->val.ipv6.addr.s6_addr[pos]; - if (bits < 32) - v4 &= htonl((~0U) << (32-bits)); - if (v4) - return ACL_PAT_FAIL; - } - return ACL_PAT_PASS; - } - return ACL_PAT_FAIL; -} - -/* Lookup an IPv4 address in the expression's pattern tree using the longest - * match method. The node is returned if it exists, otherwise NULL. - */ -static void *acl_lookup_ip(struct sample *smp, struct acl_expr *expr) -{ - struct in_addr *s; - - if (smp->type != SMP_T_IPV4) - return ACL_PAT_FAIL; - - s = &smp->data.ipv4; - return ebmb_lookup_longest(&expr->pattern_tree, &s->s_addr); -} - -/* Parse a string. It is allocated and duplicated. */ -int acl_parse_str(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) -{ - int len; - - len = strlen(*text); - pattern->type = SMP_T_CSTR; - - if (pattern->flags & ACL_PAT_F_TREE_OK) { - /* we're allowed to put the data in a tree whose root is pointed - * to by val.tree. - */ - struct acl_idx_elt *node; - - node = calloc(1, sizeof(*node) + len + 1); - if (!node) { - memprintf(err, "out of memory while loading string pattern"); - return 0; - } - node->smp = smp; - memcpy(node->node.key, *text, len + 1); - if (ebst_insert(pattern->val.tree, &node->node) != &node->node) - free(node); /* was a duplicate */ - pattern->flags |= ACL_PAT_F_TREE; /* this pattern now contains a tree */ - return 1; - } - - pattern->ptr.str = strdup(*text); - pattern->smp = smp; - if (!pattern->ptr.str) { - memprintf(err, "out of memory while loading string pattern"); - return 0; - } - pattern->len = len; - return 1; -} - -/* Parse a binary written in hexa. It is allocated. */ -int acl_parse_bin(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) -{ - int len; - const char *p = *text; - int i,j; - - len = strlen(p); - if (len%2) { - memprintf(err, "an even number of hex digit is expected"); - return 0; - } - - pattern->type = SMP_T_CBIN; - pattern->len = len >> 1; - pattern->ptr.str = malloc(pattern->len); - pattern->smp = smp; - if (!pattern->ptr.str) { - memprintf(err, "out of memory while loading string pattern"); - return 0; - } - - i = j = 0; - while (j < pattern->len) { - if (!ishex(p[i++])) - goto bad_input; - if (!ishex(p[i++])) - goto bad_input; - pattern->ptr.str[j++] = (hex2i(p[i-2]) << 4) + hex2i(p[i-1]); - } - return 1; - -bad_input: - memprintf(err, "an hex digit is expected (found '%c')", p[i-1]); - free(pattern->ptr.str); - return 0; -} - -/* Parse and concatenate all further strings into one. */ -int -acl_parse_strcat(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) -{ - - int len = 0, i; - char *s; - - for (i = 0; *text[i]; i++) - len += strlen(text[i])+1; - - pattern->type = SMP_T_CSTR; - pattern->ptr.str = s = calloc(1, len); - pattern->smp = smp; - if (!pattern->ptr.str) { - memprintf(err, "out of memory while loading pattern"); - return 0; - } - - for (i = 0; *text[i]; i++) - s += sprintf(s, i?" %s":"%s", text[i]); - - pattern->len = len; - - return i; -} - -/* Free data allocated by acl_parse_reg */ -static void acl_free_reg(void *ptr) -{ - regex_free(ptr); -} - -/* Parse a regex. It is allocated. */ -int acl_parse_reg(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) -{ - regex *preg; - - preg = calloc(1, sizeof(*preg)); - - if (!preg) { - memprintf(err, "out of memory while loading pattern"); - return 0; - } - - if (!regex_comp(*text, preg, !(pattern->flags & ACL_PAT_F_IGNORE_CASE), 0, err)) { - free(preg); - return 0; - } - - pattern->ptr.reg = preg; - pattern->freeptrbuf = &acl_free_reg; - pattern->smp = smp; - return 1; -} - -/* Parse a range of positive integers delimited by either ':' or '-'. If only - * one integer is read, it is set as both min and max. An operator may be - * specified as the prefix, among this list of 5 : - * - * 0:eq, 1:gt, 2:ge, 3:lt, 4:le - * - * The default operator is "eq". It supports range matching. Ranges are - * rejected for other operators. The operator may be changed at any time. - * The operator is stored in the 'opaque' argument. - * - * If err is non-NULL, an error message will be returned there on errors and - * the caller will have to free it. - * - */ -int acl_parse_int(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) -{ - signed long long i; - unsigned int j, last, skip = 0; - const char *ptr = *text; - - pattern->type = SMP_T_UINT; - pattern->smp = smp; - while (!isdigit((unsigned char)*ptr)) { - switch (get_std_op(ptr)) { - case STD_OP_EQ: *opaque = 0; break; - case STD_OP_GT: *opaque = 1; break; - case STD_OP_GE: *opaque = 2; break; - case STD_OP_LT: *opaque = 3; break; - case STD_OP_LE: *opaque = 4; break; - default: - memprintf(err, "'%s' is neither a number nor a supported operator", ptr); - return 0; - } - - skip++; - ptr = text[skip]; - } - - last = i = 0; - while (1) { - j = *ptr++; - if ((j == '-' || j == ':') && !last) { - last++; - pattern->val.range.min = i; - i = 0; - continue; - } - j -= '0'; - if (j > 9) - // also catches the terminating zero - break; - i *= 10; - i += j; - } - - if (last && *opaque >= 1 && *opaque <= 4) { - /* having a range with a min or a max is absurd */ - memprintf(err, "integer range '%s' specified with a comparison operator", text[skip]); - return 0; - } - - if (!last) - pattern->val.range.min = i; - pattern->val.range.max = i; - - switch (*opaque) { - case 0: /* eq */ - pattern->val.range.min_set = 1; - pattern->val.range.max_set = 1; - break; - case 1: /* gt */ - pattern->val.range.min++; /* gt = ge + 1 */ - case 2: /* ge */ - pattern->val.range.min_set = 1; - pattern->val.range.max_set = 0; - break; - case 3: /* lt */ - pattern->val.range.max--; /* lt = le - 1 */ - case 4: /* le */ - pattern->val.range.min_set = 0; - pattern->val.range.max_set = 1; - break; - } - return skip + 1; -} - -/* Parse a range of positive 2-component versions delimited by either ':' or - * '-'. The version consists in a major and a minor, both of which must be - * smaller than 65536, because internally they will be represented as a 32-bit - * integer. - * If only one version is read, it is set as both min and max. Just like for - * pure integers, an operator may be specified as the prefix, among this list - * of 5 : - * - * 0:eq, 1:gt, 2:ge, 3:lt, 4:le - * - * The default operator is "eq". It supports range matching. Ranges are - * rejected for other operators. The operator may be changed at any time. - * The operator is stored in the 'opaque' argument. This allows constructs - * such as the following one : - * - * acl obsolete_ssl ssl_req_proto lt 3 - * acl unsupported_ssl ssl_req_proto gt 3.1 - * acl valid_ssl ssl_req_proto 3.0-3.1 - * - */ -int acl_parse_dotted_ver(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) -{ - signed long long i; - unsigned int j, last, skip = 0; - const char *ptr = *text; - - - while (!isdigit((unsigned char)*ptr)) { - switch (get_std_op(ptr)) { - case STD_OP_EQ: *opaque = 0; break; - case STD_OP_GT: *opaque = 1; break; - case STD_OP_GE: *opaque = 2; break; - case STD_OP_LT: *opaque = 3; break; - case STD_OP_LE: *opaque = 4; break; - default: - memprintf(err, "'%s' is neither a number nor a supported operator", ptr); - return 0; - } - - skip++; - ptr = text[skip]; - } - - last = i = 0; - while (1) { - j = *ptr++; - if (j == '.') { - /* minor part */ - if (i >= 65536) - return 0; - i <<= 16; - continue; - } - if ((j == '-' || j == ':') && !last) { - last++; - if (i < 65536) - i <<= 16; - pattern->val.range.min = i; - i = 0; - continue; - } - j -= '0'; - if (j > 9) - // also catches the terminating zero - break; - i = (i & 0xFFFF0000) + (i & 0xFFFF) * 10; - i += j; - } - - /* if we only got a major version, let's shift it now */ - if (i < 65536) - i <<= 16; - - if (last && *opaque >= 1 && *opaque <= 4) { - /* having a range with a min or a max is absurd */ - memprintf(err, "version range '%s' specified with a comparison operator", text[skip]); - return 0; - } - - pattern->smp = smp; - - if (!last) - pattern->val.range.min = i; - pattern->val.range.max = i; - - switch (*opaque) { - case 0: /* eq */ - pattern->val.range.min_set = 1; - pattern->val.range.max_set = 1; - break; - case 1: /* gt */ - pattern->val.range.min++; /* gt = ge + 1 */ - case 2: /* ge */ - pattern->val.range.min_set = 1; - pattern->val.range.max_set = 0; - break; - case 3: /* lt */ - pattern->val.range.max--; /* lt = le - 1 */ - case 4: /* le */ - pattern->val.range.min_set = 0; - pattern->val.range.max_set = 1; - break; - } - return skip + 1; -} - -/* Parse an IP address and an optional mask in the form addr[/mask]. - * The addr may either be an IPv4 address or a hostname. The mask - * may either be a dotted mask or a number of bits. Returns 1 if OK, - * otherwise 0. NOTE: IP address patterns are typed (IPV4/IPV6). - */ -int acl_parse_ip(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) -{ - struct eb_root *tree = NULL; - if (pattern->flags & ACL_PAT_F_TREE_OK) - tree = pattern->val.tree; - - if (str2net(*text, &pattern->val.ipv4.addr, &pattern->val.ipv4.mask)) { - unsigned int mask = ntohl(pattern->val.ipv4.mask.s_addr); - struct acl_idx_elt *node; - /* check if the mask is contiguous so that we can insert the - * network into the tree. A continuous mask has only ones on - * the left. This means that this mask + its lower bit added - * once again is null. - */ - pattern->type = SMP_T_IPV4; - if (mask + (mask & -mask) == 0 && tree) { - mask = mask ? 33 - flsnz(mask & -mask) : 0; /* equals cidr value */ - /* FIXME: insert / into the tree here */ - node = calloc(1, sizeof(*node) + 4); /* reserve 4 bytes for IPv4 address */ - if (!node) { - memprintf(err, "out of memory while loading IPv4 pattern"); - return 0; - } - node->smp = smp; - memcpy(node->node.key, &pattern->val.ipv4.addr, 4); /* network byte order */ - node->node.node.pfx = mask; - if (ebmb_insert_prefix(tree, &node->node, 4) != &node->node) - free(node); /* was a duplicate */ - pattern->flags |= ACL_PAT_F_TREE; - return 1; - } - return 1; - } - else if (str62net(*text, &pattern->val.ipv6.addr, &pattern->val.ipv6.mask)) { - /* no tree support right now */ - pattern->type = SMP_T_IPV6; - return 1; - } - else { - memprintf(err, "'%s' is not a valid IPv4 or IPv6 address", *text); - return 0; - } -} - /* * Registers the ACL keyword list as a list of valid keywords for next * parsing sessions. @@ -851,41 +103,6 @@ struct acl_keyword *find_acl_kw(const char *kw) return NULL; } -/* NB: does nothing if is NULL */ -static void free_pattern(struct acl_pattern *pat) -{ - if (!pat) - return; - - if (pat->ptr.ptr) { - if (pat->freeptrbuf) - pat->freeptrbuf(pat->ptr.ptr); - - free(pat->ptr.ptr); - } - - free(pat); -} - -static void free_pattern_list(struct list *head) -{ - struct acl_pattern *pat, *tmp; - list_for_each_entry_safe(pat, tmp, head, list) - free_pattern(pat); -} - -static void free_pattern_tree(struct eb_root *root) -{ - struct eb_node *node, *next; - node = eb_first(root); - while (node) { - next = eb_next(node); - eb_delete(node); - free(node); - node = next; - } -} - static struct acl_expr *prune_acl_expr(struct acl_expr *expr) { struct arg *arg; @@ -909,120 +126,6 @@ static struct acl_expr *prune_acl_expr(struct acl_expr *expr) return expr; } -/* return 1 if the process is ok - * return -1 if the parser fail. The err message is filled. - * return -2 if out of memory - */ -int acl_register_pattern(struct acl_expr *expr, char *text, - struct sample_storage *smp, - struct acl_pattern **pattern, - int patflags, char **err) -{ - const char *args[2]; - int opaque = 0; - - args[0] = text; - args[1] = ""; - - /* we keep the previous pattern along iterations as long as it's not used */ - if (!*pattern) - *pattern = (struct acl_pattern *)malloc(sizeof(**pattern)); - if (!*pattern) - return -1; - - memset(*pattern, 0, sizeof(**pattern)); - (*pattern)->flags = patflags; - - if (!((*pattern)->flags & ACL_PAT_F_IGNORE_CASE) && - (expr->match == acl_match_str || expr->match == acl_match_ip)) { - /* we pre-set the data pointer to the tree's head so that functions - * which are able to insert in a tree know where to do that. - */ - (*pattern)->flags |= ACL_PAT_F_TREE_OK; - (*pattern)->val.tree = &expr->pattern_tree; - } - - (*pattern)->type = SMP_TYPES; /* unspecified type by default */ - if (!expr->parse(args, *pattern, smp, &opaque, err)) - return -1; - - /* if the parser did not feed the tree, let's chain the pattern to the list */ - if (!((*pattern)->flags & ACL_PAT_F_TREE)) { - LIST_ADDQ(&expr->patterns, &(*pattern)->list); - *pattern = NULL; /* get a new one */ - } - - return 1; -} - -/* Reads patterns from a file. If is non-NULL, an error message will - * be returned there on errors and the caller will have to free it. - */ -static int acl_read_patterns_from_file(struct acl_expr *expr, - const char *filename, int patflags, - char **err) -{ - FILE *file; - char *c; - char *arg; - struct acl_pattern *pattern; - int ret = 0; - int line = 0; - int code; - - file = fopen(filename, "r"); - if (!file) { - memprintf(err, "failed to open pattern file <%s>", filename); - return 0; - } - - /* now parse all patterns. The file may contain only one pattern per - * line. If the line contains spaces, they will be part of the pattern. - * The pattern stops at the first CR, LF or EOF encountered. - */ - pattern = NULL; - while (fgets(trash.str, trash.size, file) != NULL) { - line++; - c = trash.str; - - /* ignore lines beginning with a dash */ - if (*c == '#') - continue; - - /* strip leading spaces and tabs */ - while (*c == ' ' || *c == '\t') - c++; - - - arg = c; - while (*c && *c != '\n' && *c != '\r') - c++; - *c = 0; - - /* empty lines are ignored too */ - if (c == arg) - continue; - - code = acl_register_pattern(expr, arg, NULL, &pattern, patflags, err); - if (code == -2) { - memprintf(err, "out of memory when loading patterns from file <%s>", filename); - goto out_close; - } - else if (code < 0) { - memprintf(err, "%s when loading patterns from file <%s>", *err, filename); - goto out_free_pattern; - } - } - - ret = 1; /* success */ - - out_free_pattern: - free_pattern(pattern); - out_close: - fclose(file); - return ret; -} - /* Parse an ACL expression starting at [0], and return it. If is * not NULL, it will be filled with a pointer to an error message in case of * error. This pointer must be freeable or NULL. is an arg_list serving @@ -1844,57 +947,6 @@ struct acl_cond *build_acl_cond(const char *file, int line, struct proxy *px, co return cond; } -/* This function execute the match part of the acl. It's applying - * acl on sample . is filled only if the pointer - * is not NULL. The function return ACL_PAT_FAIL, ACL_PAT_MISS or - * ACL_PAT_PASS - */ -inline int acl_exec_match(struct acl_expr *expr, struct sample *smp, - struct sample_storage **sample) -{ - int acl_res = ACL_PAT_FAIL; - struct acl_pattern *pattern; - struct ebmb_node *node = NULL; - struct acl_idx_elt *elt; - - if (expr->match == acl_match_nothing) { - if (smp->data.uint) - acl_res |= ACL_PAT_PASS; - else - acl_res |= ACL_PAT_FAIL; - } - else if (!expr->match) { - /* just check for existence */ - acl_res |= ACL_PAT_PASS; - } - else { - if (!eb_is_empty(&expr->pattern_tree)) { - /* a tree is present, let's check what type it is */ - if (expr->match == acl_match_str) - node = acl_lookup_str(smp, expr); - else if (expr->match == acl_match_ip) - node = acl_lookup_ip(smp, expr); - if (node) { - acl_res |= ACL_PAT_PASS; - elt = ebmb_entry(node, struct acl_idx_elt, node); - if (sample) - *sample = elt->smp; - } - } - - /* call the match() function for all tests on this value */ - list_for_each_entry(pattern, &expr->patterns, list) { - if (acl_res == ACL_PAT_PASS) - break; - acl_res |= expr->match(smp, pattern); - if (sample) - *sample = pattern->smp; - } - } - - return acl_res; -} - /* Execute condition and return either ACL_PAT_FAIL, ACL_PAT_MISS or * ACL_PAT_PASS depending on the test results. ACL_PAT_MISS may only be * returned if does not contain SMP_OPT_FINAL, indicating that incomplete diff --git a/src/pattern.c b/src/pattern.c new file mode 100644 index 000000000..6bf7d1d6b --- /dev/null +++ b/src/pattern.c @@ -0,0 +1,974 @@ +/* + * Pattern management functions. + * + * Copyright 2000-2013 Willy Tarreau + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include + +#include +#include + +#include +#include + +#include + +#include + +char *acl_match_names[ACL_MATCH_NUM] = { + [ACL_MATCH_FOUND] = "found", + [ACL_MATCH_BOOL] = "bool", + [ACL_MATCH_INT] = "int", + [ACL_MATCH_IP] = "ip", + [ACL_MATCH_BIN] = "bin", + [ACL_MATCH_LEN] = "len", + [ACL_MATCH_STR] = "str", + [ACL_MATCH_BEG] = "beg", + [ACL_MATCH_SUB] = "sub", + [ACL_MATCH_DIR] = "dir", + [ACL_MATCH_DOM] = "dom", + [ACL_MATCH_END] = "end", + [ACL_MATCH_REG] = "reg", +}; + +int (*acl_parse_fcts[ACL_MATCH_NUM])(const char **, struct acl_pattern *, struct sample_storage *, int *, char **) = { + [ACL_MATCH_FOUND] = acl_parse_nothing, + [ACL_MATCH_BOOL] = acl_parse_nothing, + [ACL_MATCH_INT] = acl_parse_int, + [ACL_MATCH_IP] = acl_parse_ip, + [ACL_MATCH_BIN] = acl_parse_bin, + [ACL_MATCH_LEN] = acl_parse_int, + [ACL_MATCH_STR] = acl_parse_str, + [ACL_MATCH_BEG] = acl_parse_str, + [ACL_MATCH_SUB] = acl_parse_str, + [ACL_MATCH_DIR] = acl_parse_str, + [ACL_MATCH_DOM] = acl_parse_str, + [ACL_MATCH_END] = acl_parse_str, + [ACL_MATCH_REG] = acl_parse_reg, +}; + +int (*acl_match_fcts[ACL_MATCH_NUM])(struct sample *, struct acl_pattern *) = { + [ACL_MATCH_FOUND] = NULL, + [ACL_MATCH_BOOL] = acl_match_nothing, + [ACL_MATCH_INT] = acl_match_int, + [ACL_MATCH_IP] = acl_match_ip, + [ACL_MATCH_BIN] = acl_match_bin, + [ACL_MATCH_LEN] = acl_match_len, + [ACL_MATCH_STR] = acl_match_str, + [ACL_MATCH_BEG] = acl_match_beg, + [ACL_MATCH_SUB] = acl_match_sub, + [ACL_MATCH_DIR] = acl_match_dir, + [ACL_MATCH_DOM] = acl_match_dom, + [ACL_MATCH_END] = acl_match_end, + [ACL_MATCH_REG] = acl_match_reg, +}; + +/* + * These functions are exported and may be used by any other component. + */ + +/* ignore the current line */ +int acl_parse_nothing(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) +{ + return 1; +} + +/* always return false */ +int acl_match_nothing(struct sample *smp, struct acl_pattern *pattern) +{ + return ACL_PAT_FAIL; +} + + +/* NB: For two strings to be identical, it is required that their lengths match */ +int acl_match_str(struct sample *smp, struct acl_pattern *pattern) +{ + int icase; + + if (pattern->len != smp->data.str.len) + return ACL_PAT_FAIL; + + icase = pattern->flags & ACL_PAT_F_IGNORE_CASE; + if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0) || + (!icase && strncmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0)) + return ACL_PAT_PASS; + return ACL_PAT_FAIL; +} + +/* NB: For two binaries buf to be identical, it is required that their lengths match */ +int acl_match_bin(struct sample *smp, struct acl_pattern *pattern) +{ + if (pattern->len != smp->data.str.len) + return ACL_PAT_FAIL; + + if (memcmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0) + return ACL_PAT_PASS; + return ACL_PAT_FAIL; +} + +/* Lookup a string in the expression's pattern tree. The node is returned if it + * exists, otherwise NULL. + */ +static void *acl_lookup_str(struct sample *smp, struct acl_expr *expr) +{ + /* data are stored in a tree */ + struct ebmb_node *node; + char prev; + + /* we may have to force a trailing zero on the test pattern */ + prev = smp->data.str.str[smp->data.str.len]; + if (prev) + smp->data.str.str[smp->data.str.len] = '\0'; + node = ebst_lookup(&expr->pattern_tree, smp->data.str.str); + if (prev) + smp->data.str.str[smp->data.str.len] = prev; + return node; +} + +/* Executes a regex. It temporarily changes the data to add a trailing zero, + * and restores the previous character when leaving. + */ +int acl_match_reg(struct sample *smp, struct acl_pattern *pattern) +{ + if (regex_exec(pattern->ptr.reg, smp->data.str.str, smp->data.str.len) == 0) + return ACL_PAT_PASS; + return ACL_PAT_FAIL; +} + +/* Checks that the pattern matches the beginning of the tested string. */ +int acl_match_beg(struct sample *smp, struct acl_pattern *pattern) +{ + int icase; + + if (pattern->len > smp->data.str.len) + return ACL_PAT_FAIL; + + icase = pattern->flags & ACL_PAT_F_IGNORE_CASE; + if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str, pattern->len) != 0) || + (!icase && strncmp(pattern->ptr.str, smp->data.str.str, pattern->len) != 0)) + return ACL_PAT_FAIL; + return ACL_PAT_PASS; +} + +/* Checks that the pattern matches the end of the tested string. */ +int acl_match_end(struct sample *smp, struct acl_pattern *pattern) +{ + int icase; + + if (pattern->len > smp->data.str.len) + return ACL_PAT_FAIL; + icase = pattern->flags & ACL_PAT_F_IGNORE_CASE; + if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str + smp->data.str.len - pattern->len, pattern->len) != 0) || + (!icase && strncmp(pattern->ptr.str, smp->data.str.str + smp->data.str.len - pattern->len, pattern->len) != 0)) + return ACL_PAT_FAIL; + return ACL_PAT_PASS; +} + +/* Checks that the pattern is included inside the tested string. + * NB: Suboptimal, should be rewritten using a Boyer-Moore method. + */ +int acl_match_sub(struct sample *smp, struct acl_pattern *pattern) +{ + int icase; + char *end; + char *c; + + if (pattern->len > smp->data.str.len) + return ACL_PAT_FAIL; + + end = smp->data.str.str + smp->data.str.len - pattern->len; + icase = pattern->flags & ACL_PAT_F_IGNORE_CASE; + if (icase) { + for (c = smp->data.str.str; c <= end; c++) { + if (tolower(*c) != tolower(*pattern->ptr.str)) + continue; + if (strncasecmp(pattern->ptr.str, c, pattern->len) == 0) + return ACL_PAT_PASS; + } + } else { + for (c = smp->data.str.str; c <= end; c++) { + if (*c != *pattern->ptr.str) + continue; + if (strncmp(pattern->ptr.str, c, pattern->len) == 0) + return ACL_PAT_PASS; + } + } + return ACL_PAT_FAIL; +} + +/* Background: Fast way to find a zero byte in a word + * http://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord + * hasZeroByte = (v - 0x01010101UL) & ~v & 0x80808080UL; + * + * To look for 4 different byte values, xor the word with those bytes and + * then check for zero bytes: + * + * v = (((unsigned char)c * 0x1010101U) ^ delimiter) + * where is the 4 byte values to look for (as an uint) + * and is the character that is being tested + */ +static inline unsigned int is_delimiter(unsigned char c, unsigned int mask) +{ + mask ^= (c * 0x01010101); /* propagate the char to all 4 bytes */ + return (mask - 0x01010101) & ~mask & 0x80808080U; +} + +static inline unsigned int make_4delim(unsigned char d1, unsigned char d2, unsigned char d3, unsigned char d4) +{ + return d1 << 24 | d2 << 16 | d3 << 8 | d4; +} + +/* This one is used by other real functions. It checks that the pattern is + * included inside the tested string, but enclosed between the specified + * delimiters or at the beginning or end of the string. The delimiters are + * provided as an unsigned int made by make_4delim() and match up to 4 different + * delimiters. Delimiters are stripped at the beginning and end of the pattern. + */ +static int match_word(struct sample *smp, struct acl_pattern *pattern, unsigned int delimiters) +{ + int may_match, icase; + char *c, *end; + char *ps; + int pl; + + pl = pattern->len; + ps = pattern->ptr.str; + + while (pl > 0 && is_delimiter(*ps, delimiters)) { + pl--; + ps++; + } + + while (pl > 0 && is_delimiter(ps[pl - 1], delimiters)) + pl--; + + if (pl > smp->data.str.len) + return ACL_PAT_FAIL; + + may_match = 1; + icase = pattern->flags & ACL_PAT_F_IGNORE_CASE; + end = smp->data.str.str + smp->data.str.len - pl; + for (c = smp->data.str.str; c <= end; c++) { + if (is_delimiter(*c, delimiters)) { + may_match = 1; + continue; + } + + if (!may_match) + continue; + + if (icase) { + if ((tolower(*c) == tolower(*ps)) && + (strncasecmp(ps, c, pl) == 0) && + (c == end || is_delimiter(c[pl], delimiters))) + return ACL_PAT_PASS; + } else { + if ((*c == *ps) && + (strncmp(ps, c, pl) == 0) && + (c == end || is_delimiter(c[pl], delimiters))) + return ACL_PAT_PASS; + } + may_match = 0; + } + return ACL_PAT_FAIL; +} + +/* Checks that the pattern is included inside the tested string, but enclosed + * between the delimiters '?' or '/' or at the beginning or end of the string. + * Delimiters at the beginning or end of the pattern are ignored. + */ +int acl_match_dir(struct sample *smp, struct acl_pattern *pattern) +{ + return match_word(smp, pattern, make_4delim('/', '?', '?', '?')); +} + +/* Checks that the pattern is included inside the tested string, but enclosed + * between the delmiters '/', '?', '.' or ":" or at the beginning or end of + * the string. Delimiters at the beginning or end of the pattern are ignored. + */ +int acl_match_dom(struct sample *smp, struct acl_pattern *pattern) +{ + return match_word(smp, pattern, make_4delim('/', '?', '.', ':')); +} + +/* Checks that the integer in is included between min and max */ +int acl_match_int(struct sample *smp, struct acl_pattern *pattern) +{ + if ((!pattern->val.range.min_set || pattern->val.range.min <= smp->data.uint) && + (!pattern->val.range.max_set || smp->data.uint <= pattern->val.range.max)) + return ACL_PAT_PASS; + return ACL_PAT_FAIL; +} + +/* Checks that the length of the pattern in is included between min and max */ +int acl_match_len(struct sample *smp, struct acl_pattern *pattern) +{ + if ((!pattern->val.range.min_set || pattern->val.range.min <= smp->data.str.len) && + (!pattern->val.range.max_set || smp->data.str.len <= pattern->val.range.max)) + return ACL_PAT_PASS; + return ACL_PAT_FAIL; +} + +int acl_match_ip(struct sample *smp, struct acl_pattern *pattern) +{ + unsigned int v4; /* in network byte order */ + struct in6_addr *v6; + int bits, pos; + struct in6_addr tmp6; + + if (pattern->type == SMP_T_IPV4) { + if (smp->type == SMP_T_IPV4) { + v4 = smp->data.ipv4.s_addr; + } + else if (smp->type == SMP_T_IPV6) { + /* v4 match on a V6 sample. We want to check at least for + * the following forms : + * - ::ffff:ip:v4 (ipv4 mapped) + * - ::0000:ip:v4 (old ipv4 mapped) + * - 2002:ip:v4:: (6to4) + */ + if (*(uint32_t*)&smp->data.ipv6.s6_addr[0] == 0 && + *(uint32_t*)&smp->data.ipv6.s6_addr[4] == 0 && + (*(uint32_t*)&smp->data.ipv6.s6_addr[8] == 0 || + *(uint32_t*)&smp->data.ipv6.s6_addr[8] == htonl(0xFFFF))) { + v4 = *(uint32_t*)&smp->data.ipv6.s6_addr[12]; + } + else if (*(uint16_t*)&smp->data.ipv6.s6_addr[0] == htons(0x2002)) { + v4 = htonl((ntohs(*(uint16_t*)&smp->data.ipv6.s6_addr[2]) << 16) + + ntohs(*(uint16_t*)&smp->data.ipv6.s6_addr[4])); + } + else + return ACL_PAT_FAIL; + } + else + return ACL_PAT_FAIL; + + if (((v4 ^ pattern->val.ipv4.addr.s_addr) & pattern->val.ipv4.mask.s_addr) == 0) + return ACL_PAT_PASS; + else + return ACL_PAT_FAIL; + } + else if (pattern->type == SMP_T_IPV6) { + if (smp->type == SMP_T_IPV4) { + /* Convert the IPv4 sample address to IPv4 with the + * mapping method using the ::ffff: prefix. + */ + memset(&tmp6, 0, 10); + *(uint16_t*)&tmp6.s6_addr[10] = htons(0xffff); + *(uint32_t*)&tmp6.s6_addr[12] = smp->data.ipv4.s_addr; + v6 = &tmp6; + } + else if (smp->type == SMP_T_IPV6) { + v6 = &smp->data.ipv6; + } + else { + return ACL_PAT_FAIL; + } + + bits = pattern->val.ipv6.mask; + for (pos = 0; bits > 0; pos += 4, bits -= 32) { + v4 = *(uint32_t*)&v6->s6_addr[pos] ^ *(uint32_t*)&pattern->val.ipv6.addr.s6_addr[pos]; + if (bits < 32) + v4 &= htonl((~0U) << (32-bits)); + if (v4) + return ACL_PAT_FAIL; + } + return ACL_PAT_PASS; + } + return ACL_PAT_FAIL; +} + +/* Lookup an IPv4 address in the expression's pattern tree using the longest + * match method. The node is returned if it exists, otherwise NULL. + */ +static void *acl_lookup_ip(struct sample *smp, struct acl_expr *expr) +{ + struct in_addr *s; + + if (smp->type != SMP_T_IPV4) + return ACL_PAT_FAIL; + + s = &smp->data.ipv4; + return ebmb_lookup_longest(&expr->pattern_tree, &s->s_addr); +} + +/* Parse a string. It is allocated and duplicated. */ +int acl_parse_str(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) +{ + int len; + + len = strlen(*text); + pattern->type = SMP_T_CSTR; + + if (pattern->flags & ACL_PAT_F_TREE_OK) { + /* we're allowed to put the data in a tree whose root is pointed + * to by val.tree. + */ + struct acl_idx_elt *node; + + node = calloc(1, sizeof(*node) + len + 1); + if (!node) { + memprintf(err, "out of memory while loading string pattern"); + return 0; + } + node->smp = smp; + memcpy(node->node.key, *text, len + 1); + if (ebst_insert(pattern->val.tree, &node->node) != &node->node) + free(node); /* was a duplicate */ + pattern->flags |= ACL_PAT_F_TREE; /* this pattern now contains a tree */ + return 1; + } + + pattern->ptr.str = strdup(*text); + pattern->smp = smp; + if (!pattern->ptr.str) { + memprintf(err, "out of memory while loading string pattern"); + return 0; + } + pattern->len = len; + return 1; +} + +/* Parse a binary written in hexa. It is allocated. */ +int acl_parse_bin(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) +{ + int len; + const char *p = *text; + int i,j; + + len = strlen(p); + if (len%2) { + memprintf(err, "an even number of hex digit is expected"); + return 0; + } + + pattern->type = SMP_T_CBIN; + pattern->len = len >> 1; + pattern->ptr.str = malloc(pattern->len); + pattern->smp = smp; + if (!pattern->ptr.str) { + memprintf(err, "out of memory while loading string pattern"); + return 0; + } + + i = j = 0; + while (j < pattern->len) { + if (!ishex(p[i++])) + goto bad_input; + if (!ishex(p[i++])) + goto bad_input; + pattern->ptr.str[j++] = (hex2i(p[i-2]) << 4) + hex2i(p[i-1]); + } + return 1; + +bad_input: + memprintf(err, "an hex digit is expected (found '%c')", p[i-1]); + free(pattern->ptr.str); + return 0; +} + +/* Parse and concatenate all further strings into one. */ +int +acl_parse_strcat(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) +{ + + int len = 0, i; + char *s; + + for (i = 0; *text[i]; i++) + len += strlen(text[i])+1; + + pattern->type = SMP_T_CSTR; + pattern->ptr.str = s = calloc(1, len); + pattern->smp = smp; + if (!pattern->ptr.str) { + memprintf(err, "out of memory while loading pattern"); + return 0; + } + + for (i = 0; *text[i]; i++) + s += sprintf(s, i?" %s":"%s", text[i]); + + pattern->len = len; + + return i; +} + +/* Free data allocated by acl_parse_reg */ +static void acl_free_reg(void *ptr) +{ + regex_free(ptr); +} + +/* Parse a regex. It is allocated. */ +int acl_parse_reg(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) +{ + regex *preg; + + preg = calloc(1, sizeof(*preg)); + + if (!preg) { + memprintf(err, "out of memory while loading pattern"); + return 0; + } + + if (!regex_comp(*text, preg, !(pattern->flags & ACL_PAT_F_IGNORE_CASE), 0, err)) { + free(preg); + return 0; + } + + pattern->ptr.reg = preg; + pattern->freeptrbuf = &acl_free_reg; + pattern->smp = smp; + return 1; +} + +/* Parse a range of positive integers delimited by either ':' or '-'. If only + * one integer is read, it is set as both min and max. An operator may be + * specified as the prefix, among this list of 5 : + * + * 0:eq, 1:gt, 2:ge, 3:lt, 4:le + * + * The default operator is "eq". It supports range matching. Ranges are + * rejected for other operators. The operator may be changed at any time. + * The operator is stored in the 'opaque' argument. + * + * If err is non-NULL, an error message will be returned there on errors and + * the caller will have to free it. + * + */ +int acl_parse_int(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) +{ + signed long long i; + unsigned int j, last, skip = 0; + const char *ptr = *text; + + pattern->type = SMP_T_UINT; + pattern->smp = smp; + while (!isdigit((unsigned char)*ptr)) { + switch (get_std_op(ptr)) { + case STD_OP_EQ: *opaque = 0; break; + case STD_OP_GT: *opaque = 1; break; + case STD_OP_GE: *opaque = 2; break; + case STD_OP_LT: *opaque = 3; break; + case STD_OP_LE: *opaque = 4; break; + default: + memprintf(err, "'%s' is neither a number nor a supported operator", ptr); + return 0; + } + + skip++; + ptr = text[skip]; + } + + last = i = 0; + while (1) { + j = *ptr++; + if ((j == '-' || j == ':') && !last) { + last++; + pattern->val.range.min = i; + i = 0; + continue; + } + j -= '0'; + if (j > 9) + // also catches the terminating zero + break; + i *= 10; + i += j; + } + + if (last && *opaque >= 1 && *opaque <= 4) { + /* having a range with a min or a max is absurd */ + memprintf(err, "integer range '%s' specified with a comparison operator", text[skip]); + return 0; + } + + if (!last) + pattern->val.range.min = i; + pattern->val.range.max = i; + + switch (*opaque) { + case 0: /* eq */ + pattern->val.range.min_set = 1; + pattern->val.range.max_set = 1; + break; + case 1: /* gt */ + pattern->val.range.min++; /* gt = ge + 1 */ + case 2: /* ge */ + pattern->val.range.min_set = 1; + pattern->val.range.max_set = 0; + break; + case 3: /* lt */ + pattern->val.range.max--; /* lt = le - 1 */ + case 4: /* le */ + pattern->val.range.min_set = 0; + pattern->val.range.max_set = 1; + break; + } + return skip + 1; +} + +/* Parse a range of positive 2-component versions delimited by either ':' or + * '-'. The version consists in a major and a minor, both of which must be + * smaller than 65536, because internally they will be represented as a 32-bit + * integer. + * If only one version is read, it is set as both min and max. Just like for + * pure integers, an operator may be specified as the prefix, among this list + * of 5 : + * + * 0:eq, 1:gt, 2:ge, 3:lt, 4:le + * + * The default operator is "eq". It supports range matching. Ranges are + * rejected for other operators. The operator may be changed at any time. + * The operator is stored in the 'opaque' argument. This allows constructs + * such as the following one : + * + * acl obsolete_ssl ssl_req_proto lt 3 + * acl unsupported_ssl ssl_req_proto gt 3.1 + * acl valid_ssl ssl_req_proto 3.0-3.1 + * + */ +int acl_parse_dotted_ver(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) +{ + signed long long i; + unsigned int j, last, skip = 0; + const char *ptr = *text; + + + while (!isdigit((unsigned char)*ptr)) { + switch (get_std_op(ptr)) { + case STD_OP_EQ: *opaque = 0; break; + case STD_OP_GT: *opaque = 1; break; + case STD_OP_GE: *opaque = 2; break; + case STD_OP_LT: *opaque = 3; break; + case STD_OP_LE: *opaque = 4; break; + default: + memprintf(err, "'%s' is neither a number nor a supported operator", ptr); + return 0; + } + + skip++; + ptr = text[skip]; + } + + last = i = 0; + while (1) { + j = *ptr++; + if (j == '.') { + /* minor part */ + if (i >= 65536) + return 0; + i <<= 16; + continue; + } + if ((j == '-' || j == ':') && !last) { + last++; + if (i < 65536) + i <<= 16; + pattern->val.range.min = i; + i = 0; + continue; + } + j -= '0'; + if (j > 9) + // also catches the terminating zero + break; + i = (i & 0xFFFF0000) + (i & 0xFFFF) * 10; + i += j; + } + + /* if we only got a major version, let's shift it now */ + if (i < 65536) + i <<= 16; + + if (last && *opaque >= 1 && *opaque <= 4) { + /* having a range with a min or a max is absurd */ + memprintf(err, "version range '%s' specified with a comparison operator", text[skip]); + return 0; + } + + pattern->smp = smp; + + if (!last) + pattern->val.range.min = i; + pattern->val.range.max = i; + + switch (*opaque) { + case 0: /* eq */ + pattern->val.range.min_set = 1; + pattern->val.range.max_set = 1; + break; + case 1: /* gt */ + pattern->val.range.min++; /* gt = ge + 1 */ + case 2: /* ge */ + pattern->val.range.min_set = 1; + pattern->val.range.max_set = 0; + break; + case 3: /* lt */ + pattern->val.range.max--; /* lt = le - 1 */ + case 4: /* le */ + pattern->val.range.min_set = 0; + pattern->val.range.max_set = 1; + break; + } + return skip + 1; +} + +/* Parse an IP address and an optional mask in the form addr[/mask]. + * The addr may either be an IPv4 address or a hostname. The mask + * may either be a dotted mask or a number of bits. Returns 1 if OK, + * otherwise 0. NOTE: IP address patterns are typed (IPV4/IPV6). + */ +int acl_parse_ip(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) +{ + struct eb_root *tree = NULL; + if (pattern->flags & ACL_PAT_F_TREE_OK) + tree = pattern->val.tree; + + if (str2net(*text, &pattern->val.ipv4.addr, &pattern->val.ipv4.mask)) { + unsigned int mask = ntohl(pattern->val.ipv4.mask.s_addr); + struct acl_idx_elt *node; + /* check if the mask is contiguous so that we can insert the + * network into the tree. A continuous mask has only ones on + * the left. This means that this mask + its lower bit added + * once again is null. + */ + pattern->type = SMP_T_IPV4; + if (mask + (mask & -mask) == 0 && tree) { + mask = mask ? 33 - flsnz(mask & -mask) : 0; /* equals cidr value */ + /* FIXME: insert / into the tree here */ + node = calloc(1, sizeof(*node) + 4); /* reserve 4 bytes for IPv4 address */ + if (!node) { + memprintf(err, "out of memory while loading IPv4 pattern"); + return 0; + } + node->smp = smp; + memcpy(node->node.key, &pattern->val.ipv4.addr, 4); /* network byte order */ + node->node.node.pfx = mask; + if (ebmb_insert_prefix(tree, &node->node, 4) != &node->node) + free(node); /* was a duplicate */ + pattern->flags |= ACL_PAT_F_TREE; + return 1; + } + return 1; + } + else if (str62net(*text, &pattern->val.ipv6.addr, &pattern->val.ipv6.mask)) { + /* no tree support right now */ + pattern->type = SMP_T_IPV6; + return 1; + } + else { + memprintf(err, "'%s' is not a valid IPv4 or IPv6 address", *text); + return 0; + } +} + +/* NB: does nothing if is NULL */ +void free_pattern(struct acl_pattern *pat) +{ + if (!pat) + return; + + if (pat->ptr.ptr) { + if (pat->freeptrbuf) + pat->freeptrbuf(pat->ptr.ptr); + + free(pat->ptr.ptr); + } + + free(pat); +} + +void free_pattern_list(struct list *head) +{ + struct acl_pattern *pat, *tmp; + list_for_each_entry_safe(pat, tmp, head, list) + free_pattern(pat); +} + +void free_pattern_tree(struct eb_root *root) +{ + struct eb_node *node, *next; + node = eb_first(root); + while (node) { + next = eb_next(node); + eb_delete(node); + free(node); + node = next; + } +} + +/* return 1 if the process is ok + * return -1 if the parser fail. The err message is filled. + * return -2 if out of memory + */ +int acl_register_pattern(struct acl_expr *expr, char *text, + struct sample_storage *smp, + struct acl_pattern **pattern, + int patflags, char **err) +{ + const char *args[2]; + int opaque = 0; + + args[0] = text; + args[1] = ""; + + /* we keep the previous pattern along iterations as long as it's not used */ + if (!*pattern) + *pattern = (struct acl_pattern *)malloc(sizeof(**pattern)); + if (!*pattern) + return -1; + + memset(*pattern, 0, sizeof(**pattern)); + (*pattern)->flags = patflags; + + if (!((*pattern)->flags & ACL_PAT_F_IGNORE_CASE) && + (expr->match == acl_match_str || expr->match == acl_match_ip)) { + /* we pre-set the data pointer to the tree's head so that functions + * which are able to insert in a tree know where to do that. + */ + (*pattern)->flags |= ACL_PAT_F_TREE_OK; + (*pattern)->val.tree = &expr->pattern_tree; + } + + (*pattern)->type = SMP_TYPES; /* unspecified type by default */ + if (!expr->parse(args, *pattern, smp, &opaque, err)) + return -1; + + /* if the parser did not feed the tree, let's chain the pattern to the list */ + if (!((*pattern)->flags & ACL_PAT_F_TREE)) { + LIST_ADDQ(&expr->patterns, &(*pattern)->list); + *pattern = NULL; /* get a new one */ + } + + return 1; +} + +/* Reads patterns from a file. If is non-NULL, an error message will + * be returned there on errors and the caller will have to free it. + */ +int acl_read_patterns_from_file(struct acl_expr *expr, + const char *filename, int patflags, + char **err) +{ + FILE *file; + char *c; + char *arg; + struct acl_pattern *pattern; + int ret = 0; + int line = 0; + int code; + + file = fopen(filename, "r"); + if (!file) { + memprintf(err, "failed to open pattern file <%s>", filename); + return 0; + } + + /* now parse all patterns. The file may contain only one pattern per + * line. If the line contains spaces, they will be part of the pattern. + * The pattern stops at the first CR, LF or EOF encountered. + */ + pattern = NULL; + while (fgets(trash.str, trash.size, file) != NULL) { + line++; + c = trash.str; + + /* ignore lines beginning with a dash */ + if (*c == '#') + continue; + + /* strip leading spaces and tabs */ + while (*c == ' ' || *c == '\t') + c++; + + + arg = c; + while (*c && *c != '\n' && *c != '\r') + c++; + *c = 0; + + /* empty lines are ignored too */ + if (c == arg) + continue; + + code = acl_register_pattern(expr, arg, NULL, &pattern, patflags, err); + if (code == -2) { + memprintf(err, "out of memory when loading patterns from file <%s>", filename); + goto out_close; + } + else if (code < 0) { + memprintf(err, "%s when loading patterns from file <%s>", *err, filename); + goto out_free_pattern; + } + } + + ret = 1; /* success */ + + out_free_pattern: + free_pattern(pattern); + out_close: + fclose(file); + return ret; +} + +/* This function execute the match part of the acl. It's applying + * acl on sample . is filled only if the pointer + * is not NULL. The function return ACL_PAT_FAIL, ACL_PAT_MISS or + * ACL_PAT_PASS + */ +inline int acl_exec_match(struct acl_expr *expr, struct sample *smp, + struct sample_storage **sample) +{ + int acl_res = ACL_PAT_FAIL; + struct acl_pattern *pattern; + struct ebmb_node *node = NULL; + struct acl_idx_elt *elt; + + if (expr->match == acl_match_nothing) { + if (smp->data.uint) + acl_res |= ACL_PAT_PASS; + else + acl_res |= ACL_PAT_FAIL; + } + else if (!expr->match) { + /* just check for existence */ + acl_res |= ACL_PAT_PASS; + } + else { + if (!eb_is_empty(&expr->pattern_tree)) { + /* a tree is present, let's check what type it is */ + if (expr->match == acl_match_str) + node = acl_lookup_str(smp, expr); + else if (expr->match == acl_match_ip) + node = acl_lookup_ip(smp, expr); + if (node) { + acl_res |= ACL_PAT_PASS; + elt = ebmb_entry(node, struct acl_idx_elt, node); + if (sample) + *sample = elt->smp; + } + } + + /* call the match() function for all tests on this value */ + list_for_each_entry(pattern, &expr->patterns, list) { + if (acl_res == ACL_PAT_PASS) + break; + acl_res |= expr->match(smp, pattern); + if (sample) + *sample = pattern->smp; + } + } + + return acl_res; +} + diff --git a/src/payload.c b/src/payload.c index de6395f04..435602513 100644 --- a/src/payload.c +++ b/src/payload.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include diff --git a/src/proto_http.c b/src/proto_http.c index e6d5fc54b..6a3d31538 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include diff --git a/src/ssl_sock.c b/src/ssl_sock.c index e1e18c31b..fc92af1b2 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include