diff --git a/include/proto/acl.h b/include/proto/acl.h index 008e848f3..5e2e9ee28 100644 --- a/include/proto/acl.h +++ b/include/proto/acl.h @@ -125,7 +125,7 @@ int acl_cond_kw_conflicts(const struct acl_cond *cond, unsigned int where, struc * fails, with message filled. It returns -2 in "out of memory" * error case. */ -int acl_register_pattern(struct acl_expr *expr, char *text, struct acl_pattern **pattern, int patflags, char **err); +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 @@ -138,10 +138,12 @@ 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 return ACL_PAT_FAIL, ACL_PAT_MISS or ACL_PAT_PASS +/* 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); +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 @@ -168,7 +170,7 @@ int init_acl(); /* ignore the current line */ -int acl_parse_nothing(const char **text, struct acl_pattern *pattern, int *opaque, char **err); +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); @@ -183,34 +185,34 @@ int acl_match_len(struct sample *smp, struct acl_pattern *pattern); 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, int *opaque, char **err); +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, int *opaque, char **err); +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, int *opaque, char **err); +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, int *opaque, char **err); +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, int *opaque, char **err); +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, int *opaque, char **err); +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, int *opaque, char **err); +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, int *opaque, char **err); +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); diff --git a/include/types/acl.h b/include/types/acl.h index 3b3a52bd7..322e74b97 100644 --- a/include/types/acl.h +++ b/include/types/acl.h @@ -105,6 +105,14 @@ struct acl_time { 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 @@ -142,6 +150,9 @@ struct acl_pattern { 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 */ @@ -164,7 +175,7 @@ struct acl_expr; struct acl_keyword { const char *kw; char *fetch_kw; - int (*parse)(const char **text, struct acl_pattern *pattern, int *opaque, char **err); + int (*parse)(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); int (*match)(struct sample *smp, struct acl_pattern *pattern); /* must be after the config params */ struct sample_fetch *smp; /* the sample fetch we depend on */ @@ -190,7 +201,7 @@ struct acl_kw_list { * are grouped together in order to optimize caching. */ struct acl_expr { - int (*parse)(const char **text, struct acl_pattern *pattern, int *opaque, char **err); + int (*parse)(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err); int (*match)(struct sample *smp, struct acl_pattern *pattern); struct sample_expr *smp; /* the sample expression we depend on */ struct list patterns; /* list of acl_patterns */ @@ -232,7 +243,7 @@ struct acl_cond { }; extern char *acl_match_names[ACL_MATCH_NUM]; -extern int (*acl_parse_fcts[ACL_MATCH_NUM])(const char **, struct acl_pattern *, int *, char **); +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/src/acl.c b/src/acl.c index 27c680450..ca24693df 100644 --- a/src/acl.c +++ b/src/acl.c @@ -53,7 +53,7 @@ char *acl_match_names[ACL_MATCH_NUM] = { [ACL_MATCH_REG] = "reg", }; -int (*acl_parse_fcts[ACL_MATCH_NUM])(const char **, struct acl_pattern *, int *, char **) = { +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, @@ -101,7 +101,7 @@ static int acl_find_match_name(const char *name) */ /* ignore the current line */ -int acl_parse_nothing(const char **text, struct acl_pattern *pattern, int *opaque, char **err) +int acl_parse_nothing(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) { return 1; } @@ -426,7 +426,7 @@ static void *acl_lookup_ip(struct sample *smp, struct acl_expr *expr) } /* Parse a string. It is allocated and duplicated. */ -int acl_parse_str(const char **text, struct acl_pattern *pattern, int *opaque, char **err) +int acl_parse_str(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) { int len; @@ -437,21 +437,23 @@ int acl_parse_str(const char **text, struct acl_pattern *pattern, int *opaque, c /* we're allowed to put the data in a tree whose root is pointed * to by val.tree. */ - struct ebmb_node *node; + 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; } - memcpy(node->key, *text, len + 1); - if (ebst_insert(pattern->val.tree, node) != node) + 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; @@ -461,7 +463,7 @@ int acl_parse_str(const char **text, struct acl_pattern *pattern, int *opaque, c } /* Parse a binary written in hexa. It is allocated. */ -int acl_parse_bin(const char **text, struct acl_pattern *pattern, int *opaque, char **err) +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; @@ -476,6 +478,7 @@ int acl_parse_bin(const char **text, struct acl_pattern *pattern, int *opaque, c 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; @@ -499,7 +502,7 @@ int acl_parse_bin(const char **text, struct acl_pattern *pattern, int *opaque, c /* Parse and concatenate all further strings into one. */ int -acl_parse_strcat(const char **text, struct acl_pattern *pattern, int *opaque, char **err) +acl_parse_strcat(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) { int len = 0, i; @@ -510,6 +513,7 @@ acl_parse_strcat(const char **text, struct acl_pattern *pattern, int *opaque, ch 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; @@ -530,7 +534,7 @@ static void acl_free_reg(void *ptr) } /* Parse a regex. It is allocated. */ -int acl_parse_reg(const char **text, struct acl_pattern *pattern, int *opaque, char **err) +int acl_parse_reg(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) { regex *preg; @@ -548,6 +552,7 @@ int acl_parse_reg(const char **text, struct acl_pattern *pattern, int *opaque, c pattern->ptr.reg = preg; pattern->freeptrbuf = &acl_free_reg; + pattern->smp = smp; return 1; } @@ -565,13 +570,14 @@ int acl_parse_reg(const char **text, struct acl_pattern *pattern, int *opaque, c * the caller will have to free it. * */ -int acl_parse_int(const char **text, struct acl_pattern *pattern, int *opaque, char **err) +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; @@ -656,7 +662,7 @@ int acl_parse_int(const char **text, struct acl_pattern *pattern, int *opaque, c * acl valid_ssl ssl_req_proto 3.0-3.1 * */ -int acl_parse_dotted_ver(const char **text, struct acl_pattern *pattern, int *opaque, char **err) +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; @@ -715,6 +721,8 @@ int acl_parse_dotted_ver(const char **text, struct acl_pattern *pattern, int *op return 0; } + pattern->smp = smp; + if (!last) pattern->val.range.min = i; pattern->val.range.max = i; @@ -745,7 +753,7 @@ int acl_parse_dotted_ver(const char **text, struct acl_pattern *pattern, int *op * 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, int *opaque, char **err) +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) @@ -753,7 +761,7 @@ int acl_parse_ip(const char **text, struct acl_pattern *pattern, int *opaque, ch if (str2net(*text, &pattern->val.ipv4.addr, &pattern->val.ipv4.mask)) { unsigned int mask = ntohl(pattern->val.ipv4.mask.s_addr); - struct ebmb_node *node; + 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 @@ -768,9 +776,10 @@ int acl_parse_ip(const char **text, struct acl_pattern *pattern, int *opaque, ch memprintf(err, "out of memory while loading IPv4 pattern"); return 0; } - memcpy(node->key, &pattern->val.ipv4.addr, 4); /* network byte order */ - node->node.pfx = mask; - if (ebmb_insert_prefix(tree, node, 4) != node) + 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; @@ -905,6 +914,7 @@ static struct acl_expr *prune_acl_expr(struct acl_expr *expr) * 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) { @@ -933,7 +943,7 @@ int acl_register_pattern(struct acl_expr *expr, char *text, } (*pattern)->type = SMP_TYPES; /* unspecified type by default */ - if (!expr->parse(args, *pattern, &opaque, err)) + 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 */ @@ -993,7 +1003,7 @@ static int acl_read_patterns_from_file(struct acl_expr *expr, if (c == arg) continue; - code = acl_register_pattern(expr, arg, &pattern, patflags, err); + 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; @@ -1414,7 +1424,7 @@ struct acl_expr *parse_acl_expr(const char **args, char **err, struct arg_list * pattern->flags = patflags; pattern->type = SMP_TYPES; /* unspecified type */ - ret = expr->parse(args, pattern, &opaque, err); + ret = expr->parse(args, pattern, NULL, &opaque, err); if (!ret) goto out_free_pattern; @@ -1834,13 +1844,18 @@ 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 return ACL_PAT_FAIL, ACL_PAT_MISS or ACL_PAT_PASS +/* 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) +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) @@ -1856,9 +1871,15 @@ inline int acl_exec_match(struct acl_expr *expr, struct sample *smp) if (!eb_is_empty(&expr->pattern_tree)) { /* a tree is present, let's check what type it is */ if (expr->match == acl_match_str) - acl_res |= acl_lookup_str(smp, expr) ? ACL_PAT_PASS : ACL_PAT_FAIL; + node = acl_lookup_str(smp, expr); else if (expr->match == acl_match_ip) - acl_res |= acl_lookup_ip(smp, expr) ? ACL_PAT_PASS : ACL_PAT_FAIL; + 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 */ @@ -1866,6 +1887,8 @@ inline int acl_exec_match(struct acl_expr *expr, struct sample *smp) if (acl_res == ACL_PAT_PASS) break; acl_res |= expr->match(smp, pattern); + if (sample) + *sample = pattern->smp; } } @@ -1937,7 +1960,7 @@ int acl_exec_cond(struct acl_cond *cond, struct proxy *px, struct session *l4, v continue; } - acl_res |= acl_exec_match(expr, &smp); + acl_res |= acl_exec_match(expr, &smp, NULL); /* * OK now acl_res holds the result of this expression * as one of ACL_PAT_FAIL, ACL_PAT_MISS or ACL_PAT_PASS. diff --git a/src/proto_http.c b/src/proto_http.c index b5ad54c84..e6d5fc54b 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -8920,13 +8920,14 @@ 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 acl_parse_meth(const char **text, struct acl_pattern *pattern, int *opaque, char **err) +static int acl_parse_meth(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err) { int len, meth; len = strlen(*text); meth = find_http_meth(*text, len); + pattern->smp = smp; pattern->val.i = meth; if (meth == HTTP_METH_OTHER) { pattern->ptr.str = strdup(*text);