MINOR: payload: allow the payload sample fetches to retrieve arbitrary lengths

When using req.payload and res.payload to look up for specific content at an
arbitrary location, we're often facing the problem of not knowing the input
buffer length. If the length argument is larger than the buffer length, the
function did not match, and if they're smaller, there is a risk of not getting
the expected content. This is especially true when looking for data in SOAP
requests.

So let's make some provisions for scanning the whole buffer by specifying a
length of 0 bytes. This greatly simplifies the processing of random-sized
input data.
This commit is contained in:
Willy Tarreau 2013-08-02 11:07:32 +02:00
parent 47060b6ae0
commit 00f0084752
2 changed files with 16 additions and 24 deletions

View File

@ -9662,7 +9662,10 @@ req_len : integer (deprecated)
req.payload(<offset>,<length>) : binary req.payload(<offset>,<length>) : binary
This extracts a binary block of <length> bytes and starting at byte <offset> This extracts a binary block of <length> bytes and starting at byte <offset>
in the request buffer. in the request buffer. As a special case, if the <length> argument is zero,
the the whole buffer from <offset> to the end is extracted. This can be used
with ACLs in order to check for the presence of some content in a buffer at
any location.
ACL alternatives : ACL alternatives :
payload(<offset>,<length>) : hex binary match payload(<offset>,<length>) : hex binary match
@ -9805,7 +9808,10 @@ req_ssl_ver : integer (deprecated)
res.payload(<offset>,<length>) : binary res.payload(<offset>,<length>) : binary
This extracts a binary block of <length> bytes and starting at byte <offset> This extracts a binary block of <length> bytes and starting at byte <offset>
in the response buffer. in the response buffer. As a special case, if the <length> argument is zero,
the the whole buffer from <offset> to the end is extracted. This can be used
with ACLs in order to check for the presence of some content in a buffer at
any location.
res.payload_lv(<offset1>,<length>[,<offset2>]) : binary res.payload_lv(<offset1>,<length>[,<offset2>]) : binary
This extracts a binary block whose size is specified at <offset1> for <length> This extracts a binary block whose size is specified at <offset1> for <length>

View File

