MEDIUM: samples: move payload-based fetches and ACLs to their own file

The file acl.c is a real mess, it both contains functions to parse and
process ACLs, and some sample extraction functions which act on buffers.
Some other payload analysers were arbitrarily dispatched to proto_tcp.c.

So now we're moving all payload-based fetches and ACLs to payload.c
which is capable of extracting data from buffers and rely on everything
that is protocol-independant. That way we can safely inflate this file
and only use the other ones when some fetches are really specific (eg:
HTTP, SSL, ...).

As a result of this cleanup, the following new sample fetches became
available even if they're not really useful :

  always_false, always_true, rep_ssl_hello_type, rdp_cookie_cnt,
  req_len, req_ssl_hello_type, req_ssl_sni, req_ssl_ver, wait_end

The function 'acl_fetch_nothing' was wrong and never used anywhere so it
was removed.

The "rdp_cookie" sample fetch used to have a mandatory argument while it
was optional in ACLs, which are supposed to iterate over RDP cookies. So
we're making it optional as a fetch too, and it will return the first one.
This commit is contained in:
Willy Tarreau 2013-01-07 21:59:07 +01:00
parent 434c57c95c
commit d4c33c8889
11 changed files with 915 additions and 754 deletions

View File

@ -634,7 +634,7 @@ OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocol.o \
src/stream_interface.o src/dumpstats.o src/proto_tcp.o \
src/session.o src/hdr_idx.o src/ev_select.o src/signal.o \
src/acl.o src/sample.o src/memory.o src/freq_ctr.o src/auth.o \
src/compression.o
src/compression.o src/payload.o
EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o \
$(EBTREE_DIR)/eb32tree.o $(EBTREE_DIR)/eb64tree.o \

View File

@ -115,7 +115,7 @@ OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocol.o \
src/session.o src/hdr_idx.o src/ev_select.o src/signal.o \
src/lb_chash.o src/lb_fwlc.o src/lb_fwrr.o src/lb_map.o src/lb_fas.o \
src/ev_poll.o src/ev_kqueue.o src/connection.o \
src/arg.o src/acl.o src/memory.o src/freq_ctr.o \
src/arg.o src/acl.o src/memory.o src/freq_ctr.o src/payload.o \
src/auth.o src/stick_table.o src/sample.o src/compression.o
EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o \

View File

@ -111,7 +111,7 @@ OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocol.o \
src/peers.o src/stream_interface.o src/dumpstats.o src/proto_tcp.o \
src/session.o src/hdr_idx.o src/ev_select.o src/signal.o \
src/lb_chash.o src/lb_fwlc.o src/lb_fwrr.o src/lb_map.o src/lb_fas.o \
src/ev_poll.o src/connection.o \
src/ev_poll.o src/connection.o src/payload.o \
src/arg.o src/acl.o src/memory.o src/freq_ctr.o \
src/auth.o src/stick_table.o src/sample.o src/compression.o

View File

@ -8723,8 +8723,8 @@ rep_ssl_hello_type <integer>
or superior) hello message and handshake type is equal to <integer>.
This test was designed to be used with TCP response content inspection: a
SSL session ID may be fetched. Note that this only applies to raw contents
found in the request buffer and not to contents deciphered via an SSL data
layer, so this will not work with "bind" lines having the "ssl" option.
found in the response buffer and not to contents deciphered via an SSL data
layer, so this will not work with "server" lines having the "ssl" option.
req_len <integer>
Returns true when the length of the data in the request buffer matches the
@ -9542,6 +9542,12 @@ equivalent used in ACLs.
The list of currently supported pattern fetch functions is the following :
always_false
Always returns the boolean "false" value.
always_true
Always returns the boolean "true" value.
base This returns the concatenation of the first Host header and the
path part of the request, which starts at the first slash and
ends before the question mark. It can be useful in virtual
@ -9561,10 +9567,16 @@ The list of currently supported pattern fetch functions is the following :
size of 8 or 20 bytes depending on the source address family.
This can be used to track per-IP, per-URL counters.
src This is the source IPv4 address of the client of the session.
It is of type IPv4 and works on both IPv4 and IPv6 tables.
On IPv6 tables, IPv4 address is mapped to its IPv6 equivalent,
according to RFC 4291.
cookie(<name>)
This extracts the last occurrence of the cookie name <name> on a
"Cookie" header line from the request, or a "Set-Cookie" header
from the response, and uses the corresponding value to match. A
typical use is to get multiple clients sharing a same profile
use the same server. This can be similar to what "appsession"
does with the "request-learn" statement, but with support for
multi-peer synchronization and state keeping across restarts.
See also : "appsession"
dst This is the destination IPv4 address of the session on the
client side, which is the address the client connected to.
@ -9610,6 +9622,97 @@ The list of currently supported pattern fetch functions is the following :
<lengthoffset> + <lengthsize> else it is absolute.
Ex: see SSL session id example in "stick table" chapter.
rdp_cookie(<name>)
This extracts the value of the rdp cookie <name> as a string
and uses this value to match. This enables implementation of
persistence based on the mstshash cookie. This is typically
done if there is no msts cookie present.
This differs from "balance rdp-cookie" in that any balancing
algorithm may be used and thus the distribution of clients
to backend servers is not linked to a hash of the RDP
cookie. It is envisaged that using a balancing algorithm
such as "balance roundrobin" or "balance leastconnect" will
lead to a more even distribution of clients to backend
servers than the hash used by "balance rdp-cookie".
Example :
listen tse-farm
bind 0.0.0.0:3389
# wait up to 5s for an RDP cookie in the request
tcp-request inspect-delay 5s
tcp-request content accept if RDP_COOKIE
# apply RDP cookie persistence
persist rdp-cookie
# Persist based on the mstshash cookie
# This is only useful makes sense if
# balance rdp-cookie is not used
stick-table type string size 204800
stick on rdp_cookie(mstshash)
server srv1 1.1.1.1:3389
server srv1 1.1.1.2:3389
See also : "balance rdp-cookie", "persist rdp-cookie",
"tcp-request" and the "req_rdp_cookie" ACL.
rdp_cookie_cnt([name])
Tries to parse the request buffer as RDP protocol, then returns
an integer corresponding to the number of RDP cookies found. If
an optional cookie name is passed, only cookies matching this
name are considered. This is mostly used in ACL.
rep_ssl_hello_type
Returns an integer value containing the type of the SSL hello
message found in the response buffer. Note that this only
applies to raw contents found in the response buffer and not to
contents deciphered via an SSL data layer, so this will not work
with "server" lines having the "ssl" option. This is mostly used
in ACL.
req_len Returns an integer value corresponding to the number of bytes
present in the request buffer. This is mostly used in ACL.
req_ssl_hello_type
Returns an integer value containing the type of the SSL hello
message found in the request buffer. Note that this only applies
to raw contents found in the request buffer and not to contents
deciphered via an SSL data layer, so this will not work with
"bind" lines having the "ssl" option. This is mostly used in
ACL.
req_ssl_sni Returns a string containing the value of the Server Name TLS
extension sent by a client in a TLS stream passing through the
request buffer. Note that this only applies to raw contents
found in the request buffer and not to contents deciphered via
an SSL data layer, so this will not work with "bind" lines
having the "ssl" option. This is mostly used in ACL.
req_ssl_ver Returns an integer value containing the version of the SSL/TLS
protocol of a stream present in the request buffer. The value is
composed of the major version multiplied by 65536, added to the
minor version. Note that this only applies to raw contents found
in the request buffer and not to contents deciphered via an SSL
data layer, so this will not work with "bind" lines having the
"ssl" option. This is mostly used in ACL.
set-cookie(<name>)
This fetch function is deprecated and has been superseded by the
"cookie" fetch which is capable of handling both requests and
responses. This keyword will disappear soon.
This extracts the last occurrence of the cookie name <name> on a
"Set-Cookie" header line from the response and uses the
corresponding value to match. This can be comparable to what
"appsession" does with default options, but with support for
multi-peer synchronization and state keeping across restarts.
See also : "appsession"
src This is the source IPv4 address of the client of the session.
It is of type IPv4 and works on both IPv4 and IPv6 tables.
On IPv6 tables, IPv4 address is mapped to its IPv6 equivalent,
according to RFC 4291.
src_port This is the source TCP port of the session on the client side,
which is the port the client connected from. It is very unlikely
that this function will be useful but it's available at no cost.
@ -9823,62 +9926,8 @@ The list of currently supported pattern fetch functions is the following :
# match http://example.com/foo;JSESSIONID=some_id
stick on url_param(JSESSIONID,;)
rdp_cookie(<name>)
This extracts the value of the rdp cookie <name> as a string
and uses this value to match. This enables implementation of
persistence based on the mstshash cookie. This is typically
done if there is no msts cookie present.
This differs from "balance rdp-cookie" in that any balancing
algorithm may be used and thus the distribution of clients
to backend servers is not linked to a hash of the RDP
cookie. It is envisaged that using a balancing algorithm
such as "balance roundrobin" or "balance leastconnect" will
lead to a more even distribution of clients to backend
servers than the hash used by "balance rdp-cookie".
Example :
listen tse-farm
bind 0.0.0.0:3389
# wait up to 5s for an RDP cookie in the request
tcp-request inspect-delay 5s
tcp-request content accept if RDP_COOKIE
# apply RDP cookie persistence
persist rdp-cookie
# Persist based on the mstshash cookie
# This is only useful makes sense if
# balance rdp-cookie is not used
stick-table type string size 204800
stick on rdp_cookie(mstshash)
server srv1 1.1.1.1:3389
server srv1 1.1.1.2:3389
See also : "balance rdp-cookie", "persist rdp-cookie",
"tcp-request" and the "req_rdp_cookie" ACL.
cookie(<name>)
This extracts the last occurrence of the cookie name <name> on a
"Cookie" header line from the request, or a "Set-Cookie" header
from the response, and uses the corresponding value to match. A
typical use is to get multiple clients sharing a same profile
use the same server. This can be similar to what "appsession"
does with the "request-learn" statement, but with support for
multi-peer synchronization and state keeping across restarts.
See also : "appsession"
set-cookie(<name>)
This fetch function is deprecated and has been superseded by the
"cookie" fetch which is capable of handling both requests and
responses. This keyword will disappear soon.
This extracts the last occurrence of the cookie name <name> on a
"Set-Cookie" header line from the response and uses the
corresponding value to match. This can be comparable to what
"appsession" does with default options, but with support for
multi-peer synchronization and state keeping across restarts.
See also : "appsession"
wait_end Always returns true or does not fetch. This is only used for ACL
compatibility.
The currently available list of transformations include :

