mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-05-04 16:57:58 +00:00
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:
parent
434c57c95c
commit
d4c33c8889
2
Makefile
2
Makefile
@ -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 \
|
||||
|
@ -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 \
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 :
|
||||
|
@ -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
40
include/proto/payload.h
Normal 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:
|
||||
*/
|
@ -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
456
src/acl.c
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
698
src/payload.c
Normal 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:
|
||||
*/
|
285
src/proto_tcp.c
285
src/proto_tcp.c
@ -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 */ },
|
||||
}};
|
||||
|
||||
/************************************************************************/
|
||||
|
Loading…
Reference in New Issue
Block a user