MINOR: proto_htx: Add functions htx_req_get_intercept_rule and htx_res_get_intercept_rule

It is more or less the same than legacy versions but adapted to be called from
HTX analyzers.
This commit is contained in:
Christopher Faulet 2018-10-24 11:39:23 +02:00 committed by Willy Tarreau
parent 6eb92897f7
commit 3e9641951f

View File

@ -19,6 +19,7 @@
#include <types/capture.h>
#include <proto/acl.h>
#include <proto/action.h>
#include <proto/channel.h>
#include <proto/checks.h>
#include <proto/connection.h>
@ -27,6 +28,7 @@
#include <proto/http_htx.h>
#include <proto/htx.h>
#include <proto/log.h>
#include <proto/pattern.h>
#include <proto/proto_http.h>
#include <proto/proxy.h>
#include <proto/stream.h>
@ -44,6 +46,9 @@ static size_t htx_fmt_res_line(const union h1_sl sl, char *str, size_t len);
static void htx_debug_stline(const char *dir, struct stream *s, const union h1_sl sl);
static void htx_debug_hdr(const char *dir, struct stream *s, const struct ist n, const struct ist v);
static enum rule_result htx_req_get_intercept_rule(struct proxy *px, struct list *rules, struct stream *s, int *deny_status);
static enum rule_result htx_res_get_intercept_rule(struct proxy *px, struct list *rules, struct stream *s);
/* This stream analyser waits for a complete HTTP request. It returns 1 if the
* processing can continue on next analysers, or zero if it either needs more
* data or wants to immediately abort the request (eg: timeout, error, ...). It
@ -2708,6 +2713,747 @@ void htx_res_set_status(unsigned int status, const char *reason, struct stream *
http_replace_res_reason(htx, ist2(reason, strlen(reason)));
}
/* Executes the http-request rules <rules> for stream <s>, proxy <px> and
* transaction <txn>. Returns the verdict of the first rule that prevents
* further processing of the request (auth, deny, ...), and defaults to
* HTTP_RULE_RES_STOP if it executed all rules or stopped on an allow, or
* HTTP_RULE_RES_CONT if the last rule was reached. It may set the TX_CLTARPIT
* on txn->flags if it encounters a tarpit rule. If <deny_status> is not NULL
* and a deny/tarpit rule is matched, it will be filled with this rule's deny
* status.
*/
static enum rule_result htx_req_get_intercept_rule(struct proxy *px, struct list *rules,
struct stream *s, int *deny_status)
{
struct session *sess = strm_sess(s);
struct http_txn *txn = s->txn;
struct htx *htx;
struct connection *cli_conn;
struct act_rule *rule;
struct http_hdr_ctx ctx;
const char *auth_realm;
struct buffer *early_hints = NULL;
enum rule_result rule_ret = HTTP_RULE_RES_CONT;
int act_flags = 0;
htx = htx_from_buf(&s->req.buf);
/* If "the current_rule_list" match the executed rule list, we are in
* resume condition. If a resume is needed it is always in the action
* and never in the ACL or converters. In this case, we initialise the
* current rule, and go to the action execution point.
*/
if (s->current_rule) {
rule = s->current_rule;
s->current_rule = NULL;
if (s->current_rule_list == rules)
goto resume_execution;
}
s->current_rule_list = rules;
list_for_each_entry(rule, rules, list) {
/* check optional condition */
if (rule->cond) {
int ret;
ret = acl_exec_cond(rule->cond, px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
ret = acl_pass(ret);
if (rule->cond->pol == ACL_COND_UNLESS)
ret = !ret;
if (!ret) /* condition not matched */
continue;
}
act_flags |= ACT_FLAG_FIRST;
resume_execution:
switch (rule->action) {
case ACT_ACTION_ALLOW:
rule_ret = HTTP_RULE_RES_STOP;
goto end;
case ACT_ACTION_DENY:
if (deny_status)
*deny_status = rule->deny_status;
rule_ret = HTTP_RULE_RES_DENY;
goto end;
case ACT_HTTP_REQ_TARPIT:
txn->flags |= TX_CLTARPIT;
if (deny_status)
*deny_status = rule->deny_status;
rule_ret = HTTP_RULE_RES_DENY;
goto end;
case ACT_HTTP_REQ_AUTH:
/* Be sure to sned any pending HTTP 103 response first */
if (early_hints) {
htx_send_early_hints(s, early_hints);
free_trash_chunk(early_hints);
early_hints = NULL;
}
/* Auth might be performed on regular http-req rules as well as on stats */
auth_realm = rule->arg.auth.realm;
if (!auth_realm) {
if (px->uri_auth && rules == &px->uri_auth->http_req_rules)
auth_realm = STATS_DEFAULT_REALM;
else
auth_realm = px->id;
}
/* send 401/407 depending on whether we use a proxy or not. We still
* count one error, because normal browsing won't significantly
* increase the counter but brute force attempts will.
*/
chunk_printf(&trash, (txn->flags & TX_USE_PX_CONN) ? HTTP_407_fmt : HTTP_401_fmt, auth_realm);
txn->status = (txn->flags & TX_USE_PX_CONN) ? 407 : 401;
htx_reply_and_close(s, txn->status, &trash);
stream_inc_http_err_ctr(s);
rule_ret = HTTP_RULE_RES_ABRT;
goto end;
case ACT_HTTP_REDIR:
/* Be sure to sned any pending HTTP 103 response first */
if (early_hints) {
htx_send_early_hints(s, early_hints);
free_trash_chunk(early_hints);
early_hints = NULL;
}
rule_ret = HTTP_RULE_RES_DONE;
if (!htx_apply_redirect_rule(rule->arg.redir, s, txn))
rule_ret = HTTP_RULE_RES_BADREQ;
goto end;
case ACT_HTTP_SET_NICE:
s->task->nice = rule->arg.nice;
break;
case ACT_HTTP_SET_TOS:
if ((cli_conn = objt_conn(sess->origin)) && conn_ctrl_ready(cli_conn))
inet_set_tos(cli_conn->handle.fd, &cli_conn->addr.from, rule->arg.tos);
break;
case ACT_HTTP_SET_MARK:
#ifdef SO_MARK
if ((cli_conn = objt_conn(sess->origin)) && conn_ctrl_ready(cli_conn))
setsockopt(cli_conn->handle.fd, SOL_SOCKET, SO_MARK, &rule->arg.mark, sizeof(rule->arg.mark));
#endif
break;
case ACT_HTTP_SET_LOGL:
s->logs.level = rule->arg.loglevel;
break;
case ACT_HTTP_REPLACE_HDR:
case ACT_HTTP_REPLACE_VAL:
if (htx_transform_header(s, &s->req, htx,
ist2(rule->arg.hdr_add.name, rule->arg.hdr_add.name_len),
&rule->arg.hdr_add.fmt,
&rule->arg.hdr_add.re, rule->action)) {
rule_ret = HTTP_RULE_RES_BADREQ;
goto end;
}
break;
case ACT_HTTP_DEL_HDR:
/* remove all occurrences of the header */
ctx.blk = NULL;
while (http_find_header(htx, ist2(rule->arg.hdr_add.name, rule->arg.hdr_add.name_len), &ctx, 1))
http_remove_header(htx, &ctx);
break;
case ACT_HTTP_SET_HDR:
case ACT_HTTP_ADD_HDR: {
/* The scope of the trash buffer must be limited to this function. The
* build_logline() function can execute a lot of other function which
* can use the trash buffer. So for limiting the scope of this global
* buffer, we build first the header value using build_logline, and
* after we store the header name.
*/
struct buffer *replace;
struct ist n, v;
replace = alloc_trash_chunk();
if (!replace) {
rule_ret = HTTP_RULE_RES_BADREQ;
goto end;
}
replace->data = build_logline(s, replace->area, replace->size, &rule->arg.hdr_add.fmt);
n = ist2(rule->arg.hdr_add.name, rule->arg.hdr_add.name_len);
v = ist2(replace->area, replace->data);
if (rule->action == ACT_HTTP_SET_HDR) {
/* remove all occurrences of the header */
ctx.blk = NULL;
while (http_find_header(htx, ist2(rule->arg.hdr_add.name, rule->arg.hdr_add.name_len), &ctx, 1))
http_remove_header(htx, &ctx);
}
if (!http_add_header(htx, n, v)) {
static unsigned char rate_limit = 0;
if ((rate_limit++ & 255) == 0) {
send_log(px, LOG_WARNING, "Proxy %s failed to add or set the request header '%.*s' for request #%u. You might need to increase tune.maxrewrite.", px->id, (int)n.len, n.ptr, s->uniq_id);
}
HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
if (sess->fe != s->be)
HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
if (sess->listener->counters)
HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
}
free_trash_chunk(replace);
break;
}
case ACT_HTTP_DEL_ACL:
case ACT_HTTP_DEL_MAP: {
struct pat_ref *ref;
struct buffer *key;
/* collect reference */
ref = pat_ref_lookup(rule->arg.map.ref);
if (!ref)
continue;
/* allocate key */
key = alloc_trash_chunk();
if (!key) {
rule_ret = HTTP_RULE_RES_BADREQ;
goto end;
}
/* collect key */
key->data = build_logline(s, key->area, key->size, &rule->arg.map.key);
key->area[key->data] = '\0';
/* perform update */
/* returned code: 1=ok, 0=ko */
HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
pat_ref_delete(ref, key->area);
HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
free_trash_chunk(key);
break;
}
case ACT_HTTP_ADD_ACL: {
struct pat_ref *ref;
struct buffer *key;
/* collect reference */
ref = pat_ref_lookup(rule->arg.map.ref);
if (!ref)
continue;
/* allocate key */
key = alloc_trash_chunk();
if (!key) {
rule_ret = HTTP_RULE_RES_BADREQ;
goto end;
}
/* collect key */
key->data = build_logline(s, key->area, key->size, &rule->arg.map.key);
key->area[key->data] = '\0';
/* perform update */
/* add entry only if it does not already exist */
HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
if (pat_ref_find_elt(ref, key->area) == NULL)
pat_ref_add(ref, key->area, NULL, NULL);
HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
free_trash_chunk(key);
break;
}
case ACT_HTTP_SET_MAP: {
struct pat_ref *ref;
struct buffer *key, *value;
/* collect reference */
ref = pat_ref_lookup(rule->arg.map.ref);
if (!ref)
continue;
/* allocate key */
key = alloc_trash_chunk();
if (!key) {
rule_ret = HTTP_RULE_RES_BADREQ;
goto end;
}
/* allocate value */
value = alloc_trash_chunk();
if (!value) {
free_trash_chunk(key);
rule_ret = HTTP_RULE_RES_BADREQ;
goto end;
}
/* collect key */
key->data = build_logline(s, key->area, key->size, &rule->arg.map.key);
key->area[key->data] = '\0';
/* collect value */
value->data = build_logline(s, value->area, value->size, &rule->arg.map.value);
value->area[value->data] = '\0';
/* perform update */
if (pat_ref_find_elt(ref, key->area) != NULL)
/* update entry if it exists */
pat_ref_set(ref, key->area, value->area, NULL);
else
/* insert a new entry */
pat_ref_add(ref, key->area, value->area, NULL);
free_trash_chunk(key);
free_trash_chunk(value);
break;
}
case ACT_HTTP_EARLY_HINT:
if (!(txn->req.flags & HTTP_MSGF_VER_11))
break;
early_hints = htx_apply_early_hint_rule(s, early_hints,
rule->arg.early_hint.name,
rule->arg.early_hint.name_len,
&rule->arg.early_hint.fmt);
if (!early_hints) {
rule_ret = HTTP_RULE_RES_DONE;
goto end;
}
break;
case ACT_CUSTOM:
if ((s->req.flags & CF_READ_ERROR) ||
((s->req.flags & (CF_SHUTR|CF_READ_NULL)) &&
!(s->si[0].flags & SI_FL_CLEAN_ABRT) &&
(px->options & PR_O_ABRT_CLOSE)))
act_flags |= ACT_FLAG_FINAL;
switch (rule->action_ptr(rule, px, s->sess, s, act_flags)) {
case ACT_RET_ERR:
case ACT_RET_CONT:
break;
case ACT_RET_STOP:
rule_ret = HTTP_RULE_RES_DONE;
goto end;
case ACT_RET_YIELD:
s->current_rule = rule;
rule_ret = HTTP_RULE_RES_YIELD;
goto end;
}
break;
case ACT_ACTION_TRK_SC0 ... ACT_ACTION_TRK_SCMAX:
/* Note: only the first valid tracking parameter of each
* applies.
*/
if (stkctr_entry(&s->stkctr[trk_idx(rule->action)]) == NULL) {
struct stktable *t;
struct stksess *ts;
struct stktable_key *key;
void *ptr1, *ptr2;
t = rule->arg.trk_ctr.table.t;
key = stktable_fetch_key(t, s->be, sess, s, SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
rule->arg.trk_ctr.expr, NULL);
if (key && (ts = stktable_get_entry(t, key))) {
stream_track_stkctr(&s->stkctr[trk_idx(rule->action)], t, ts);
/* let's count a new HTTP request as it's the first time we do it */
ptr1 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_CNT);
ptr2 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_RATE);
if (ptr1 || ptr2) {
HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
if (ptr1)
stktable_data_cast(ptr1, http_req_cnt)++;
if (ptr2)
update_freq_ctr_period(&stktable_data_cast(ptr2, http_req_rate),
t->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
/* If data was modified, we need to touch to re-schedule sync */
stktable_touch_local(t, ts, 0);
}
stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_CONTENT);
if (sess->fe != s->be)
stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_BACKEND);
}
}
break;
/* other flags exists, but normaly, they never be matched. */
default:
break;
}
}
end:
if (early_hints) {
htx_send_early_hints(s, early_hints);
free_trash_chunk(early_hints);
}
/* we reached the end of the rules, nothing to report */
return rule_ret;
}
/* Executes the http-response rules <rules> for stream <s> and proxy <px>. It
* returns one of 5 possible statuses: HTTP_RULE_RES_CONT, HTTP_RULE_RES_STOP,
* HTTP_RULE_RES_DONE, HTTP_RULE_RES_YIELD, or HTTP_RULE_RES_BADREQ. If *CONT
* is returned, the process can continue the evaluation of next rule list. If
* *STOP or *DONE is returned, the process must stop the evaluation. If *BADREQ
* is returned, it means the operation could not be processed and a server error
* must be returned. It may set the TX_SVDENY on txn->flags if it encounters a
* deny rule. If *YIELD is returned, the caller must call again the function
* with the same context.
*/
static enum rule_result htx_res_get_intercept_rule(struct proxy *px, struct list *rules,
struct stream *s)
{
struct session *sess = strm_sess(s);
struct http_txn *txn = s->txn;
struct htx *htx;
struct connection *cli_conn;
struct act_rule *rule;
struct http_hdr_ctx ctx;
enum rule_result rule_ret = HTTP_RULE_RES_CONT;
int act_flags = 0;
htx = htx_from_buf(&s->res.buf);
/* If "the current_rule_list" match the executed rule list, we are in
* resume condition. If a resume is needed it is always in the action
* and never in the ACL or converters. In this case, we initialise the
* current rule, and go to the action execution point.
*/
if (s->current_rule) {
rule = s->current_rule;
s->current_rule = NULL;
if (s->current_rule_list == rules)
goto resume_execution;
}
s->current_rule_list = rules;
list_for_each_entry(rule, rules, list) {
/* check optional condition */
if (rule->cond) {
int ret;
ret = acl_exec_cond(rule->cond, px, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL);
ret = acl_pass(ret);
if (rule->cond->pol == ACL_COND_UNLESS)
ret = !ret;
if (!ret) /* condition not matched */
continue;
}
act_flags |= ACT_FLAG_FIRST;
resume_execution:
switch (rule->action) {
case ACT_ACTION_ALLOW:
rule_ret = HTTP_RULE_RES_STOP; /* "allow" rules are OK */
goto end;
case ACT_ACTION_DENY:
txn->flags |= TX_SVDENY;
rule_ret = HTTP_RULE_RES_STOP;
goto end;
case ACT_HTTP_SET_NICE:
s->task->nice = rule->arg.nice;
break;
case ACT_HTTP_SET_TOS:
if ((cli_conn = objt_conn(sess->origin)) && conn_ctrl_ready(cli_conn))
inet_set_tos(cli_conn->handle.fd, &cli_conn->addr.from, rule->arg.tos);
break;
case ACT_HTTP_SET_MARK:
#ifdef SO_MARK
if ((cli_conn = objt_conn(sess->origin)) && conn_ctrl_ready(cli_conn))
setsockopt(cli_conn->handle.fd, SOL_SOCKET, SO_MARK, &rule->arg.mark, sizeof(rule->arg.mark));
#endif
break;
case ACT_HTTP_SET_LOGL:
s->logs.level = rule->arg.loglevel;
break;
case ACT_HTTP_REPLACE_HDR:
case ACT_HTTP_REPLACE_VAL:
if (htx_transform_header(s, &s->res, htx,
ist2(rule->arg.hdr_add.name, rule->arg.hdr_add.name_len),
&rule->arg.hdr_add.fmt,
&rule->arg.hdr_add.re, rule->action)) {
rule_ret = HTTP_RULE_RES_BADREQ;
goto end;
}
break;
case ACT_HTTP_DEL_HDR:
/* remove all occurrences of the header */
ctx.blk = NULL;
while (http_find_header(htx, ist2(rule->arg.hdr_add.name, rule->arg.hdr_add.name_len), &ctx, 1))
http_remove_header(htx, &ctx);
break;
case ACT_HTTP_SET_HDR:
case ACT_HTTP_ADD_HDR: {
struct buffer *replace;
struct ist n, v;
replace = alloc_trash_chunk();
if (!replace) {
rule_ret = HTTP_RULE_RES_BADREQ;
goto end;
}
replace->data = build_logline(s, replace->area, replace->size, &rule->arg.hdr_add.fmt);
n = ist2(rule->arg.hdr_add.name, rule->arg.hdr_add.name_len);
v = ist2(replace->area, replace->data);
if (rule->action == ACT_HTTP_SET_HDR) {
/* remove all occurrences of the header */
ctx.blk = NULL;
while (http_find_header(htx, ist2(rule->arg.hdr_add.name, rule->arg.hdr_add.name_len), &ctx, 1))
http_remove_header(htx, &ctx);
}
if (!http_add_header(htx, n, v)) {
static unsigned char rate_limit = 0;
if ((rate_limit++ & 255) == 0) {
send_log(px, LOG_WARNING, "Proxy %s failed to add or set the response header '%.*s' for request #%u. You might need to increase tune.maxrewrite.", px->id, (int)n.len, n.ptr, s->uniq_id);
}
HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
if (sess->fe != s->be)
HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
if (sess->listener->counters)
HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
if (objt_server(s->target))
HA_ATOMIC_ADD(&objt_server(s->target)->counters.failed_rewrites, 1);
}
free_trash_chunk(replace);
break;
}
case ACT_HTTP_DEL_ACL:
case ACT_HTTP_DEL_MAP: {
struct pat_ref *ref;
struct buffer *key;
/* collect reference */
ref = pat_ref_lookup(rule->arg.map.ref);
if (!ref)
continue;
/* allocate key */
key = alloc_trash_chunk();
if (!key) {
rule_ret = HTTP_RULE_RES_BADREQ;
goto end;
}
/* collect key */
key->data = build_logline(s, key->area, key->size, &rule->arg.map.key);
key->area[key->data] = '\0';
/* perform update */
/* returned code: 1=ok, 0=ko */
HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
pat_ref_delete(ref, key->area);
HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
free_trash_chunk(key);
break;
}
case ACT_HTTP_ADD_ACL: {
struct pat_ref *ref;
struct buffer *key;
/* collect reference */
ref = pat_ref_lookup(rule->arg.map.ref);
if (!ref)
continue;
/* allocate key */
key = alloc_trash_chunk();
if (!key) {
rule_ret = HTTP_RULE_RES_BADREQ;
goto end;
}
/* collect key */
key->data = build_logline(s, key->area, key->size, &rule->arg.map.key);
key->area[key->data] = '\0';
/* perform update */
/* check if the entry already exists */
if (pat_ref_find_elt(ref, key->area) == NULL)
pat_ref_add(ref, key->area, NULL, NULL);
free_trash_chunk(key);
break;
}
case ACT_HTTP_SET_MAP: {
struct pat_ref *ref;
struct buffer *key, *value;
/* collect reference */
ref = pat_ref_lookup(rule->arg.map.ref);
if (!ref)
continue;
/* allocate key */
key = alloc_trash_chunk();
if (!key) {
rule_ret = HTTP_RULE_RES_BADREQ;
goto end;
}
/* allocate value */
value = alloc_trash_chunk();
if (!value) {
free_trash_chunk(key);
rule_ret = HTTP_RULE_RES_BADREQ;
goto end;
}
/* collect key */
key->data = build_logline(s, key->area, key->size, &rule->arg.map.key);
key->area[key->data] = '\0';
/* collect value */
value->data = build_logline(s, value->area, value->size, &rule->arg.map.value);
value->area[value->data] = '\0';
/* perform update */
HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
if (pat_ref_find_elt(ref, key->area) != NULL)
/* update entry if it exists */
pat_ref_set(ref, key->area, value->area, NULL);
else
/* insert a new entry */
pat_ref_add(ref, key->area, value->area, NULL);
HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
free_trash_chunk(key);
free_trash_chunk(value);
break;
}
case ACT_HTTP_REDIR:
rule_ret = HTTP_RULE_RES_DONE;
if (!http_apply_redirect_rule(rule->arg.redir, s, txn))
rule_ret = HTTP_RULE_RES_BADREQ;
goto end;
case ACT_ACTION_TRK_SC0 ... ACT_ACTION_TRK_SCMAX:
/* Note: only the first valid tracking parameter of each
* applies.
*/
if (stkctr_entry(&s->stkctr[trk_idx(rule->action)]) == NULL) {
struct stktable *t;
struct stksess *ts;
struct stktable_key *key;
void *ptr;
t = rule->arg.trk_ctr.table.t;
key = stktable_fetch_key(t, s->be, sess, s, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
rule->arg.trk_ctr.expr, NULL);
if (key && (ts = stktable_get_entry(t, key))) {
stream_track_stkctr(&s->stkctr[trk_idx(rule->action)], t, ts);
HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
/* let's count a new HTTP request as it's the first time we do it */
ptr = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_CNT);
if (ptr)
stktable_data_cast(ptr, http_req_cnt)++;
ptr = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_RATE);
if (ptr)
update_freq_ctr_period(&stktable_data_cast(ptr, http_req_rate),
t->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
/* When the client triggers a 4xx from the server, it's most often due
* to a missing object or permission. These events should be tracked
* because if they happen often, it may indicate a brute force or a
* vulnerability scan. Normally this is done when receiving the response
* but here we're tracking after this ought to have been done so we have
* to do it on purpose.
*/
if ((unsigned)(txn->status - 400) < 100) {
ptr = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_CNT);
if (ptr)
stktable_data_cast(ptr, http_err_cnt)++;
ptr = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_RATE);
if (ptr)
update_freq_ctr_period(&stktable_data_cast(ptr, http_err_rate),
t->data_arg[STKTABLE_DT_HTTP_ERR_RATE].u, 1);
}
HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
/* If data was modified, we need to touch to re-schedule sync */
stktable_touch_local(t, ts, 0);
stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_CONTENT);
if (sess->fe != s->be)
stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_BACKEND);
}
}
break;
case ACT_CUSTOM:
if ((s->req.flags & CF_READ_ERROR) ||
((s->req.flags & (CF_SHUTR|CF_READ_NULL)) &&
!(s->si[0].flags & SI_FL_CLEAN_ABRT) &&
(px->options & PR_O_ABRT_CLOSE)))
act_flags |= ACT_FLAG_FINAL;
switch (rule->action_ptr(rule, px, s->sess, s, act_flags)) {
case ACT_RET_ERR:
case ACT_RET_CONT:
break;
case ACT_RET_STOP:
rule_ret = HTTP_RULE_RES_STOP;
goto end;
case ACT_RET_YIELD:
s->current_rule = rule;
rule_ret = HTTP_RULE_RES_YIELD;
goto end;
}
break;
/* other flags exists, but normaly, they never be matched. */
default:
break;
}
}
end:
/* we reached the end of the rules, nothing to report */
return rule_ret;
}
/* This function terminates the request because it was completly analyzed or
* because an error was triggered during the body forwarding.
*/