@ -589,7 +589,7 @@ smp_fetch_payload(struct proxy *px, struct session *s, void *l7, unsigned int op
if (!chn) if (!chn)
return 0; return 0;
if (!buf_size || buf_size > chn->buf->size || buf_offset + buf_size > chn->buf->size) { if (buf_size > chn->buf->size || buf_offset + buf_size > chn->buf->size) {
/* will never match */ /* will never match */
smp->flags = 0; smp->flags = 0;
return 0; return 0;
@ -600,8 +600,11 @@ smp_fetch_payload(struct proxy *px, struct session *s, void *l7, unsigned int op
/* init chunk as read only */ /* init chunk as read only */
smp->type = SMP_T_CBIN; smp->type = SMP_T_CBIN;
chunk_initlen(&smp->data.str, chn->buf->p + buf_offset, 0, buf_size); chunk_initlen(&smp->data.str, chn->buf->p + buf_offset, 0, buf_size ? buf_size : (chn->buf->i - buf_offset));
smp->flags = SMP_F_VOLATILE; smp->flags = SMP_F_VOLATILE;
if (!buf_size && !channel_full(chn) && !channel_input_closed(chn))
smp->flags |= SMP_F_MAY_CHANGE;
return 1; return 1;
too_short: too_short:
@ -609,23 +612,6 @@ smp_fetch_payload(struct proxy *px, struct session *s, void *l7, unsigned int op
return 0; return 0;
} }
/* This function is used to validate the arguments passed to a "payload" fetch
* keyword. This keyword expects two positive integers, with the second one
* being strictly positive. It is assumed that the types are already the correct
* ones. Returns 0 on error, non-zero if OK. If <err_msg> 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_payload(struct arg *arg, char **err_msg)
{
if (!arg[1].data.uint) {
memprintf(err_msg, "payload length must be > 0");
return 0;
}
return 1;
}
/* This function is used to validate the arguments passed to a "payload_lv" fetch /* This function is used to validate the arguments passed to a "payload_lv" fetch
* keyword. This keyword allows two positive integers and an optional signed one, * keyword. This keyword allows two positive integers and an optional signed one,
* with the second one being strictly positive and the third one being greater than * with the second one being strictly positive and the third one being greater than
@ -660,7 +646,7 @@ static int val_payload_lv(struct arg *arg, char **err_msg)
* instance IPv4/IPv6 must be declared IPv4. * instance IPv4/IPv6 must be declared IPv4.
*/ */
static struct sample_fetch_kw_list smp_kws = {ILH, { static struct sample_fetch_kw_list smp_kws = {ILH, {
{ "payload", smp_fetch_payload, ARG2(2,UINT,UINT), val_payload, SMP_T_CBIN, SMP_USE_L6REQ|SMP_USE_L6RES }, { "payload", smp_fetch_payload, ARG2(2,UINT,UINT), NULL, SMP_T_CBIN, SMP_USE_L6REQ|SMP_USE_L6RES },
{ "payload_lv", smp_fetch_payload_lv, ARG3(2,UINT,UINT,SINT), val_payload_lv, SMP_T_CBIN, SMP_USE_L6REQ|SMP_USE_L6RES }, { "payload_lv", smp_fetch_payload_lv, ARG3(2,UINT,UINT,SINT), val_payload_lv, SMP_T_CBIN, SMP_USE_L6REQ|SMP_USE_L6RES },
{ "rdp_cookie", smp_fetch_rdp_cookie, ARG1(0,STR), NULL, SMP_T_CSTR, SMP_USE_L6REQ }, { "rdp_cookie", smp_fetch_rdp_cookie, ARG1(0,STR), NULL, SMP_T_CSTR, SMP_USE_L6REQ },
{ "rdp_cookie_cnt", smp_fetch_rdp_cookie_cnt, ARG1(0,STR), NULL, SMP_T_UINT, SMP_USE_L6REQ }, { "rdp_cookie_cnt", smp_fetch_rdp_cookie_cnt, ARG1(0,STR), NULL, SMP_T_UINT, SMP_USE_L6REQ },
@ -671,14 +657,14 @@ static struct sample_fetch_kw_list smp_kws = {ILH, {
{ "req_ssl_ver", smp_fetch_req_ssl_ver, 0, NULL, SMP_T_UINT, SMP_USE_L6REQ }, { "req_ssl_ver", smp_fetch_req_ssl_ver, 0, NULL, SMP_T_UINT, SMP_USE_L6REQ },
{ "req.len", smp_fetch_req_len, 0, NULL, SMP_T_UINT, SMP_USE_L6REQ }, { "req.len", smp_fetch_req_len, 0, NULL, SMP_T_UINT, SMP_USE_L6REQ },
{ "req.payload", smp_fetch_payload, ARG2(2,UINT,UINT), val_payload, SMP_T_CBIN, SMP_USE_L6REQ }, { "req.payload", smp_fetch_payload, ARG2(2,UINT,UINT), NULL, SMP_T_CBIN, SMP_USE_L6REQ },
{ "req.payload_lv", smp_fetch_payload_lv, ARG3(2,UINT,UINT,SINT), val_payload_lv, SMP_T_CBIN, SMP_USE_L6REQ }, { "req.payload_lv", smp_fetch_payload_lv, ARG3(2,UINT,UINT,SINT), val_payload_lv, SMP_T_CBIN, SMP_USE_L6REQ },
{ "req.rdp_cookie", smp_fetch_rdp_cookie, ARG1(0,STR), NULL, SMP_T_CSTR, SMP_USE_L6REQ }, { "req.rdp_cookie", smp_fetch_rdp_cookie, ARG1(0,STR), NULL, SMP_T_CSTR, SMP_USE_L6REQ },
{ "req.rdp_cookie_cnt", smp_fetch_rdp_cookie_cnt, ARG1(0,STR), NULL, SMP_T_UINT, SMP_USE_L6REQ }, { "req.rdp_cookie_cnt", smp_fetch_rdp_cookie_cnt, ARG1(0,STR), NULL, SMP_T_UINT, SMP_USE_L6REQ },
{ "req.ssl_hello_type", smp_fetch_ssl_hello_type, 0, NULL, SMP_T_UINT, SMP_USE_L6REQ }, { "req.ssl_hello_type", smp_fetch_ssl_hello_type, 0, NULL, SMP_T_UINT, SMP_USE_L6REQ },
{ "req.ssl_sni", smp_fetch_ssl_hello_sni, 0, NULL, SMP_T_CSTR, SMP_USE_L6REQ }, { "req.ssl_sni", smp_fetch_ssl_hello_sni, 0, NULL, SMP_T_CSTR, SMP_USE_L6REQ },
{ "req.ssl_ver", smp_fetch_req_ssl_ver, 0, NULL, SMP_T_UINT, SMP_USE_L6REQ }, { "req.ssl_ver", smp_fetch_req_ssl_ver, 0, NULL, SMP_T_UINT, SMP_USE_L6REQ },
{ "res.payload", smp_fetch_payload, ARG2(2,UINT,UINT), val_payload, SMP_T_CBIN, SMP_USE_L6RES }, { "res.payload", smp_fetch_payload, ARG2(2,UINT,UINT), NULL, SMP_T_CBIN, SMP_USE_L6RES },
{ "res.payload_lv", smp_fetch_payload_lv, ARG3(2,UINT,UINT,SINT), val_payload_lv, SMP_T_CBIN, SMP_USE_L6RES }, { "res.payload_lv", smp_fetch_payload_lv, ARG3(2,UINT,UINT,SINT), val_payload_lv, SMP_T_CBIN, SMP_USE_L6RES },
{ "res.ssl_hello_type", smp_fetch_ssl_hello_type, 0, NULL, SMP_T_UINT, SMP_USE_L6RES }, { "res.ssl_hello_type", smp_fetch_ssl_hello_type, 0, NULL, SMP_T_UINT, SMP_USE_L6RES },
{ "wait_end", smp_fetch_wait_end, 0, NULL, SMP_T_BOOL, SMP_USE_INTRN }, { "wait_end", smp_fetch_wait_end, 0, NULL, SMP_T_BOOL, SMP_USE_INTRN },