From: Willy Tarreau Date: Thu, 26 Apr 2012 13:11:51 +0000 (+0200) Subject: MEDIUM: http: merge acl and pattern header fetch functions X-Git-Tag: v1.5-dev9~28 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=185b5c4a7b88d12b4a127dc820d58329433a4472;p=thirdparty%2Fhaproxy.git MEDIUM: http: merge acl and pattern header fetch functions HTTP header fetch is now done using smp_fetch_hdr() for both ACLs and patterns. This one also supports an occurrence number, making it possible to specify explicit occurrences for ACLs and patterns. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 20bb05055c..979af6336b 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -7927,12 +7927,16 @@ cook_sub() scook_sub() variant for response cookies sent by the server. hdr -hdr(
) +hdr(
[,]) Note: all the "hdr*" matching criteria either apply to all headers, or to a particular header whose name is passed between parenthesis and without any space. The header name is not case-sensitive. The header matching complies with RFC2616, and treats as separate headers all values delimited by commas. - Use the shdr() variant for response headers sent by the server. + If an occurrence number is specified as the optional second argument, only + this occurrence will be considered. Positive values indicate a position from + the first occurrence, 1 being the first one. Negative values indicate + positions relative to the last one, -1 being the last one. Use the shdr() + variant for response headers sent by the server. The "hdr" criteria returns true if any of the headers matching the criteria match any of the strings. This can be used to check for exact values. For @@ -7941,7 +7945,7 @@ hdr(
) hdr(Connection) -i close hdr_beg -hdr_beg(
) +hdr_beg(
[,]) Returns true when one of the headers begins with one of the strings. See "hdr" for more information on header matching. Use the shdr_beg() variant for response headers sent by the server. @@ -7957,7 +7961,7 @@ hdr_cnt(
) the shdr_cnt() variant for response headers sent by the server. hdr_dir -hdr_dir(
) +hdr_dir(
[,]) Returns true when one of the headers contains one of the strings either isolated or delimited by slashes. This is used to perform filename or directory name matching, and may be used with Referer. See "hdr" for more @@ -7965,7 +7969,7 @@ hdr_dir(
) headers sent by the server. hdr_dom -hdr_dom(
) +hdr_dom(
[,]) Returns true when one of the headers contains one of the strings either isolated or delimited by dots. This is used to perform domain name matching, and may be used with the Host header. See "hdr" for more information on @@ -7973,27 +7977,27 @@ hdr_dom(
) server. hdr_end -hdr_end(
) +hdr_end(
[,]) Returns true when one of the headers ends with one of the strings. See "hdr" for more information on header matching. Use the shdr_end() variant for response headers sent by the server. hdr_ip -hdr_ip(
) +hdr_ip(
[,]) Returns true when one of the headers' values contains an IP address matching . This is mainly used with headers such as X-Forwarded-For or X-Client-IP. See "hdr" for more information on header matching. Use the shdr_ip() variant for response headers sent by the server. hdr_len -hdr_len(
) +hdr_len(
[,]) Returns true when at least one of the headers has a length which matches the values or ranges specified. This may be used to detect empty or too large headers. See "hdr" for more information on header matching. Use the shdr_len() variant for response headers sent by the server. hdr_reg -hdr_reg(
) +hdr_reg(
[,]) Returns true it one of the headers matches one of the regular expressions. It can be used at any time, but it is important to remember that regex matching is slower than other methods. See also other "hdr_" criteria, as well as @@ -8001,13 +8005,13 @@ hdr_reg(
) response headers sent by the server. hdr_sub -hdr_sub(
) +hdr_sub(
[,]) Returns true when one of the headers contains one of the strings. See "hdr" for more information on header matching. Use the shdr_sub() variant for response headers sent by the server. hdr_val -hdr_val(
) +hdr_val(
[,]) Returns true when one of the headers starts with a number which matches the values or ranges specified. This may be used to limit content-length to acceptable values for example. See "hdr" for more information on header @@ -8331,8 +8335,13 @@ The list of currently supported pattern fetch functions is the following : ports to some clients for a whole application session. It is of type integer and only works with such tables. - hdr() This extracts the last occurrence of header in an HTTP - request. A typical use is with the X-Forwarded-For header once + hdr([,]) + This extracts the last occurrence of header in an HTTP + request. Optionally, a specific occurrence might be specified as + a position number. Positive values indicate a position from the + first occurrence, with 1 being the first one. Negative values + indicate positions relative to the last one, with -1 being the + last one. A typical use is with the X-Forwarded-For header once converted to IP, associated with an IP stick-table. payload(,) diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h index 16f03c2eb3..06fe292574 100644 --- a/include/proto/proto_http.h +++ b/include/proto/proto_http.h @@ -98,7 +98,7 @@ void http_return_srv_error(struct session *s, struct stream_interface *si); void http_capture_bad_message(struct error_snapshot *es, struct session *s, struct http_msg *msg, int state, struct proxy *other_end); -unsigned int http_get_hdr(struct http_msg *msg, const char *hname, int hlen, +unsigned int http_get_hdr(const struct http_msg *msg, const char *hname, int hlen, struct hdr_idx *idx, int occ, struct hdr_ctx *ctx, char **vptr, int *vlen); diff --git a/src/proto_http.c b/src/proto_http.c index 08750d7f75..a1b7e136af 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -7261,7 +7261,7 @@ void http_capture_bad_message(struct error_snapshot *es, struct session *s, * -1. * The return value is 0 if nothing was found, or non-zero otherwise. */ -unsigned int http_get_hdr(struct http_msg *msg, const char *hname, int hlen, +unsigned int http_get_hdr(const struct http_msg *msg, const char *hname, int hlen, struct hdr_idx *idx, int occ, struct hdr_ctx *ctx, char **vptr, int *vlen) { @@ -7843,39 +7843,54 @@ acl_fetch_url_port(struct proxy *px, struct session *l4, void *l7, unsigned int return 1; } -/* 5. Check on HTTP header. A pointer to the beginning of the value is returned. - * Accepts exactly 1 argument of type string. +/* Fetch an HTTP header. A pointer to the beginning of the value is returned. + * Accepts an optional argument of type string containing the header field name, + * and an optional argument of type signed or unsigned integer to request an + * explicit occurrence of the header. Note that in the event of a missing name, + * headers are considered from the first one. */ static int -acl_fetch_hdr(struct proxy *px, struct session *l4, void *l7, unsigned int opt, +smp_fetch_hdr(struct proxy *px, struct session *l4, void *l7, unsigned int opt, const struct arg *args, struct sample *smp) { struct http_txn *txn = l7; struct hdr_idx *idx = &txn->hdr_idx; struct hdr_ctx *ctx = (struct hdr_ctx *)smp->ctx.a; const struct http_msg *msg = ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &txn->req : &txn->rsp; + int occ = 0; + const char *name_str = NULL; + int name_len = 0; - if (!args || args->type != ARGT_STR) - return 0; + if (args) { + if (args[0].type != ARGT_STR) + return 0; + name_str = args[0].data.str.str; + name_len = args[0].data.str.len; + + if (args[1].type == ARGT_UINT || args[1].type == ARGT_SINT) + occ = args[1].data.uint; + } CHECK_HTTP_MESSAGE_FIRST(); - if (!(smp->flags & SMP_F_NOT_LAST)) + if (ctx && !(smp->flags & SMP_F_NOT_LAST)) /* search for header from the beginning */ ctx->idx = 0; - if (http_find_header2(args->data.str.str, args->data.str.len, msg->buf->p + msg->sol, idx, ctx)) { + if (!occ && !(opt & SMP_OPT_ITERATE)) + /* no explicit occurrence and single fetch => last header by default */ + occ = -1; + + if (!occ) + /* prepare to report multiple occurrences for ACL fetches */ smp->flags |= SMP_F_NOT_LAST; - smp->flags |= SMP_F_VOL_HDR; - smp->type = SMP_T_CSTR; - smp->data.str.str = (char *)ctx->line + ctx->val; - smp->data.str.len = ctx->vlen; + smp->type = SMP_T_CSTR; + smp->flags |= SMP_F_VOL_HDR; + if (http_get_hdr(msg, name_str, name_len, idx, occ, ctx, &smp->data.str.str, &smp->data.str.len)) return 1; - } smp->flags &= ~SMP_F_NOT_LAST; - smp->flags |= SMP_F_VOL_HDR; return 0; } @@ -7883,7 +7898,7 @@ acl_fetch_hdr(struct proxy *px, struct session *l4, void *l7, unsigned int opt, * Accepts exactly 1 argument of type string. */ static int -acl_fetch_hdr_cnt(struct proxy *px, struct session *l4, void *l7, unsigned int opt, +smp_fetch_hdr_cnt(struct proxy *px, struct session *l4, void *l7, unsigned int opt, const struct arg *args, struct sample *smp) { struct http_txn *txn = l7; @@ -7908,14 +7923,16 @@ acl_fetch_hdr_cnt(struct proxy *px, struct session *l4, void *l7, unsigned int o return 1; } -/* 7. Check on HTTP header's integer value. The integer value is returned. - * FIXME: the type is 'int', it may not be appropriate for everything. +/* Fetch an HTTP header's integer value. The integer value is returned. It + * takes a mandatory argument of type string and an optional one of type int + * to designate a specific occurrence. It returns an unsigned integer, which + * may or may not be appropriate for everything. */ static int -acl_fetch_hdr_val(struct proxy *px, struct session *l4, void *l7, unsigned int opt, +smp_fetch_hdr_val(struct proxy *px, struct session *l4, void *l7, unsigned int opt, const struct arg *args, struct sample *smp) { - int ret = acl_fetch_hdr(px, l4, l7, opt, args, smp); + int ret = smp_fetch_hdr(px, l4, l7, opt, args, smp); if (ret > 0) { smp->type = SMP_T_UINT; @@ -7925,19 +7942,23 @@ acl_fetch_hdr_val(struct proxy *px, struct session *l4, void *l7, unsigned int o return ret; } -/* 7. Check on HTTP header's IPv4 address value. The IPv4 address is returned. +/* Fetch an HTTP header's integer value. The integer value is returned. It + * takes a mandatory argument of type string and an optional one of type int + * to designate a specific occurrence. It returns an IPv4 address. */ static int -acl_fetch_hdr_ip(struct proxy *px, struct session *l4, void *l7, unsigned int opt, +smp_fetch_hdr_ip(struct proxy *px, struct session *l4, void *l7, unsigned int opt, const struct arg *args, struct sample *smp) { int ret; - while ((ret = acl_fetch_hdr(px, l4, l7, opt, args, smp)) > 0) { + while ((ret = smp_fetch_hdr(px, l4, l7, opt, args, smp)) > 0) { smp->type = SMP_T_IPV4; if (url2ipv4((char *)smp->data.str.str, &smp->data.ipv4)) break; /* if the header doesn't match an IP address, fetch next one */ + if (!(smp->flags & SMP_F_NOT_LAST)) + return 0; } return ret; } @@ -8260,18 +8281,6 @@ acl_fetch_cookie_cnt(struct proxy *px, struct session *l4, void *l7, unsigned in /* The code below is dedicated to pattern fetching and matching */ /************************************************************************/ -/* Returns the last occurrence of specified header. */ -static int -pattern_fetch_hdr(struct proxy *px, struct session *l4, void *l7, unsigned int opt, - const struct arg *arg_p, struct sample *smp) -{ - struct http_txn *txn = l7; - - smp->type = SMP_T_CSTR; - return http_get_hdr(&txn->req, arg_p->data.str.str, arg_p->data.str.len, &txn->hdr_idx, - -1, NULL, &smp->data.str.str, &smp->data.str.len); -} - /* * Given a path string and its length, find the position of beginning of the * query string. Returns NULL if no query string is found in the path. @@ -8458,6 +8467,24 @@ pattern_fetch_set_cookie(struct proxy *px, struct session *l4, void *l7, unsigne return found; } +/* This function is used to validate the arguments passed to any "hdr" fetch + * keyword. These keywords support an optional positive or negative occurrence + * number. We must ensure that the number is greater than -MAX_HDR_HISTORY. It + * is assumed that the types are already the correct ones. Returns 0 on error, + * non-zero if OK. If is not NULL, it will be filled with a pointer to an + * error message in case of error, that the caller is responsible for freeing. + * The initial location must either be freeable or NULL. + */ +static int val_hdr(struct arg *arg, char **err_msg) +{ + if (arg && arg[1].type == ARGT_SINT && arg[1].data.sint < -MAX_HDR_HISTORY) { + if (err_msg) + memprintf(err_msg, "header occurrence must be >= %d", -MAX_HDR_HISTORY); + return 0; + } + return 1; +} + /************************************************************************/ /* All supported ACL keywords must be declared here. */ /************************************************************************/ @@ -8476,17 +8503,17 @@ static struct acl_kw_list acl_kws = {{ },{ { "cook_reg", acl_parse_reg, acl_fetch_cookie_value, acl_match_reg, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) }, { "cook_sub", acl_parse_str, acl_fetch_cookie_value, acl_match_sub, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) }, - { "hdr", acl_parse_str, acl_fetch_hdr, acl_match_str, ACL_USE_L7REQ_VOLATILE|ACL_MAY_LOOKUP, ARG1(0,STR) }, - { "hdr_beg", acl_parse_str, acl_fetch_hdr, acl_match_beg, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) }, - { "hdr_cnt", acl_parse_int, acl_fetch_hdr_cnt, acl_match_int, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) }, - { "hdr_dir", acl_parse_str, acl_fetch_hdr, acl_match_dir, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) }, - { "hdr_dom", acl_parse_str, acl_fetch_hdr, acl_match_dom, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) }, - { "hdr_end", acl_parse_str, acl_fetch_hdr, acl_match_end, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) }, - { "hdr_ip", acl_parse_ip, acl_fetch_hdr_ip, acl_match_ip, ACL_USE_L7REQ_VOLATILE|ACL_MAY_LOOKUP, ARG1(0,STR) }, - { "hdr_len", acl_parse_int, acl_fetch_hdr, acl_match_len, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) }, - { "hdr_reg", acl_parse_reg, acl_fetch_hdr, acl_match_reg, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) }, - { "hdr_sub", acl_parse_str, acl_fetch_hdr, acl_match_sub, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) }, - { "hdr_val", acl_parse_int, acl_fetch_hdr_val, acl_match_int, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) }, + { "hdr", acl_parse_str, smp_fetch_hdr, acl_match_str, ACL_USE_L7REQ_VOLATILE|ACL_MAY_LOOKUP, ARG2(0,STR,SINT), val_hdr }, + { "hdr_beg", acl_parse_str, smp_fetch_hdr, acl_match_beg, ACL_USE_L7REQ_VOLATILE, ARG2(0,STR,SINT), val_hdr }, + { "hdr_cnt", acl_parse_int, smp_fetch_hdr_cnt, acl_match_int, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) }, + { "hdr_dir", acl_parse_str, smp_fetch_hdr, acl_match_dir, ACL_USE_L7REQ_VOLATILE, ARG2(0,STR,SINT), val_hdr }, + { "hdr_dom", acl_parse_str, smp_fetch_hdr, acl_match_dom, ACL_USE_L7REQ_VOLATILE, ARG2(0,STR,SINT), val_hdr }, + { "hdr_end", acl_parse_str, smp_fetch_hdr, acl_match_end, ACL_USE_L7REQ_VOLATILE, ARG2(0,STR,SINT), val_hdr }, + { "hdr_ip", acl_parse_ip, smp_fetch_hdr_ip, acl_match_ip, ACL_USE_L7REQ_VOLATILE|ACL_MAY_LOOKUP, ARG2(0,STR,SINT), val_hdr }, + { "hdr_len", acl_parse_int, smp_fetch_hdr, acl_match_len, ACL_USE_L7REQ_VOLATILE, ARG2(0,STR,SINT), val_hdr }, + { "hdr_reg", acl_parse_reg, smp_fetch_hdr, acl_match_reg, ACL_USE_L7REQ_VOLATILE, ARG2(0,STR,SINT), val_hdr }, + { "hdr_sub", acl_parse_str, smp_fetch_hdr, acl_match_sub, ACL_USE_L7REQ_VOLATILE, ARG2(0,STR,SINT), val_hdr }, + { "hdr_val", acl_parse_int, smp_fetch_hdr_val, acl_match_int, ACL_USE_L7REQ_VOLATILE, ARG2(0,STR,SINT), val_hdr }, { "http_auth", acl_parse_nothing, acl_fetch_http_auth, acl_match_nothing, ACL_USE_L7REQ_VOLATILE, ARG1(0,USR) }, { "http_auth_group", acl_parse_strcat, acl_fetch_http_auth, acl_match_auth, ACL_USE_L7REQ_VOLATILE, ARG1(0,USR) }, @@ -8517,17 +8544,17 @@ static struct acl_kw_list acl_kws = {{ },{ { "scook_reg", acl_parse_reg, acl_fetch_cookie_value, acl_match_reg, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) }, { "scook_sub", acl_parse_str, acl_fetch_cookie_value, acl_match_sub, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) }, - { "shdr", acl_parse_str, acl_fetch_hdr, acl_match_str, ACL_USE_L7RTR_VOLATILE|ACL_MAY_LOOKUP, ARG1(0,STR) }, - { "shdr_beg", acl_parse_str, acl_fetch_hdr, acl_match_beg, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) }, - { "shdr_cnt", acl_parse_int, acl_fetch_hdr_cnt, acl_match_int, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) }, - { "shdr_dir", acl_parse_str, acl_fetch_hdr, acl_match_dir, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) }, - { "shdr_dom", acl_parse_str, acl_fetch_hdr, acl_match_dom, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) }, - { "shdr_end", acl_parse_str, acl_fetch_hdr, acl_match_end, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) }, - { "shdr_ip", acl_parse_ip, acl_fetch_hdr_ip, acl_match_ip, ACL_USE_L7RTR_VOLATILE|ACL_MAY_LOOKUP, ARG1(0,STR) }, - { "shdr_len", acl_parse_int, acl_fetch_hdr, acl_match_len, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) }, - { "shdr_reg", acl_parse_reg, acl_fetch_hdr, acl_match_reg, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) }, - { "shdr_sub", acl_parse_str, acl_fetch_hdr, acl_match_sub, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) }, - { "shdr_val", acl_parse_int, acl_fetch_hdr_val, acl_match_int, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) }, + { "shdr", acl_parse_str, smp_fetch_hdr, acl_match_str, ACL_USE_L7RTR_VOLATILE|ACL_MAY_LOOKUP, ARG2(0,STR,SINT), val_hdr }, + { "shdr_beg", acl_parse_str, smp_fetch_hdr, acl_match_beg, ACL_USE_L7RTR_VOLATILE, ARG2(0,STR,SINT), val_hdr }, + { "shdr_cnt", acl_parse_int, smp_fetch_hdr_cnt, acl_match_int, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) }, + { "shdr_dir", acl_parse_str, smp_fetch_hdr, acl_match_dir, ACL_USE_L7RTR_VOLATILE, ARG2(0,STR,SINT), val_hdr }, + { "shdr_dom", acl_parse_str, smp_fetch_hdr, acl_match_dom, ACL_USE_L7RTR_VOLATILE, ARG2(0,STR,SINT), val_hdr }, + { "shdr_end", acl_parse_str, smp_fetch_hdr, acl_match_end, ACL_USE_L7RTR_VOLATILE, ARG2(0,STR,SINT), val_hdr }, + { "shdr_ip", acl_parse_ip, smp_fetch_hdr_ip, acl_match_ip, ACL_USE_L7RTR_VOLATILE|ACL_MAY_LOOKUP, ARG2(0,STR,SINT), val_hdr }, + { "shdr_len", acl_parse_int, smp_fetch_hdr, acl_match_len, ACL_USE_L7RTR_VOLATILE, ARG2(0,STR,SINT), val_hdr }, + { "shdr_reg", acl_parse_reg, smp_fetch_hdr, acl_match_reg, ACL_USE_L7RTR_VOLATILE, ARG2(0,STR,SINT), val_hdr }, + { "shdr_sub", acl_parse_str, smp_fetch_hdr, acl_match_sub, ACL_USE_L7RTR_VOLATILE, ARG2(0,STR,SINT), val_hdr }, + { "shdr_val", acl_parse_int, smp_fetch_hdr_val, acl_match_int, ACL_USE_L7RTR_VOLATILE, ARG2(0,STR,SINT), val_hdr }, { "status", acl_parse_int, acl_fetch_stcode, acl_match_int, ACL_USE_L7RTR_PERMANENT, 0 }, @@ -8560,7 +8587,7 @@ static struct acl_kw_list acl_kws = {{ },{ /************************************************************************/ /* Note: must not be declared as its list will be overwritten */ static struct pattern_fetch_kw_list pattern_fetch_keywords = {{ },{ - { "hdr", pattern_fetch_hdr, ARG1(1,STR), NULL, SMP_T_CSTR, SMP_CAP_REQ }, + { "hdr", smp_fetch_hdr, ARG2(1,STR,SINT), val_hdr, SMP_T_CSTR, SMP_CAP_REQ }, { "url_param", smp_fetch_url_param, ARG1(1,STR), NULL, SMP_T_CSTR, SMP_CAP_REQ }, { "cookie", pattern_fetch_cookie, ARG1(1,STR), NULL, SMP_T_CSTR, SMP_CAP_REQ }, { "set-cookie", pattern_fetch_set_cookie, ARG1(1,STR), NULL, SMP_T_CSTR, SMP_CAP_RES },