From 0b2fe4a5cd8f694ce1d79682e96bfc861b0fcfbf Mon Sep 17 00:00:00 2001 From: Thierry FOURNIER Date: Fri, 6 Dec 2013 20:33:50 +0100 Subject: [PATCH] MINOR: pattern: add support for compiling patterns for lookups With this patch, patterns can be compiled for two modes : - match - lookup The match mode is used for example in ACLs or maps. The lookup mode is used to lookup a key for pattern maintenance. For example, looking up a network is different from looking up one address belonging to this network. A special case is made for regex. In lookup mode they return the input regex string and do not compile the regex. --- include/proto/pattern.h | 20 ++++----- include/types/acl.h | 2 +- include/types/pattern.h | 13 +++++- src/pattern.c | 99 +++++++++++++++++++++++++++++------------ src/proto_http.c | 24 +++++++--- 5 files changed, 110 insertions(+), 48 deletions(-) diff --git a/include/proto/pattern.h b/include/proto/pattern.h index 80d4ee1fe..ee193929a 100644 --- a/include/proto/pattern.h +++ b/include/proto/pattern.h @@ -68,7 +68,7 @@ enum pat_match_res pattern_exec_match(struct pattern_expr *expr, struct sample * /* ignore the current line */ -int pat_parse_nothing(const char **text, struct pattern *pattern, int *opaque, char **err); +int pat_parse_nothing(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err); /* NB: For two strings to be identical, it is required that their lengths match */ enum pat_match_res pat_match_str(struct sample *smp, struct pattern *pattern); @@ -83,37 +83,37 @@ enum pat_match_res pat_match_len(struct sample *smp, struct pattern *pattern); enum pat_match_res pat_match_int(struct sample *smp, struct pattern *pattern); /* Parse an integer. It is put both in min and max. */ -int pat_parse_int(const char **text, struct pattern *pattern, int *opaque, char **err); +int pat_parse_int(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err); /* Parse len like an integer, but specify expected string type */ -int pat_parse_len(const char **text, struct pattern *pattern, int *opaque, char **err); +int pat_parse_len(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err); /* Parse an version. It is put both in min and max. */ -int pat_parse_dotted_ver(const char **text, struct pattern *pattern, int *opaque, char **err); +int pat_parse_dotted_ver(const char **text, struct pattern *pattern, enum pat_usage usage, 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 pat_parse_range(const char **text, struct pattern *pattern, int *opaque, char **err); +int pat_parse_range(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err); /* Parse a string. It is allocated and duplicated. */ -int pat_parse_str(const char **text, struct pattern *pattern, int *opaque, char **err); +int pat_parse_str(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err); /* Parse a hexa binary definition. It is allocated and duplicated. */ -int pat_parse_bin(const char **text, struct pattern *pattern, int *opaque, char **err); +int pat_parse_bin(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err); /* Parse and concatenate strings into one. It is allocated and duplicated. */ -int pat_parse_strcat(const char **text, struct pattern *pattern, int *opaque, char **err); +int pat_parse_strcat(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err); /* Parse a regex. It is allocated. */ -int pat_parse_reg(const char **text, struct pattern *pattern, int *opaque, char **err); +int pat_parse_reg(const char **text, struct pattern *pattern, enum pat_usage usage, 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 pat_parse_ip(const char **text, struct pattern *pattern, int *opaque, char **err); +int pat_parse_ip(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err); /* always return false */ enum pat_match_res pat_match_nothing(struct sample *smp, struct pattern *pattern); diff --git a/include/types/acl.h b/include/types/acl.h index 9692015ba..afce9393c 100644 --- a/include/types/acl.h +++ b/include/types/acl.h @@ -92,7 +92,7 @@ struct acl_expr; struct acl_keyword { const char *kw; char *fetch_kw; - int (*parse)(const char **text, struct pattern *pattern, int *opaque, char **err); + int (*parse)(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err); enum pat_match_res (*match)(struct sample *smp, struct pattern *pattern); /* must be after the config params */ struct sample_fetch *smp; /* the sample fetch we depend on */ diff --git a/include/types/pattern.h b/include/types/pattern.h index 7065b6b9c..4e893724f 100644 --- a/include/types/pattern.h +++ b/include/types/pattern.h @@ -61,6 +61,15 @@ enum pat_match_res { PAT_MATCH = 3, /* sample matched at least one pattern */ }; +/* This enum describe the running mode of the function pat_parse_*(). + * The lookup mode does not allocate memory. The compile mode allocate + * memory and create any data + */ +enum pat_usage { + PAT_U_LOOKUP, + PAT_U_COMPILE, +}; + /* possible flags for expressions or patterns */ enum { PAT_F_IGNORE_CASE = 1 << 0, /* ignore case */ @@ -151,14 +160,14 @@ struct pattern { * are grouped together in order to optimize caching. */ struct pattern_expr { - int (*parse)(const char **text, struct pattern *pattern, int *opaque, char **err); + int (*parse)(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err); enum pat_match_res (*match)(struct sample *smp, struct pattern *pattern); struct list patterns; /* list of acl_patterns */ struct eb_root pattern_tree; /* may be used for lookup in large datasets */ }; extern char *pat_match_names[PAT_MATCH_NUM]; -extern int (*pat_parse_fcts[PAT_MATCH_NUM])(const char **, struct pattern *, int *, char **); +extern int (*pat_parse_fcts[PAT_MATCH_NUM])(const char **, struct pattern *, enum pat_usage, int *, char **); extern enum pat_match_res (*pat_match_fcts[PAT_MATCH_NUM])(struct sample *, struct pattern *); extern int pat_match_types[PAT_MATCH_NUM]; diff --git a/src/pattern.c b/src/pattern.c index 3ac8f0361..43011cd09 100644 --- a/src/pattern.c +++ b/src/pattern.c @@ -40,7 +40,7 @@ char *pat_match_names[PAT_MATCH_NUM] = { [PAT_MATCH_REG] = "reg", }; -int (*pat_parse_fcts[PAT_MATCH_NUM])(const char **, struct pattern *, int *, char **) = { +int (*pat_parse_fcts[PAT_MATCH_NUM])(const char **, struct pattern *, enum pat_usage, int *, char **) = { [PAT_MATCH_FOUND] = pat_parse_nothing, [PAT_MATCH_BOOL] = pat_parse_nothing, [PAT_MATCH_INT] = pat_parse_int, @@ -94,7 +94,7 @@ int pat_match_types[PAT_MATCH_NUM] = { */ /* ignore the current line */ -int pat_parse_nothing(const char **text, struct pattern *pattern, int *opaque, char **err) +int pat_parse_nothing(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err) { return 1; } @@ -419,45 +419,70 @@ static void *pat_lookup_ip(struct sample *smp, struct pattern_expr *expr) } /* Parse a string. It is allocated and duplicated. */ -int pat_parse_str(const char **text, struct pattern *pattern, int *opaque, char **err) +int pat_parse_str(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err) { pattern->type = SMP_T_CSTR; pattern->expect_type = SMP_T_CSTR; - pattern->ptr.str = strdup(*text); - if (!pattern->ptr.str) { - memprintf(err, "out of memory while loading string pattern"); - return 0; + if (usage == PAT_U_COMPILE) { + pattern->ptr.str = strdup(*text); + if (!pattern->ptr.str) { + memprintf(err, "out of memory while loading string pattern"); + return 0; + } } + else + pattern->ptr.str = (char *)*text; pattern->len = strlen(*text); return 1; } /* Parse a binary written in hexa. It is allocated. */ -int pat_parse_bin(const char **text, struct pattern *pattern, int *opaque, char **err) +int pat_parse_bin(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err) { + struct chunk *trash; + pattern->type = SMP_T_CBIN; pattern->expect_type = SMP_T_CBIN; + if (usage == PAT_U_COMPILE) + return parse_binary(*text, &pattern->ptr.str, &pattern->len, err); + + trash = get_trash_chunk(); + pattern->len = trash->size; + pattern->ptr.str = trash->str; return parse_binary(*text, &pattern->ptr.str, &pattern->len, err); } /* Parse and concatenate all further strings into one. */ int -pat_parse_strcat(const char **text, struct pattern *pattern, int *opaque, char **err) +pat_parse_strcat(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err) { - int len = 0, i; char *s; + struct chunk *trash; for (i = 0; *text[i]; i++) len += strlen(text[i])+1; pattern->type = SMP_T_CSTR; - pattern->ptr.str = s = calloc(1, len); - if (!pattern->ptr.str) { - memprintf(err, "out of memory while loading pattern"); - return 0; + if (usage == PAT_U_COMPILE) { + pattern->ptr.str = calloc(1, len); + if (!pattern->ptr.str) { + memprintf(err, "out of memory while loading pattern"); + return 0; + } } + else { + trash = get_trash_chunk(); + if (trash->size < len) { + memprintf(err, "no space avalaible in the buffer. expect %d, provides %d", + len, trash->size); + return 0; + } + pattern->ptr.str = trash->str; + } + + s = pattern->ptr.str; for (i = 0; *text[i]; i++) s += sprintf(s, i?" %s":"%s", text[i]); @@ -474,24 +499,40 @@ static void pat_free_reg(void *ptr) } /* Parse a regex. It is allocated. */ -int pat_parse_reg(const char **text, struct pattern *pattern, int *opaque, char **err) +int pat_parse_reg(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err) { struct my_regex *preg; + struct chunk *trash; - preg = calloc(1, sizeof(*preg)); + if (usage == PAT_U_COMPILE) { - if (!preg) { - memprintf(err, "out of memory while loading pattern"); - return 0; + preg = calloc(1, sizeof(*preg)); + if (!preg) { + memprintf(err, "out of memory while loading pattern"); + return 0; + } + + if (!regex_comp(*text, preg, !(pattern->flags & PAT_F_IGNORE_CASE), 0, err)) { + free(preg); + return 0; + } + pattern->freeptrbuf = &pat_free_reg; } + else { - if (!regex_comp(*text, preg, !(pattern->flags & PAT_F_IGNORE_CASE), 0, err)) { - free(preg); - return 0; + trash = get_trash_chunk(); + if (trash->size < sizeof(*preg)) { + memprintf(err, "no space avalaible in the buffer. expect %d, provides %d", + (int)sizeof(*preg), trash->size); + return 0; + } + + preg = (struct my_regex *)trash->str; + preg->regstr = (char *)*text; + pattern->freeptrbuf = NULL; } pattern->ptr.reg = preg; - pattern->freeptrbuf = &pat_free_reg; pattern->expect_type = SMP_T_CSTR; return 1; } @@ -510,7 +551,7 @@ int pat_parse_reg(const char **text, struct pattern *pattern, int *opaque, char * the caller will have to free it. * */ -int pat_parse_int(const char **text, struct pattern *pattern, int *opaque, char **err) +int pat_parse_int(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err) { signed long long i; unsigned int j, last, skip = 0; @@ -583,11 +624,11 @@ int pat_parse_int(const char **text, struct pattern *pattern, int *opaque, char return skip + 1; } -int pat_parse_len(const char **text, struct pattern *pattern, int *opaque, char **err) +int pat_parse_len(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err) { int ret; - ret = pat_parse_int(text, pattern, opaque, err); + ret = pat_parse_int(text, pattern, usage, opaque, err); pattern->expect_type = SMP_T_CSTR; return ret; } @@ -612,7 +653,7 @@ int pat_parse_len(const char **text, struct pattern *pattern, int *opaque, char * acl valid_ssl ssl_req_proto 3.0-3.1 * */ -int pat_parse_dotted_ver(const char **text, struct pattern *pattern, int *opaque, char **err) +int pat_parse_dotted_ver(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err) { signed long long i; unsigned int j, last, skip = 0; @@ -703,7 +744,7 @@ int pat_parse_dotted_ver(const char **text, struct pattern *pattern, int *opaque * 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 pat_parse_ip(const char **text, struct pattern *pattern, int *opaque, char **err) +int pat_parse_ip(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err) { pattern->expect_type = SMP_T_ADDR; if (str2net(*text, &pattern->val.ipv4.addr, &pattern->val.ipv4.mask)) { @@ -801,7 +842,7 @@ int pattern_register(struct pattern_expr *expr, const char **args, memset(*pattern, 0, sizeof(**pattern)); (*pattern)->flags = patflags; - ret = expr->parse(args, *pattern, &opaque, err); + ret = expr->parse(args, *pattern, PAT_U_COMPILE, &opaque, err); if (!ret) return 0; diff --git a/src/proto_http.c b/src/proto_http.c index 2ce0d33a4..b7982bce3 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -8735,21 +8735,33 @@ smp_prefetch_http(struct proxy *px, struct session *s, void *l7, unsigned int op * We use the pre-parsed method if it is known, and store its number as an * integer. If it is unknown, we use the pointer and the length. */ -static int pat_parse_meth(const char **text, struct pattern *pattern, int *opaque, char **err) +static int pat_parse_meth(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err) { int len, meth; + struct chunk *trash; len = strlen(*text); meth = find_http_meth(*text, len); pattern->val.i = meth; if (meth == HTTP_METH_OTHER) { - pattern->ptr.str = strdup(*text); - pattern->expect_type = SMP_T_CSTR; - if (!pattern->ptr.str) { - memprintf(err, "out of memory while loading pattern"); - return 0; + if (usage == PAT_U_COMPILE) { + pattern->ptr.str = strdup(*text); + if (!pattern->ptr.str) { + memprintf(err, "out of memory while loading pattern"); + return 0; + } } + else { + trash = get_trash_chunk(); + if (trash->size < len) { + memprintf(err, "no space avalaible in the buffer. expect %d, provides %d", + len, trash->size); + return 0; + } + pattern->ptr.str = trash->str; + } + pattern->expect_type = SMP_T_CSTR; pattern->len = len; } else