diff --git a/doc/configuration.txt b/doc/configuration.txt index 89bdf986a..43d476091 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -7171,7 +7171,7 @@ tcp-request connection [{if | unless} ] accept the incoming connection. There is no specific limit to the number of rules which may be inserted. - Three types of actions are supported : + Five types of actions are supported : - accept : accepts the connection if the condition is true (when used with "if") or false (when used with "unless"). The first such rule executed ends @@ -7200,6 +7200,18 @@ tcp-request connection [{if | unless} ] of load balancers are passed through by traffic coming from public hosts. + - capture len : + This only applies to "tcp-request content" rules. It captures sample + expression from the request buffer, and converts it to a + string of at most characters. The resulting string is stored into + the next request "capture" slot, so it will possibly appear next to + some captured HTTP headers. It will then automatically appear in the + logs, and it will be possible to extract it using sample fetch rules to + feed it into headers or anything. The length should be limited given + that this size will be allocated for each capture during the whole + session life. Since it applies to Please check section 7.3 (Fetching + samples) and "capture request header" for more information. + - { track-sc0 | track-sc1 | track-sc2 } [table ] : enables tracking of sticky counters from current connection. These rules do not stop evaluation and do not change default action. Two sets @@ -7278,8 +7290,8 @@ tcp-request content [{if | unless} ] Arguments : defines the action to perform if the condition applies. Valid actions include : "accept", "reject", "track-sc0", "track-sc1", - and "track-sc2". See "tcp-request connection" above for their - signification. + "track-sc2" and "capture". See "tcp-request connection" above + for their signification. is a standard layer 4-7 ACL-based condition (see section 7). @@ -7307,9 +7319,10 @@ tcp-request content [{if | unless} ] contents. There is no specific limit to the number of rules which may be inserted. - Three types of actions are supported : - - accept : - - reject : + Four types of actions are supported : + - accept : the request is accepted + - reject : the request is rejected and the connection is closed + - capture : the specified sample expression is captured - { track-sc0 | track-sc1 | track-sc2 } [table
] They have the same meaning as their counter-parts in "tcp-request connection" diff --git a/src/proto_tcp.c b/src/proto_tcp.c index a672de415..65c4fdad3 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -990,13 +991,8 @@ int tcp_inspect_request(struct session *s, struct channel *req, int an_bit) if (rule->cond) { ret = acl_exec_cond(rule->cond, s->be, s, &s->txn, SMP_OPT_DIR_REQ | partial); - if (ret == ACL_TEST_MISS) { - channel_dont_connect(req); - /* just set the request timeout once at the beginning of the request */ - if (!tick_isset(req->analyse_exp) && s->be->tcp_req.inspect_delay) - req->analyse_exp = tick_add(now_ms, s->be->tcp_req.inspect_delay); - return 0; - } + if (ret == ACL_TEST_MISS) + goto missing_data; ret = acl_pass(ret); if (rule->cond->pol == ACL_COND_UNLESS) @@ -1040,6 +1036,32 @@ int tcp_inspect_request(struct session *s, struct channel *req, int an_bit) stkctr_set_flags(&s->stkctr[tcp_trk_idx(rule->action)], STKCTR_TRACK_BACKEND); } } + else if (rule->action == TCP_ACT_CAPTURE) { + struct sample *key; + struct cap_hdr *h = rule->act_prm.cap.hdr; + char **cap = s->txn.req.cap; + int len; + + key = sample_fetch_string(s->be, s, &s->txn, SMP_OPT_DIR_REQ | partial, rule->act_prm.cap.expr); + if (!key) + continue; + + if (key->flags & SMP_F_MAY_CHANGE) + goto missing_data; + + if (cap[h->index] == NULL) + cap[h->index] = pool_alloc2(h->pool); + + if (cap[h->index] == NULL) /* no more capture memory */ + continue; + + len = key->data.str.len; + if (len > h->len) + len = h->len; + + memcpy(cap[h->index], key->data.str.str, len); + cap[h->index][len] = 0; + } else { /* otherwise accept */ break; @@ -1053,6 +1075,14 @@ int tcp_inspect_request(struct session *s, struct channel *req, int an_bit) req->analysers &= ~an_bit; req->analyse_exp = TICK_ETERNITY; return 1; + + missing_data: + channel_dont_connect(req); + /* just set the request timeout once at the beginning of the request */ + if (!tick_isset(req->analyse_exp) && s->be->tcp_req.inspect_delay) + req->analyse_exp = tick_add(now_ms, s->be->tcp_req.inspect_delay); + return 0; + } /* This function performs the TCP response analysis on the current response. It @@ -1287,6 +1317,92 @@ static int tcp_parse_request_rule(char **args, int arg, int section_type, arg++; rule->action = TCP_ACT_REJECT; } + else if (strcmp(args[arg], "capture") == 0) { + struct sample_expr *expr; + struct cap_hdr *hdr; + int kw = arg; + int len = 0; + + if (!(curpx->cap & PR_CAP_FE)) { + memprintf(err, + "'%s %s %s' : proxy '%s' has no frontend capability", + args[0], args[1], args[kw], curpx->id); + return -1; + } + + if (!(where & SMP_VAL_FE_REQ_CNT)) { + memprintf(err, + "'%s %s' is not allowed in '%s %s' rules in %s '%s'", + args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id); + return -1; + } + + arg++; + + curpx->conf.args.ctx = ARGC_CAP; + expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args); + if (!expr) { + memprintf(err, + "'%s %s %s' : %s", + args[0], args[1], args[kw], *err); + return -1; + } + + if (!(expr->fetch->val & where)) { + memprintf(err, + "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here", + args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use)); + free(expr); + return -1; + } + + if (strcmp(args[arg], "len") == 0) { + arg++; + if (!args[arg]) { + memprintf(err, + "'%s %s %s' : missing length value", + args[0], args[1], args[kw]); + free(expr); + return -1; + } + /* we copy the table name for now, it will be resolved later */ + len = atoi(args[arg]); + if (len <= 0) { + memprintf(err, + "'%s %s %s' : length must be > 0", + args[0], args[1], args[kw]); + free(expr); + return -1; + } + arg++; + } + + if (!len) { + memprintf(err, + "'%s %s %s' : a positive 'len' argument is mandatory", + args[0], args[1], args[kw]); + free(expr); + return -1; + } + + hdr = calloc(sizeof(struct cap_hdr), 1); + hdr->next = curpx->req_cap; + hdr->name = NULL; /* not a header capture */ + hdr->namelen = 0; + hdr->len = len; + hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED); + hdr->index = curpx->nb_req_cap++; + + curpx->req_cap = hdr; + curpx->to_log |= LW_REQHDR; + + /* check if we need to allocate an hdr_idx struct for HTTP parsing */ + curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY); + + rule->act_prm.cap.expr = expr; + rule->act_prm.cap.hdr = hdr; + rule->action = TCP_ACT_CAPTURE; + } else if (strncmp(args[arg], "track-sc", 8) == 0 && args[arg][9] == '\0' && args[arg][8] >= '0' && args[arg][8] <= '0' + MAX_SESS_STKCTR) { /* track-sc 0..9 */