View File

@ -2,7 +2,7 @@
* include/proto/acl.h
* This file provides interface definitions for ACL manipulation.
*
* Copyright (C) 2000-2011 Willy Tarreau - w@1wt.eu
* Copyright (C) 2000-2013 Willy Tarreau - w@1wt.eu
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -177,10 +177,6 @@ int acl_parse_reg(const char **text, struct acl_pattern *pattern, int *opaque, c
*/
int acl_parse_ip(const char **text, struct acl_pattern *pattern, int *opaque, char **err);
/* always fake a data retrieval */
int acl_fetch_nothing(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp);
/* always return false */
int acl_match_nothing(struct sample *smp, struct acl_pattern *pattern);

40
include/proto/payload.h Normal file
View File

@ -0,0 +1,40 @@
/*
* include/proto/payload.h
* Definitions for payload-based sample fetches and ACLs
*
* Copyright (C) 2000-2013 Willy Tarreau - w@1wt.eu
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, version 2.1
* exclusively.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PROTO_PROTO_PAYLOAD_H
#define _PROTO_PROTO_PAYLOAD_H
#include <common/config.h>
#include <types/arg.h>
#include <types/proxy.h>
#include <types/sample.h>
#include <types/session.h>
int smp_fetch_rdp_cookie(struct proxy *px, struct session *s, void *l7, unsigned int opt, const struct arg *args, struct sample *smp);
#endif /* _PROTO_PROTO_PAYLOAD_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -2,7 +2,7 @@
* include/proto/proto_tcp.h
* This file contains TCP socket protocol definitions.
*
* Copyright (C) 2000-2010 Willy Tarreau - w@1wt.eu
* Copyright (C) 2000-2013 Willy Tarreau - w@1wt.eu
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -37,7 +37,6 @@ int tcp_get_dst(int fd, struct sockaddr *sa, socklen_t salen, int dir);
int tcp_inspect_request(struct session *s, struct channel *req, int an_bit);
int tcp_inspect_response(struct session *s, struct channel *rep, int an_bit);
int tcp_exec_req_rules(struct session *s);
int smp_fetch_rdp_cookie(struct proxy *px, struct session *l4, void *l7, unsigned int opt, const struct arg *args, struct sample *smp);
/* Converts the INET/INET6 source address to a stick_table key usable for table
* lookups. Returns either NULL if the source cannot be converted (eg: not

456
src/acl.c
View File

@ -1,7 +1,7 @@
/*
* ACL management functions.
*
* Copyright 2000-2011 Willy Tarreau <w@1wt.eu>
* Copyright 2000-2013 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -59,400 +59,6 @@ static struct acl_kw_list acl_keywords = {
};
/*
* These functions are only used for debugging complex configurations.
*/
/* force TRUE to be returned at the fetch level */
static int
acl_fetch_true(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
smp->type = SMP_T_BOOL;
smp->data.uint = 1;
return 1;
}
/* wait for more data as long as possible, then return TRUE. This should be
* used with content inspection.
*/
static int
acl_fetch_wait_end(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
if (!(opt & SMP_OPT_FINAL)) {
smp->flags |= SMP_F_MAY_CHANGE;
return 0;
}
smp->type = SMP_T_BOOL;
smp->data.uint = 1;
return 1;
}
/* force FALSE to be returned at the fetch level */
static int
acl_fetch_false(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
smp->type = SMP_T_BOOL;
smp->data.uint = 0;
return 1;
}
/* return the number of bytes in the request buffer */
static int
acl_fetch_req_len(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
if (!l4 || !l4->req)
return 0;
smp->type = SMP_T_UINT;
smp->data.uint = l4->req->buf->i;
smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
return 1;
}
static int
acl_fetch_ssl_hello_type(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
int hs_len;
int hs_type, bleft;
struct channel *chn;
const unsigned char *data;
if (!l4)
goto not_ssl_hello;
chn = ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? l4->rep : l4->req;
bleft = chn->buf->i;
data = (const unsigned char *)chn->buf->p;
if (!bleft)
goto too_short;
if ((*data >= 0x14 && *data <= 0x17) || (*data == 0xFF)) {
/* SSLv3 header format */
if (bleft < 9)
goto too_short;
/* ssl version 3 */
if ((data[1] << 16) + data[2] < 0x00030000)
goto not_ssl_hello;
/* ssl message len must present handshake type and len */
if ((data[3] << 8) + data[4] < 4)
goto not_ssl_hello;
/* format introduced with SSLv3 */
hs_type = (int)data[5];
hs_len = ( data[6] << 16 ) + ( data[7] << 8 ) + data[8];
/* not a full handshake */
if (bleft < (9 + hs_len))
goto too_short;
}
else {
goto not_ssl_hello;
}
smp->type = SMP_T_UINT;
smp->data.uint = hs_type;
smp->flags = SMP_F_VOLATILE;
return 1;
too_short:
smp->flags = SMP_F_MAY_CHANGE;
not_ssl_hello:
return 0;
}
/* Return the version of the SSL protocol in the request. It supports both
* SSLv3 (TLSv1) header format for any message, and SSLv2 header format for
* the hello message. The SSLv3 format is described in RFC 2246 p49, and the
* SSLv2 format is described here, and completed p67 of RFC 2246 :
* http://wp.netscape.com/eng/security/SSL_2.html
*
* Note: this decoder only works with non-wrapping data.
*/
static int
acl_fetch_req_ssl_ver(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
int version, bleft, msg_len;
const unsigned char *data;
if (!l4 || !l4->req)
return 0;
msg_len = 0;
bleft = l4->req->buf->i;
if (!bleft)
goto too_short;
data = (const unsigned char *)l4->req->buf->p;
if ((*data >= 0x14 && *data <= 0x17) || (*data == 0xFF)) {
/* SSLv3 header format */
if (bleft < 5)
goto too_short;
version = (data[1] << 16) + data[2]; /* version: major, minor */
msg_len = (data[3] << 8) + data[4]; /* record length */
/* format introduced with SSLv3 */
if (version < 0x00030000)
goto not_ssl;
/* message length between 1 and 2^14 + 2048 */
if (msg_len < 1 || msg_len > ((1<<14) + 2048))
goto not_ssl;
bleft -= 5; data += 5;
} else {
/* SSLv2 header format, only supported for hello (msg type 1) */
int rlen, plen, cilen, silen, chlen;
if (*data & 0x80) {
if (bleft < 3)
goto too_short;
/* short header format : 15 bits for length */
rlen = ((data[0] & 0x7F) << 8) | data[1];
plen = 0;
bleft -= 2; data += 2;
} else {
if (bleft < 4)
goto too_short;
/* long header format : 14 bits for length + pad length */
rlen = ((data[0] & 0x3F) << 8) | data[1];
plen = data[2];
bleft -= 3; data += 2;
}
if (*data != 0x01)
goto not_ssl;
bleft--; data++;
if (bleft < 8)
goto too_short;
version = (data[0] << 16) + data[1]; /* version: major, minor */
cilen = (data[2] << 8) + data[3]; /* cipher len, multiple of 3 */
silen = (data[4] << 8) + data[5]; /* session_id_len: 0 or 16 */
chlen = (data[6] << 8) + data[7]; /* 16<=challenge length<=32 */
bleft -= 8; data += 8;
if (cilen % 3 != 0)
goto not_ssl;
if (silen && silen != 16)
goto not_ssl;
if (chlen < 16 || chlen > 32)
goto not_ssl;
if (rlen != 9 + cilen + silen + chlen)
goto not_ssl;
/* focus on the remaining data length */
msg_len = cilen + silen + chlen + plen;
}
/* We could recursively check that the buffer ends exactly on an SSL
* fragment boundary and that a possible next segment is still SSL,
* but that's a bit pointless. However, we could still check that
* all the part of the request which fits in a buffer is already
* there.
*/
if (msg_len > buffer_max_len(l4->req) + l4->req->buf->data - l4->req->buf->p)
msg_len = buffer_max_len(l4->req) + l4->req->buf->data - l4->req->buf->p;
if (bleft < msg_len)
goto too_short;
/* OK that's enough. We have at least the whole message, and we have
* the protocol version.
*/
smp->type = SMP_T_UINT;
smp->data.uint = version;
smp->flags = SMP_F_VOLATILE;
return 1;
too_short:
smp->flags = SMP_F_MAY_CHANGE;
not_ssl:
return 0;
}
/* Try to extract the Server Name Indication that may be presented in a TLS
* client hello handshake message. The format of the message is the following
* (cf RFC5246 + RFC6066) :
* TLS frame :
* - uint8 type = 0x16 (Handshake)
* - uint16 version >= 0x0301 (TLSv1)
* - uint16 length (frame length)
* - TLS handshake :
* - uint8 msg_type = 0x01 (ClientHello)
* - uint24 length (handshake message length)
* - ClientHello :
* - uint16 client_version >= 0x0301 (TLSv1)
* - uint8 Random[32] (4 first ones are timestamp)
* - SessionID :
* - uint8 session_id_len (0..32) (SessionID len in bytes)
* - uint8 session_id[session_id_len]
* - CipherSuite :
* - uint16 cipher_len >= 2 (Cipher length in bytes)
* - uint16 ciphers[cipher_len/2]
* - CompressionMethod :
* - uint8 compression_len >= 1 (# of supported methods)
* - uint8 compression_methods[compression_len]
* - optional client_extension_len (in bytes)
* - optional sequence of ClientHelloExtensions (as many bytes as above):
* - uint16 extension_type = 0 for server_name
* - uint16 extension_len
* - opaque extension_data[extension_len]
* - uint16 server_name_list_len (# of bytes here)
* - opaque server_names[server_name_list_len bytes]
* - uint8 name_type = 0 for host_name
* - uint16 name_len
* - opaque hostname[name_len bytes]
*/
static int
acl_fetch_ssl_hello_sni(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
int hs_len, ext_len, bleft;
struct channel *chn;
unsigned char *data;
if (!l4)
goto not_ssl_hello;
chn = ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? l4->rep : l4->req;
bleft = chn->buf->i;
data = (unsigned char *)chn->buf->p;
/* Check for SSL/TLS Handshake */
if (!bleft)
goto too_short;
if (*data != 0x16)
goto not_ssl_hello;
/* Check for TLSv1 or later (SSL version >= 3.1) */
if (bleft < 3)
goto too_short;
if (data[1] < 0x03 || data[2] < 0x01)
goto not_ssl_hello;
if (bleft < 5)
goto too_short;
hs_len = (data[3] << 8) + data[4];
if (hs_len < 1 + 3 + 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
goto not_ssl_hello; /* too short to have an extension */
data += 5; /* enter TLS handshake */
bleft -= 5;
/* Check for a complete client hello starting at <data> */
if (bleft < 1)
goto too_short;
if (data[0] != 0x01) /* msg_type = Client Hello */
goto not_ssl_hello;
/* Check the Hello's length */
if (bleft < 4)
goto too_short;
hs_len = (data[1] << 16) + (data[2] << 8) + data[3];
if (hs_len < 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
goto not_ssl_hello; /* too short to have an extension */
/* We want the full handshake here */
if (bleft < hs_len)
goto too_short;
data += 4;
/* Start of the ClientHello message */
if (data[0] < 0x03 || data[1] < 0x01) /* TLSv1 minimum */
goto not_ssl_hello;
ext_len = data[34]; /* session_id_len */
if (ext_len > 32 || ext_len > (hs_len - 35)) /* check for correct session_id len */
goto not_ssl_hello;
/* Jump to cipher suite */
hs_len -= 35 + ext_len;
data += 35 + ext_len;
if (hs_len < 4 || /* minimum one cipher */
(ext_len = (data[0] << 8) + data[1]) < 2 || /* minimum 2 bytes for a cipher */
ext_len > hs_len)
goto not_ssl_hello;
/* Jump to the compression methods */
hs_len -= 2 + ext_len;
data += 2 + ext_len;
if (hs_len < 2 || /* minimum one compression method */
data[0] < 1 || data[0] > hs_len) /* minimum 1 bytes for a method */
goto not_ssl_hello;
/* Jump to the extensions */
hs_len -= 1 + data[0];
data += 1 + data[0];
if (hs_len < 2 || /* minimum one extension list length */
(ext_len = (data[0] << 8) + data[1]) > hs_len - 2) /* list too long */
goto not_ssl_hello;
hs_len = ext_len; /* limit ourselves to the extension length */
data += 2;
while (hs_len >= 4) {
int ext_type, name_type, srv_len, name_len;
ext_type = (data[0] << 8) + data[1];
ext_len = (data[2] << 8) + data[3];
if (ext_len > hs_len - 4) /* Extension too long */
goto not_ssl_hello;
if (ext_type == 0) { /* Server name */
if (ext_len < 2) /* need one list length */
goto not_ssl_hello;
srv_len = (data[4] << 8) + data[5];
if (srv_len < 4 || srv_len > hs_len - 6)
goto not_ssl_hello; /* at least 4 bytes per server name */
name_type = data[6];
name_len = (data[7] << 8) + data[8];
if (name_type == 0) { /* hostname */
smp->type = SMP_T_CSTR;
smp->data.str.str = (char *)data + 9;
smp->data.str.len = name_len;
smp->flags = SMP_F_VOLATILE;
return 1;
}
}
hs_len -= 4 + ext_len;
data += 4 + ext_len;
}
/* server name not found */
goto not_ssl_hello;
too_short:
smp->flags = SMP_F_MAY_CHANGE;
not_ssl_hello:
return 0;
}
/*
* These functions are exported and may be used by any other component.
*/
@ -463,13 +69,6 @@ int acl_parse_nothing(const char **text, struct acl_pattern *pattern, int *opaqu
return 1;
}
/* always fake a data retrieval */
int acl_fetch_nothing(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
return 1;
}
/* always return false */
int acl_match_nothing(struct sample *smp, struct acl_pattern *pattern)
{
@ -2302,29 +1901,62 @@ acl_find_targets(struct proxy *p)
return cfgerr;
}
/************************************************************************/
/* All supported keywords must be declared here. */
/* All supported sample fetch functions must be declared here */
/************************************************************************/
/* force TRUE to be returned at the fetch level */
static int
smp_fetch_true(struct proxy *px, struct session *s, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
smp->type = SMP_T_BOOL;
smp->data.uint = 1;
return 1;
}
/* force FALSE to be returned at the fetch level */
static int
smp_fetch_false(struct proxy *px, struct session *s, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
smp->type = SMP_T_BOOL;
smp->data.uint = 0;
return 1;
}
/************************************************************************/
/* All supported sample and ACL keywords must be declared here. */
/************************************************************************/
/* Note: must not be declared <const> as its list will be overwritten.
* Note: fetches that may return multiple types must be declared as the lowest
* common denominator, the type that can be casted into all other ones. For
* instance IPv4/IPv6 must be declared IPv4.
*/
static struct sample_fetch_kw_list smp_kws = {{ },{
{ "always_false", smp_fetch_false, 0, NULL, SMP_T_BOOL, SMP_USE_INTRN },
{ "always_true", smp_fetch_true, 0, NULL, SMP_T_BOOL, SMP_USE_INTRN },
{ /* END */ },
}};
/* Note: must not be declared <const> as its list will be overwritten.
* Please take care of keeping this list alphabetically sorted.
*/
static struct acl_kw_list acl_kws = {{ },{
{ "always_false", acl_parse_nothing, acl_fetch_false, acl_match_nothing, ACL_USE_NOTHING, 0 },
{ "always_true", acl_parse_nothing, acl_fetch_true, acl_match_nothing, ACL_USE_NOTHING, 0 },
{ "rep_ssl_hello_type", acl_parse_int, acl_fetch_ssl_hello_type, acl_match_int, ACL_USE_L6RTR_VOLATILE, 0 },
{ "req_len", acl_parse_int, acl_fetch_req_len, acl_match_int, ACL_USE_L6REQ_VOLATILE, 0 },
{ "req_ssl_hello_type", acl_parse_int, acl_fetch_ssl_hello_type, acl_match_int, ACL_USE_L6REQ_VOLATILE, 0 },
{ "req_ssl_sni", acl_parse_str, acl_fetch_ssl_hello_sni, acl_match_str, ACL_USE_L6REQ_VOLATILE, 0 },
{ "req_ssl_ver", acl_parse_dotted_ver, acl_fetch_req_ssl_ver, acl_match_int, ACL_USE_L6REQ_VOLATILE, 0 },
{ "wait_end", acl_parse_nothing, acl_fetch_wait_end, acl_match_nothing, ACL_USE_NOTHING, 0 },
{ NULL, NULL, NULL, NULL }
{ "always_false", acl_parse_nothing, smp_fetch_false, acl_match_nothing, ACL_USE_NOTHING, 0 },
{ "always_true", acl_parse_nothing, smp_fetch_true, acl_match_nothing, ACL_USE_NOTHING, 0 },
{ /* END */ },
}};
__attribute__((constructor))
static void __acl_init(void)
{
sample_register_fetches(&smp_kws);
acl_register_keywords(&acl_kws);
}

View File

@ -39,10 +39,12 @@
#include <proto/lb_fwrr.h>
#include <proto/lb_map.h>
#include <proto/obj_type.h>
#include <proto/payload.h>
#include <proto/protocol.h>
#include <proto/proto_http.h>
#include <proto/proto_tcp.h>
#include <proto/queue.h>
#include <proto/sample.h>
#include <proto/server.h>
#include <proto/session.h>
#include <proto/raw_sock.h>

698
src/payload.c Normal file
View File

@ -0,0 +1,698 @@
/*
* General protocol-agnostic payload-based sample fetches and ACLs
*
* Copyright 2000-2013 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <stdlib.h>
#include <string.h>
#include <proto/acl.h>
#include <proto/arg.h>
#include <proto/channel.h>
#include <proto/payload.h>
#include <proto/sample.h>
/************************************************************************/
/* All supported sample fetch functions must be declared here */
/************************************************************************/
/* wait for more data as long as possible, then return TRUE. This should be
* used with content inspection.
*/
static int
smp_fetch_wait_end(struct proxy *px, struct session *s, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
if (!(opt & SMP_OPT_FINAL)) {
smp->flags |= SMP_F_MAY_CHANGE;
return 0;
}
smp->type = SMP_T_BOOL;
smp->data.uint = 1;
return 1;
}
/* return the number of bytes in the request buffer */
static int
smp_fetch_req_len(struct proxy *px, struct session *s, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
if (!s || !s->req)
return 0;
smp->type = SMP_T_UINT;
smp->data.uint = s->req->buf->i;
smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
return 1;
}
/* returns the type of SSL hello message (mainly used to detect an SSL hello) */
static int
smp_fetch_ssl_hello_type(struct proxy *px, struct session *s, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
int hs_len;
int hs_type, bleft;
struct channel *chn;
const unsigned char *data;
if (!s)
goto not_ssl_hello;
chn = ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? s->rep : s->req;
bleft = chn->buf->i;
data = (const unsigned char *)chn->buf->p;
if (!bleft)
goto too_short;
if ((*data >= 0x14 && *data <= 0x17) || (*data == 0xFF)) {
/* SSLv3 header format */
if (bleft < 9)
goto too_short;
/* ssl version 3 */
if ((data[1] << 16) + data[2] < 0x00030000)
goto not_ssl_hello;
/* ssl message len must present handshake type and len */
if ((data[3] << 8) + data[4] < 4)
goto not_ssl_hello;
/* format introduced with SSLv3 */
hs_type = (int)data[5];
hs_len = ( data[6] << 16 ) + ( data[7] << 8 ) + data[8];
/* not a full handshake */
if (bleft < (9 + hs_len))
goto too_short;
}
else {
goto not_ssl_hello;
}
smp->type = SMP_T_UINT;
smp->data.uint = hs_type;
smp->flags = SMP_F_VOLATILE;
return 1;
too_short:
smp->flags = SMP_F_MAY_CHANGE;
not_ssl_hello:
return 0;
}
/* Return the version of the SSL protocol in the request. It supports both
* SSLv3 (TLSv1) header format for any message, and SSLv2 header format for
* the hello message. The SSLv3 format is described in RFC 2246 p49, and the
* SSLv2 format is described here, and completed p67 of RFC 2246 :
* http://wp.netscape.com/eng/security/SSL_2.html
*
* Note: this decoder only works with non-wrapping data.
*/
static int
smp_fetch_req_ssl_ver(struct proxy *px, struct session *s, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
int version, bleft, msg_len;
const unsigned char *data;
if (!s || !s->req)
return 0;
msg_len = 0;
bleft = s->req->buf->i;
if (!bleft)
goto too_short;
data = (const unsigned char *)s->req->buf->p;
if ((*data >= 0x14 && *data <= 0x17) || (*data == 0xFF)) {
/* SSLv3 header format */
if (bleft < 5)
goto too_short;
version = (data[1] << 16) + data[2]; /* version: major, minor */
msg_len = (data[3] << 8) + data[4]; /* record length */
/* format introduced with SSLv3 */
if (version < 0x00030000)
goto not_ssl;
/* message length between 1 and 2^14 + 2048 */
if (msg_len < 1 || msg_len > ((1<<14) + 2048))
goto not_ssl;
bleft -= 5; data += 5;
} else {
/* SSLv2 header format, only supported for hello (msg type 1) */
int rlen, plen, cilen, silen, chlen;
if (*data & 0x80) {
if (bleft < 3)
goto too_short;
/* short header format : 15 bits for length */
rlen = ((data[0] & 0x7F) << 8) | data[1];
plen = 0;
bleft -= 2; data += 2;
} else {
if (bleft < 4)
goto too_short;
/* long header format : 14 bits for length + pad length */
rlen = ((data[0] & 0x3F) << 8) | data[1];
plen = data[2];
bleft -= 3; data += 2;
}
if (*data != 0x01)
goto not_ssl;
bleft--; data++;
if (bleft < 8)
goto too_short;
version = (data[0] << 16) + data[1]; /* version: major, minor */
cilen = (data[2] << 8) + data[3]; /* cipher len, multiple of 3 */
silen = (data[4] << 8) + data[5]; /* session_id_len: 0 or 16 */
chlen = (data[6] << 8) + data[7]; /* 16<=challenge length<=32 */
bleft -= 8; data += 8;
if (cilen % 3 != 0)
goto not_ssl;
if (silen && silen != 16)
goto not_ssl;
if (chlen < 16 || chlen > 32)
goto not_ssl;
if (rlen != 9 + cilen + silen + chlen)
goto not_ssl;
/* focus on the remaining data length */
msg_len = cilen + silen + chlen + plen;
}
/* We could recursively check that the buffer ends exactly on an SSL
* fragment boundary and that a possible next segment is still SSL,
* but that's a bit pointless. However, we could still check that
* all the part of the request which fits in a buffer is already
* there.
*/
if (msg_len > buffer_max_len(s->req) + s->req->buf->data - s->req->buf->p)
msg_len = buffer_max_len(s->req) + s->req->buf->data - s->req->buf->p;
if (bleft < msg_len)
goto too_short;
/* OK that's enough. We have at least the whole message, and we have
* the protocol version.
*/
smp->type = SMP_T_UINT;
smp->data.uint = version;
smp->flags = SMP_F_VOLATILE;
return 1;
too_short:
smp->flags = SMP_F_MAY_CHANGE;
not_ssl:
return 0;
}
/* Try to extract the Server Name Indication that may be presented in a TLS
* client hello handshake message. The format of the message is the following
* (cf RFC5246 + RFC6066) :
* TLS frame :
* - uint8 type = 0x16 (Handshake)
* - uint16 version >= 0x0301 (TLSv1)
* - uint16 length (frame length)
* - TLS handshake :
* - uint8 msg_type = 0x01 (ClientHello)
* - uint24 length (handshake message length)
* - ClientHello :
* - uint16 client_version >= 0x0301 (TLSv1)
* - uint8 Random[32] (4 first ones are timestamp)
* - SessionID :
* - uint8 session_id_len (0..32) (SessionID len in bytes)
* - uint8 session_id[session_id_len]
* - CipherSuite :
* - uint16 cipher_len >= 2 (Cipher length in bytes)
* - uint16 ciphers[cipher_len/2]
* - CompressionMethod :
* - uint8 compression_len >= 1 (# of supported methods)
* - uint8 compression_methods[compression_len]
* - optional client_extension_len (in bytes)
* - optional sequence of ClientHelloExtensions (as many bytes as above):
* - uint16 extension_type = 0 for server_name
* - uint16 extension_len
* - opaque extension_data[extension_len]
* - uint16 server_name_list_len (# of bytes here)
* - opaque server_names[server_name_list_len bytes]
* - uint8 name_type = 0 for host_name
* - uint16 name_len
* - opaque hostname[name_len bytes]
*/
static int
smp_fetch_ssl_hello_sni(struct proxy *px, struct session *s, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
int hs_len, ext_len, bleft;
struct channel *chn;
unsigned char *data;
if (!s)
goto not_ssl_hello;
chn = ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? s->rep : s->req;
bleft = chn->buf->i;
data = (unsigned char *)chn->buf->p;
/* Check for SSL/TLS Handshake */
if (!bleft)
goto too_short;
if (*data != 0x16)
goto not_ssl_hello;
/* Check for TLSv1 or later (SSL version >= 3.1) */
if (bleft < 3)
goto too_short;
if (data[1] < 0x03 || data[2] < 0x01)
goto not_ssl_hello;
if (bleft < 5)
goto too_short;
hs_len = (data[3] << 8) + data[4];
if (hs_len < 1 + 3 + 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
goto not_ssl_hello; /* too short to have an extension */
data += 5; /* enter TLS handshake */
bleft -= 5;
/* Check for a complete client hello starting at <data> */
if (bleft < 1)
goto too_short;
if (data[0] != 0x01) /* msg_type = Client Hello */
goto not_ssl_hello;
/* Check the Hello's length */
if (bleft < 4)
goto too_short;
hs_len = (data[1] << 16) + (data[2] << 8) + data[3];
if (hs_len < 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
goto not_ssl_hello; /* too short to have an extension */
/* We want the full handshake here */
if (bleft < hs_len)
goto too_short;
data += 4;
/* Start of the ClientHello message */
if (data[0] < 0x03 || data[1] < 0x01) /* TLSv1 minimum */
goto not_ssl_hello;
ext_len = data[34]; /* session_id_len */
if (ext_len > 32 || ext_len > (hs_len - 35)) /* check for correct session_id len */
goto not_ssl_hello;
/* Jump to cipher suite */
hs_len -= 35 + ext_len;
data += 35 + ext_len;
if (hs_len < 4 || /* minimum one cipher */
(ext_len = (data[0] << 8) + data[1]) < 2 || /* minimum 2 bytes for a cipher */
ext_len > hs_len)
goto not_ssl_hello;
/* Jump to the compression methods */
hs_len -= 2 + ext_len;
data += 2 + ext_len;
if (hs_len < 2 || /* minimum one compression method */
data[0] < 1 || data[0] > hs_len) /* minimum 1 bytes for a method */
goto not_ssl_hello;
/* Jump to the extensions */
hs_len -= 1 + data[0];
data += 1 + data[0];
if (hs_len < 2 || /* minimum one extension list length */
(ext_len = (data[0] << 8) + data[1]) > hs_len - 2) /* list too long */
goto not_ssl_hello;
hs_len = ext_len; /* limit ourselves to the extension length */
data += 2;
while (hs_len >= 4) {
int ext_type, name_type, srv_len, name_len;
ext_type = (data[0] << 8) + data[1];
ext_len = (data[2] << 8) + data[3];
if (ext_len > hs_len - 4) /* Extension too long */
goto not_ssl_hello;
if (ext_type == 0) { /* Server name */
if (ext_len < 2) /* need one list length */
goto not_ssl_hello;
srv_len = (data[4] << 8) + data[5];
if (srv_len < 4 || srv_len > hs_len - 6)
goto not_ssl_hello; /* at least 4 bytes per server name */
name_type = data[6];
name_len = (data[7] << 8) + data[8];
if (name_type == 0) { /* hostname */
smp->type = SMP_T_CSTR;
smp->data.str.str = (char *)data + 9;
smp->data.str.len = name_len;
smp->flags = SMP_F_VOLATILE;
return 1;
}
}
hs_len -= 4 + ext_len;
data += 4 + ext_len;
}
/* server name not found */
goto not_ssl_hello;
too_short:
smp->flags = SMP_F_MAY_CHANGE;
not_ssl_hello:
return 0;
}
/* Fetch the request RDP cookie identified in the args, or any cookie if no arg
* is passed. It is usable both for ACL and for samples. Note: this decoder
* only works with non-wrapping data. Accepts either 0 or 1 argument. Argument
* is a string (cookie name), other types will lead to undefined behaviour.
*/
int
smp_fetch_rdp_cookie(struct proxy *px, struct session *s, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
int bleft;
const unsigned char *data;
if (!s || !s->req)
return 0;
smp->flags = 0;
smp->type = SMP_T_CSTR;
bleft = s->req->buf->i;
if (bleft <= 11)
goto too_short;
data = (const unsigned char *)s->req->buf->p + 11;
bleft -= 11;
if (bleft <= 7)
goto too_short;
if (strncasecmp((const char *)data, "Cookie:", 7) != 0)
goto not_cookie;
data += 7;
bleft -= 7;
while (bleft > 0 && *data == ' ') {
data++;
bleft--;
}
if (args) {
if (bleft <= args->data.str.len)
goto too_short;
if ((data[args->data.str.len] != '=') ||
strncasecmp(args->data.str.str, (const char *)data, args->data.str.len) != 0)
goto not_cookie;
data += args->data.str.len + 1;
bleft -= args->data.str.len + 1;
} else {
while (bleft > 0 && *data != '=') {
if (*data == '\r' || *data == '\n')
goto not_cookie;
data++;
bleft--;
}
if (bleft < 1)
goto too_short;
if (*data != '=')
goto not_cookie;
data++;
bleft--;
}
/* data points to cookie value */
smp->data.str.str = (char *)data;
smp->data.str.len = 0;
while (bleft > 0 && *data != '\r') {
data++;
bleft--;
}
if (bleft < 2)
goto too_short;
if (data[0] != '\r' || data[1] != '\n')
goto not_cookie;
smp->data.str.len = (char *)data - smp->data.str.str;
smp->flags = SMP_F_VOLATILE;
return 1;
too_short:
smp->flags = SMP_F_MAY_CHANGE;
not_cookie:
return 0;
}
/* returns either 1 or 0 depending on whether an RDP cookie is found or not */
static int
smp_fetch_rdp_cookie_cnt(struct proxy *px, struct session *s, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
int ret;
ret = smp_fetch_rdp_cookie(px, s, l7, opt, args, smp);
if (smp->flags & SMP_F_MAY_CHANGE)
return 0;
smp->flags = SMP_F_VOLATILE;
smp->type = SMP_T_UINT;
smp->data.uint = ret;
return 1;
}
/* extracts part of a payload with offset and length at a given position */
static int
smp_fetch_payload_lv(struct proxy *px, struct session *s, void *l7, unsigned int opt,
const struct arg *arg_p, struct sample *smp)
{
unsigned int len_offset = arg_p[0].data.uint;
unsigned int len_size = arg_p[1].data.uint;
unsigned int buf_offset;
unsigned int buf_size = 0;
struct channel *chn;
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 (!s)
return 0;
chn = ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? s->rep : s->req;
if (!chn)
return 0;
if (len_offset + len_size > chn->buf->i)
goto too_short;
for (i = 0; i < len_size; i++) {
buf_size = (buf_size << 8) + ((unsigned char *)chn->buf->p)[i + len_offset];
}
/* buf offset may be implicit, absolute or relative */
buf_offset = len_offset + len_size;
if (arg_p[2].type == ARGT_UINT)
buf_offset = arg_p[2].data.uint;
else if (arg_p[2].type == ARGT_SINT)
buf_offset += arg_p[2].data.sint;
if (!buf_size || buf_size > chn->buf->size || buf_offset + buf_size > chn->buf->size) {
/* will never match */
smp->flags = 0;
return 0;
}
if (buf_offset + buf_size > chn->buf->i)
goto too_short;
/* init chunk as read only */
smp->type = SMP_T_CBIN;
chunk_initlen(&smp->data.str, chn->buf->p + buf_offset, 0, buf_size);
smp->flags = SMP_F_VOLATILE;
return 1;
too_short:
smp->flags = SMP_F_MAY_CHANGE;
return 0;
}
/* extracts some payload at a fixed position and length */
static int
smp_fetch_payload(struct proxy *px, struct session *s, void *l7, unsigned int opt,
const struct arg *arg_p, struct sample *smp)
{
unsigned int buf_offset = arg_p[0].data.uint;
unsigned int buf_size = arg_p[1].data.uint;
struct channel *chn;
if (!s)
return 0;
chn = ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? s->rep : s->req;
if (!chn)
return 0;
if (!buf_size || buf_size > chn->buf->size || buf_offset + buf_size > chn->buf->size) {
/* will never match */
smp->flags = 0;
return 0;
}
if (buf_offset + buf_size > chn->buf->i)
goto too_short;
/* init chunk as read only */
smp->type = SMP_T_CBIN;
chunk_initlen(&smp->data.str, chn->buf->p + buf_offset, 0, buf_size);
smp->flags = SMP_F_VOLATILE;
return 1;
too_short:
smp->flags = SMP_F_MAY_CHANGE;
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
* 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
* the opposite of the two others if negative. 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_lv(struct arg *arg, char **err_msg)
{
if (!arg[1].data.uint) {
memprintf(err_msg, "payload length must be > 0");
return 0;
}
if (arg[2].type == ARGT_SINT &&
(int)(arg[0].data.uint + arg[1].data.uint + arg[2].data.sint) < 0) {
memprintf(err_msg, "payload offset too negative");
return 0;
}
return 1;
}
/************************************************************************/
/* All supported sample and ACL keywords must be declared here. */
/************************************************************************/
/* Note: must not be declared <const> as its list will be overwritten.
* Note: fetches that may return multiple types must be declared as the lowest
* common denominator, the type that can be casted into all other ones. For
* instance IPv4/IPv6 must be declared IPv4.
*/
static struct sample_fetch_kw_list smp_kws = {{ },{
{ "payload", smp_fetch_payload, ARG2(2,UINT,UINT), val_payload, 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_cnt", smp_fetch_rdp_cookie_cnt, ARG1(0,STR), NULL, SMP_T_UINT, SMP_USE_L6REQ },
{ "rep_ssl_hello_type", smp_fetch_ssl_hello_type, 0, NULL, SMP_T_UINT, SMP_USE_L6RES },
{ "req_len", smp_fetch_req_len, 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_ver", smp_fetch_req_ssl_ver, 0, NULL, SMP_T_UINT, SMP_USE_L6REQ },
{ "wait_end", smp_fetch_wait_end, 0, NULL, SMP_T_BOOL, SMP_USE_INTRN },
{ /* END */ },
}};
/* Note: must not be declared <const> as its list will be overwritten.
* Please take care of keeping this list alphabetically sorted.
*/
static struct acl_kw_list acl_kws = {{ },{
{ "payload", acl_parse_str, smp_fetch_payload, acl_match_str, ACL_USE_L6REQ_VOLATILE, ARG2(2,UINT,UINT), val_payload },
{ "payload_lv", acl_parse_str, smp_fetch_payload_lv, acl_match_str, ACL_USE_L6REQ_VOLATILE, ARG3(2,UINT,UINT,SINT), val_payload_lv },
{ "rep_ssl_hello_type", acl_parse_int, smp_fetch_ssl_hello_type, acl_match_int, ACL_USE_L6RTR_VOLATILE, 0 },
{ "req_len", acl_parse_int, smp_fetch_req_len, acl_match_int, ACL_USE_L6REQ_VOLATILE, 0 },
{ "req_rdp_cookie", acl_parse_str, smp_fetch_rdp_cookie, acl_match_str, ACL_USE_L6REQ_VOLATILE, ARG1(0,STR) },
{ "req_rdp_cookie_cnt", acl_parse_int, smp_fetch_rdp_cookie_cnt, acl_match_int, ACL_USE_L6REQ_VOLATILE, ARG1(0,STR) },
{ "req_ssl_hello_type", acl_parse_int, smp_fetch_ssl_hello_type, acl_match_int, ACL_USE_L6REQ_VOLATILE, 0 },
{ "req_ssl_sni", acl_parse_str, smp_fetch_ssl_hello_sni, acl_match_str, ACL_USE_L6REQ_VOLATILE, 0 },
{ "req_ssl_ver", acl_parse_dotted_ver, smp_fetch_req_ssl_ver, acl_match_int, ACL_USE_L6REQ_VOLATILE, 0 },
{ "wait_end", acl_parse_nothing, smp_fetch_wait_end, acl_match_nothing, ACL_USE_NOTHING, 0 },
{ /* END */ },
}};
__attribute__((constructor))
static void __payload_init(void)
{
sample_register_fetches(&smp_kws);
acl_register_keywords(&acl_kws);
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -1,7 +1,7 @@
/*
* AF_INET/AF_INET6 SOCK_STREAM protocol layer (tcp)
*
* Copyright 2000-2010 Willy Tarreau <w@1wt.eu>
* Copyright 2000-2013 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -1403,125 +1403,9 @@ static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
/************************************************************************/
/* All supported sample fetch functios must be declared here */
/* All supported sample fetch functions must be declared here */
/************************************************************************/
/* Fetch the request RDP cookie identified in the args, or any cookie if no arg
* is passed. It is usable both for ACL and for samples. Note: this decoder
* only works with non-wrapping data. Accepts either 0 or 1 argument. Argument
* is a string (cookie name), other types will lead to undefined behaviour.
*/
int
smp_fetch_rdp_cookie(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
int bleft;
const unsigned char *data;
if (!l4 || !l4->req)
return 0;
smp->flags = 0;
smp->type = SMP_T_CSTR;
bleft = l4->req->buf->i;
if (bleft <= 11)
goto too_short;
data = (const unsigned char *)l4->req->buf->p + 11;
bleft -= 11;
if (bleft <= 7)
goto too_short;
if (strncasecmp((const char *)data, "Cookie:", 7) != 0)
goto not_cookie;
data += 7;
bleft -= 7;
while (bleft > 0 && *data == ' ') {
data++;
bleft--;
}
if (args) {
if (bleft <= args->data.str.len)
goto too_short;
if ((data[args->data.str.len] != '=') ||
strncasecmp(args->data.str.str, (const char *)data, args->data.str.len) != 0)
goto not_cookie;
data += args->data.str.len + 1;
bleft -= args->data.str.len + 1;
} else {
while (bleft > 0 && *data != '=') {
if (*data == '\r' || *data == '\n')
goto not_cookie;
data++;
bleft--;
}
if (bleft < 1)
goto too_short;
if (*data != '=')
goto not_cookie;
data++;
bleft--;
}
/* data points to cookie value */
smp->data.str.str = (char *)data;
smp->data.str.len = 0;
while (bleft > 0 && *data != '\r') {
data++;
bleft--;
}
if (bleft < 2)
goto too_short;
if (data[0] != '\r' || data[1] != '\n')
goto not_cookie;
smp->data.str.len = (char *)data - smp->data.str.str;
smp->flags = SMP_F_VOLATILE;
return 1;
too_short:
smp->flags = SMP_F_MAY_CHANGE;
not_cookie:
return 0;
}
/************************************************************************/
/* All supported ACL keywords must be declared here. */
/************************************************************************/
/* returns either 1 or 0 depending on whether an RDP cookie is found or not */
static int
acl_fetch_rdp_cookie_cnt(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
int ret;
ret = smp_fetch_rdp_cookie(px, l4, l7, opt, args, smp);
if (smp->flags & SMP_F_MAY_CHANGE)
return 0;
smp->flags = SMP_F_VOLATILE;
smp->type = SMP_T_UINT;
smp->data.uint = ret;
return 1;
}
/* fetch the connection's source IPv4/IPv6 address */
static int
smp_fetch_src(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
@ -1596,140 +1480,6 @@ smp_fetch_dport(struct proxy *px, struct session *l4, void *l7, unsigned int opt
return 1;
}
static int
smp_fetch_payload_lv(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *arg_p, struct sample *smp)
{
unsigned int len_offset = arg_p[0].data.uint;
unsigned int len_size = arg_p[1].data.uint;
unsigned int buf_offset;
unsigned int buf_size = 0;
struct channel *chn;
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 (!l4)
return 0;
chn = ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? l4->rep : l4->req;
if (!chn)
return 0;
if (len_offset + len_size > chn->buf->i)
goto too_short;
for (i = 0; i < len_size; i++) {
buf_size = (buf_size << 8) + ((unsigned char *)chn->buf->p)[i + len_offset];
}
/* buf offset may be implicit, absolute or relative */
buf_offset = len_offset + len_size;
if (arg_p[2].type == ARGT_UINT)
buf_offset = arg_p[2].data.uint;
else if (arg_p[2].type == ARGT_SINT)
buf_offset += arg_p[2].data.sint;
if (!buf_size || buf_size > chn->buf->size || buf_offset + buf_size > chn->buf->size) {
/* will never match */
smp->flags = 0;
return 0;
}
if (buf_offset + buf_size > chn->buf->i)
goto too_short;
/* init chunk as read only */
smp->type = SMP_T_CBIN;
chunk_initlen(&smp->data.str, chn->buf->p + buf_offset, 0, buf_size);
smp->flags = SMP_F_VOLATILE;
return 1;
too_short:
smp->flags = SMP_F_MAY_CHANGE;
return 0;
}
static int
smp_fetch_payload(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *arg_p, struct sample *smp)
{
unsigned int buf_offset = arg_p[0].data.uint;
unsigned int buf_size = arg_p[1].data.uint;
struct channel *chn;
if (!l4)
return 0;
chn = ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? l4->rep : l4->req;
if (!chn)
return 0;
if (!buf_size || buf_size > chn->buf->size || buf_offset + buf_size > chn->buf->size) {
/* will never match */
smp->flags = 0;
return 0;
}
if (buf_offset + buf_size > chn->buf->i)
goto too_short;
/* init chunk as read only */
smp->type = SMP_T_CBIN;
chunk_initlen(&smp->data.str, chn->buf->p + buf_offset, 0, buf_size);
smp->flags = SMP_F_VOLATILE;
return 1;
too_short:
smp->flags = SMP_F_MAY_CHANGE;
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
* 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
* the opposite of the two others if negative. 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_lv(struct arg *arg, char **err_msg)
{
if (!arg[1].data.uint) {
memprintf(err_msg, "payload length must be > 0");
return 0;
}
if (arg[2].type == ARGT_SINT &&
(int)(arg[0].data.uint + arg[1].data.uint + arg[2].data.sint) < 0) {
memprintf(err_msg, "payload offset too negative");
return 0;
}
return 1;
}
#ifdef IPV6_V6ONLY
/* parse the "v4v6" bind keyword */
static int bind_parse_v4v6(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
@ -1852,40 +1602,35 @@ static int bind_parse_interface(char **args, int cur_arg, struct proxy *px, stru
#endif
static struct cfg_kw_list cfg_kws = {{ },{
{ CFG_LISTEN, "tcp-request", tcp_parse_tcp_req },
{ CFG_LISTEN, "tcp-request", tcp_parse_tcp_req },
{ CFG_LISTEN, "tcp-response", tcp_parse_tcp_rep },
{ 0, NULL, NULL },
}};
/* Note: must not be declared <const> as its list will be overwritten.
* Please take care of keeping this list alphabetically sorted.
*/
static struct acl_kw_list acl_kws = {{ },{
{ "dst", acl_parse_ip, smp_fetch_dst, acl_match_ip, ACL_USE_TCP4_PERMANENT, 0 },
{ "dst_port", acl_parse_int, smp_fetch_dport, acl_match_int, ACL_USE_TCP_PERMANENT, 0 },
{ "payload", acl_parse_str, smp_fetch_payload, acl_match_str, ACL_USE_L6REQ_VOLATILE, ARG2(2,UINT,UINT), val_payload },
{ "payload_lv", acl_parse_str, smp_fetch_payload_lv, acl_match_str, ACL_USE_L6REQ_VOLATILE, ARG3(2,UINT,UINT,SINT), val_payload_lv },
{ "req_rdp_cookie", acl_parse_str, smp_fetch_rdp_cookie, acl_match_str, ACL_USE_L6REQ_VOLATILE, ARG1(0,STR) },
{ "req_rdp_cookie_cnt", acl_parse_int, acl_fetch_rdp_cookie_cnt, acl_match_int, ACL_USE_L6REQ_VOLATILE, ARG1(0,STR) },
{ "src", acl_parse_ip, smp_fetch_src, acl_match_ip, ACL_USE_TCP4_PERMANENT, 0 },
{ "src_port", acl_parse_int, smp_fetch_sport, acl_match_int, ACL_USE_TCP_PERMANENT, 0 },
{ NULL, NULL, NULL, NULL },
{ "dst", acl_parse_ip, smp_fetch_dst, acl_match_ip, ACL_USE_TCP4_PERMANENT, 0 },
{ "dst_port", acl_parse_int, smp_fetch_dport, acl_match_int, ACL_USE_TCP_PERMANENT, 0 },
{ "src", acl_parse_ip, smp_fetch_src, acl_match_ip, ACL_USE_TCP4_PERMANENT, 0 },
{ "src_port", acl_parse_int, smp_fetch_sport, acl_match_int, ACL_USE_TCP_PERMANENT, 0 },
{ /* END */ },
}};
/* Note: must not be declared <const> as its list will be overwritten.
* Note: fetches that may return multiple types must be declared as the lowest
* common denominator, the type that can be casted into all other ones. For
* instance v4/v6 must be declared v4.
*/
static struct sample_fetch_kw_list sample_fetch_keywords = {{ },{
{ "dst", smp_fetch_dst, 0, NULL, SMP_T_IPV4, SMP_USE_L4CLI },
{ "dst_port", smp_fetch_dport, 0, NULL, SMP_T_UINT, SMP_USE_L4CLI },
{ "payload", smp_fetch_payload, ARG2(2,UINT,UINT), val_payload, 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(1,STR), NULL, SMP_T_CSTR, SMP_USE_L6REQ },
{ "src", smp_fetch_src, 0, NULL, SMP_T_IPV4, SMP_USE_L4CLI },
{ "src_port", smp_fetch_sport, 0, NULL, SMP_T_UINT, SMP_USE_L4CLI },
{ NULL, NULL, 0, 0, 0 },
{ "dst", smp_fetch_dst, 0, NULL, SMP_T_IPV4, SMP_USE_L4CLI },
{ "dst_port", smp_fetch_dport, 0, NULL, SMP_T_UINT, SMP_USE_L4CLI },
{ "src", smp_fetch_src, 0, NULL, SMP_T_IPV4, SMP_USE_L4CLI },
{ "src_port", smp_fetch_sport, 0, NULL, SMP_T_UINT, SMP_USE_L4CLI },
{ /* END */ },
}};
/************************************************************************/