MINOR: checks: Add support of payload-based sample fetches

It is now possible to call check.payload(), check.payload_lv() and check.len()
sample fetches from any sample expression or log-format string in a tcp-check
based ruleset. In fact, check.payload() was already added. But instead of having
a specific function to handle this sample fetch, we use the same than
req.payload().

These sample fetches act on the check input buffer, containing data received for
the server. So it should be part of or after an expect rule, but before any send
rule. Because the input buffer is cleared at this stage.
This commit is contained in:
Christopher Faulet 2020-04-30 09:38:08 +02:00
parent 16fff67e2e
commit 78f371e498
3 changed files with 96 additions and 70 deletions

View File

@ -17506,14 +17506,31 @@ placed in the dedicated scope "check". Other sample fetches may also be called
when an health-check is performed if it makes sense and if the sample fetch was
adapted to be called in this context.
check.payload(<offset>,<length>) : binary
This extracts a binary block of <length> bytes and starting at byte <offset>
in the check input buffer. As a special case, if the <length> argument is
zero, then the whole buffer from <offset> to the end is extracted. This can
be called from a tcp-check expect rule, or eventually from a set-var rule
after an expect rule and before a send rule (check input buffer is filled on
check.len : integer
Returns an integer value corresponding to the number of bytes present in the
check input buffer, containing the data received from the server. This can be
called from a tcp-check expect rule, or eventually from a set-var rule after
an expect rule and before a send rule (check input buffer is filled on
tcp-check expect rules and reset on tcp-check send rules).
check.payload(<offset>,<length>) : binary
This extracts a binary block of <length> bytes and starting at byte <offset>
in the check input buffer, containing data received from the server. As a
special case, if the <length> argument is zero, then the whole buffer from
<offset> to the end is extracted. This can be called from a tcp-check expect
rule, or eventually from a set-var rule after an expect rule and before a
send rule (check input buffer is filled on tcp-check expect rules and reset
on tcp-check send rules).
check.payload_lv(<offset1>,<length>[,<offset2>]) : binary
This extracts a binary block whose size is specified at <offset1> for
<length> bytes, and which starts at <offset2> if specified or just after the
length in the check input buffer, containing data received from the
server. The <offset2> parameter also supports relative offsets if prepended
with a '+' or '-' sign. This can be called from a tcp-check expect rule, or
eventually from a set-var rule after an expect rule and before a send rule
(check input buffer is filled on tcp-check expect rules and reset on
tcp-check send rules).
7.3.8. Fetching samples for developers
---------------------------------------

View File

@ -1087,8 +1087,10 @@ static void tcpcheck_expect_onerror_message(struct buffer *msg, struct check *ch
*/
if (rule->expect.status_expr) {
smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
rule->expect.status_expr, SMP_T_SINT);
if (smp)
rule->expect.status_expr, SMP_T_STR);
if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
sample_casts[smp->data.type][SMP_T_SINT](smp))
check->code = smp->data.u.sint;
}
@ -1122,8 +1124,10 @@ static void tcpcheck_expect_onsuccess_message(struct buffer *msg, struct check *
*/
if (rule->expect.status_expr) {
smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
rule->expect.status_expr, SMP_T_SINT);
if (smp)
rule->expect.status_expr, SMP_T_STR);
if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
sample_casts[smp->data.type][SMP_T_SINT](smp))
check->code = smp->data.u.sint;
}
@ -5489,38 +5493,8 @@ void send_email_alert(struct server *s, int level, const char *format, ...)
/**************************************************************************/
/************************** Check sample fetches **************************/
/**************************************************************************/
/* extracts check payload at a fixed position and length */
static int
smp_fetch_chk_payload(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
{
unsigned int buf_offset = ((arg_p[0].type == ARGT_SINT) ? arg_p[0].data.sint : 0);
unsigned int buf_size = ((arg_p[1].type == ARGT_SINT) ? arg_p[1].data.sint : 0);
struct check *check = (smp->sess ? objt_check(smp->sess->origin) : NULL);
struct buffer *buf;
if (!check)
return 0;
buf = &check->bi;
if (buf_offset > b_data(buf))
goto no_match;
if (buf_offset + buf_size > b_data(buf))
buf_size = 0;
/* init chunk as read only */
smp->data.type = SMP_T_STR;
smp->flags = SMP_F_VOLATILE | SMP_F_CONST;
chunk_initlen(&smp->data.u.str, b_head(buf) + buf_offset, 0, (buf_size ? buf_size : (b_data(buf) - buf_offset)));
return 1;
no_match:
smp->flags = 0;
return 0;
}
static struct sample_fetch_kw_list smp_kws = {ILH, {
{ "check.payload", smp_fetch_chk_payload, ARG2(0,SINT,SINT), NULL, SMP_T_STR, SMP_USE_INTRN },
{ /* END */ },
}};
@ -5873,7 +5847,7 @@ int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, s
chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
"error-status", "L7STS",
"on-error", "%[check.payload(),cut_crlf]",
"on-error", "%[check.payload(0,0),cut_crlf]",
"on-success", "Redis server is ok",
""},
1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
@ -6075,7 +6049,7 @@ int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struc
chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
"min-recv", "4",
"error-status", "L7RSP",
"on-error", "%[check.payload(),cut_crlf]",
"on-error", "%[check.payload(0,0),cut_crlf]",
""},
1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
if (!chk) {

View File

@ -19,6 +19,7 @@
#include <proto/acl.h>
#include <proto/arg.h>
#include <proto/channel.h>
#include <proto/connection.h>
#include <proto/pattern.h>
#include <proto/payload.h>
#include <proto/sample.h>
@ -48,19 +49,23 @@ smp_fetch_wait_end(const struct arg *args, struct sample *smp, const char *kw, v
static int
smp_fetch_len(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
struct channel *chn;
if (!smp->strm)
return 0;
chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
smp->data.type = SMP_T_SINT;
if (IS_HTX_STRM(smp->strm)) {
struct htx *htx = htxbuf(&chn->buf);
smp->data.u.sint = htx->data - co_data(chn);
if (smp->strm) {
struct channel *chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
if (IS_HTX_STRM(smp->strm)) {
struct htx *htx = htxbuf(&chn->buf);
smp->data.u.sint = htx->data - co_data(chn);
}
else
smp->data.u.sint = ci_data(chn);
}
else if (smp->sess && obj_type(smp->sess->origin) == OBJ_TYPE_CHECK) {
struct check *check = __objt_check(smp->sess->origin);
smp->data.u.sint = ((check->cs && IS_HTX_CS(check->cs)) ? (htxbuf(&check->bi))->data: b_data(&check->bi));
}
else
smp->data.u.sint = ci_data(chn);
return 0;
smp->data.type = SMP_T_SINT;
smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
return 1;
}
@ -959,22 +964,35 @@ smp_fetch_payload_lv(const struct arg *arg_p, struct sample *smp, const char *kw
unsigned int len_size = arg_p[1].data.sint;
unsigned int buf_offset;
unsigned int buf_size = 0;
struct channel *chn;
struct channel *chn = NULL;
char *head = NULL;
size_t max, data;
int i;
/* Format is (len offset, len size, buf offset) or (len offset, len size) */
/* by default buf offset == len offset + len size */
/* buf offset could be absolute or relative to len offset + len size if prefixed by + or - */
if (!smp->strm)
if (smp->strm) {
chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
head = ci_head(chn);
data = ci_data(chn);
max = global.tune.bufsize;
}
else if (smp->sess && obj_type(smp->sess->origin) == OBJ_TYPE_CHECK) {
struct buffer *buf = &(__objt_check(smp->sess->origin)->bi);
head = b_head(buf);
data = b_data(buf);
max = global.tune.chksize;
}
if (!head)
return 0;
chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
if (len_offset + len_size > ci_data(chn))
if (len_offset + len_size > data)
goto too_short;
for (i = 0; i < len_size; i++) {
buf_size = (buf_size << 8) + ((unsigned char *)ci_head(chn))[i + len_offset];
buf_size = (buf_size << 8) + ((unsigned char *)head)[i + len_offset];
}
/* buf offset may be implicit, absolute or relative. If the LSB
@ -988,19 +1006,19 @@ smp_fetch_payload_lv(const struct arg *arg_p, struct sample *smp, const char *kw
buf_offset = arg_p[2].data.sint >> 1;
}
if (!buf_size || buf_size > global.tune.bufsize || buf_offset + buf_size > global.tune.bufsize) {
if (!buf_size || buf_size > max || buf_offset + buf_size > max) {
/* will never match */
smp->flags = 0;
return 0;
}
if (buf_offset + buf_size > ci_data(chn))
if (buf_offset + buf_size > data)
goto too_short;
/* init chunk as read only */
smp->data.type = SMP_T_BIN;
smp->flags = SMP_F_VOLATILE | SMP_F_CONST;
chunk_initlen(&smp->data.u.str, ci_head(chn) + buf_offset, 0, buf_size);
chunk_initlen(&smp->data.u.str, head + buf_offset, 0, buf_size);
return 1;
too_short:
@ -1014,31 +1032,44 @@ smp_fetch_payload(const struct arg *arg_p, struct sample *smp, const char *kw, v
{
unsigned int buf_offset = arg_p[0].data.sint;
unsigned int buf_size = arg_p[1].data.sint;
struct channel *chn;
struct channel *chn = NULL;
char *head = NULL;
size_t max, data;
if (!smp->strm)
if (smp->strm) {
chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
head = ci_head(chn);
data = ci_data(chn);
max = global.tune.bufsize;
}
else if (smp->sess && obj_type(smp->sess->origin) == OBJ_TYPE_CHECK) {
struct buffer *buf = &(__objt_check(smp->sess->origin)->bi);
head = b_head(buf);
data = b_data(buf);
max = global.tune.chksize;
}
if (!head)
return 0;
chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
if (buf_size > global.tune.bufsize || buf_offset + buf_size > global.tune.bufsize) {
if (buf_size > max || buf_offset + buf_size > max) {
/* will never match */
smp->flags = 0;
return 0;
}
if (buf_offset + buf_size > ci_data(chn))
if (buf_offset + buf_size > data)
goto too_short;
/* init chunk as read only */
smp->data.type = SMP_T_BIN;
smp->flags = SMP_F_VOLATILE | SMP_F_CONST;
chunk_initlen(&smp->data.u.str, ci_head(chn) + buf_offset, 0, buf_size ? buf_size : (ci_data(chn) - buf_offset));
if (!buf_size && channel_may_recv(chn) && !channel_input_closed(chn))
chunk_initlen(&smp->data.u.str, head + buf_offset, 0, buf_size ? buf_size : (data - buf_offset));
if (!buf_size && chn && channel_may_recv(chn) && !channel_input_closed(chn))
smp->flags |= SMP_F_MAY_CHANGE;
return 1;
too_short:
too_short:
smp->flags = SMP_F_MAY_CHANGE | SMP_F_CONST;
return 0;
}
@ -1328,6 +1359,10 @@ static struct sample_fetch_kw_list smp_kws = {ILH, {
{ "res.payload_lv", smp_fetch_payload_lv, ARG3(2,SINT,SINT,STR), val_payload_lv, SMP_T_BIN, SMP_USE_L6RES },
{ "res.ssl_hello_type", smp_fetch_ssl_hello_type, 0, NULL, SMP_T_SINT, SMP_USE_L6RES },
{ "wait_end", smp_fetch_wait_end, 0, NULL, SMP_T_BOOL, SMP_USE_INTRN },
{ "check.len", smp_fetch_len, 0, NULL, SMP_T_SINT, SMP_USE_INTRN },
{ "check.payload", smp_fetch_payload, ARG2(2,SINT,SINT), NULL, SMP_T_BIN, SMP_USE_INTRN },
{ "check.payload_lv", smp_fetch_payload_lv, ARG3(2,SINT,SINT,STR), val_payload_lv, SMP_T_BIN, SMP_USE_INTRN },
{ /* END */ },
}};