mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-05-05 09:18:10 +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/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/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/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_OBJS = $(EBTREE_DIR)/ebtree.o \
|
||||||
$(EBTREE_DIR)/eb32tree.o $(EBTREE_DIR)/eb64tree.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/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/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/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
|
src/auth.o src/stick_table.o src/sample.o src/compression.o
|
||||||
|
|
||||||
EBTREE_OBJS = $(EBTREE_DIR)/ebtree.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/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/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/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/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
|
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>.
|
or superior) hello message and handshake type is equal to <integer>.
|
||||||
This test was designed to be used with TCP response content inspection: a
|
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
|
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
|
found in the response buffer and not to contents deciphered via an SSL data
|
||||||
layer, so this will not work with "bind" lines having the "ssl" option.
|
layer, so this will not work with "server" lines having the "ssl" option.
|
||||||
|
|
||||||
req_len <integer>
|
req_len <integer>
|
||||||
Returns true when the length of the data in the request buffer matches the
|
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 :
|
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
|
base This returns the concatenation of the first Host header and the
|
||||||
path part of the request, which starts at the first slash and
|
path part of the request, which starts at the first slash and
|
||||||
ends before the question mark. It can be useful in virtual
|
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.
|
size of 8 or 20 bytes depending on the source address family.
|
||||||
This can be used to track per-IP, per-URL counters.
|
This can be used to track per-IP, per-URL counters.
|
||||||
|
|
||||||
src This is the source IPv4 address of the client of the session.
|
cookie(<name>)
|
||||||
It is of type IPv4 and works on both IPv4 and IPv6 tables.
|
This extracts the last occurrence of the cookie name <name> on a
|
||||||
On IPv6 tables, IPv4 address is mapped to its IPv6 equivalent,
|
"Cookie" header line from the request, or a "Set-Cookie" header
|
||||||
according to RFC 4291.
|
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
|
dst This is the destination IPv4 address of the session on the
|
||||||
client side, which is the address the client connected to.
|
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.
|
<lengthoffset> + <lengthsize> else it is absolute.
|
||||||
Ex: see SSL session id example in "stick table" chapter.
|
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,
|
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
|
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.
|
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
|
# match http://example.com/foo;JSESSIONID=some_id
|
||||||
stick on url_param(JSESSIONID,;)
|
stick on url_param(JSESSIONID,;)
|
||||||
|
|
||||||
rdp_cookie(<name>)
|
wait_end Always returns true or does not fetch. This is only used for ACL
|
||||||
This extracts the value of the rdp cookie <name> as a string
|
compatibility.
|
||||||
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"
|
|
||||||
|
|
||||||
|
|
||||||
The currently available list of transformations include :
|
The currently available list of transformations include :
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* include/proto/acl.h
|
* include/proto/acl.h
|
||||||
* This file provides interface definitions for ACL manipulation.
|
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* 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);
|
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 */
|
/* always return false */
|
||||||
int acl_match_nothing(struct sample *smp, struct acl_pattern *pattern);
|
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
|
* include/proto/proto_tcp.h
|
||||||
* This file contains TCP socket protocol definitions.
|
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* 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_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_inspect_response(struct session *s, struct channel *rep, int an_bit);
|
||||||
int tcp_exec_req_rules(struct session *s);
|
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
|
/* 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
|
* 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.
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* 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.
|
* 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;
|
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 */
|
/* always return false */
|
||||||
int acl_match_nothing(struct sample *smp, struct acl_pattern *pattern)
|
int acl_match_nothing(struct sample *smp, struct acl_pattern *pattern)
|
||||||
{
|
{
|
||||||
@ -2302,29 +1901,62 @@ acl_find_targets(struct proxy *p)
|
|||||||
return cfgerr;
|
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.
|
/* Note: must not be declared <const> as its list will be overwritten.
|
||||||
* Please take care of keeping this list alphabetically sorted.
|
* Please take care of keeping this list alphabetically sorted.
|
||||||
*/
|
*/
|
||||||
static struct acl_kw_list acl_kws = {{ },{
|
static struct acl_kw_list acl_kws = {{ },{
|
||||||
{ "always_false", acl_parse_nothing, acl_fetch_false, acl_match_nothing, ACL_USE_NOTHING, 0 },
|
{ "always_false", acl_parse_nothing, smp_fetch_false, acl_match_nothing, ACL_USE_NOTHING, 0 },
|
||||||
{ "always_true", acl_parse_nothing, acl_fetch_true, acl_match_nothing, ACL_USE_NOTHING, 0 },
|
{ "always_true", acl_parse_nothing, smp_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 },
|
{ /* END */ },
|
||||||
{ "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 }
|
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
|
||||||
__attribute__((constructor))
|
__attribute__((constructor))
|
||||||
static void __acl_init(void)
|
static void __acl_init(void)
|
||||||
{
|
{
|
||||||
|
sample_register_fetches(&smp_kws);
|
||||||
acl_register_keywords(&acl_kws);
|
acl_register_keywords(&acl_kws);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,10 +39,12 @@
|
|||||||
#include <proto/lb_fwrr.h>
|
#include <proto/lb_fwrr.h>
|
||||||
#include <proto/lb_map.h>
|
#include <proto/lb_map.h>
|
||||||
#include <proto/obj_type.h>
|
#include <proto/obj_type.h>
|
||||||
|
#include <proto/payload.h>
|
||||||
#include <proto/protocol.h>
|
#include <proto/protocol.h>
|
||||||
#include <proto/proto_http.h>
|
#include <proto/proto_http.h>
|
||||||
#include <proto/proto_tcp.h>
|
#include <proto/proto_tcp.h>
|
||||||
#include <proto/queue.h>
|
#include <proto/queue.h>
|
||||||
|
#include <proto/sample.h>
|
||||||
#include <proto/server.h>
|
#include <proto/server.h>
|
||||||
#include <proto/session.h>
|
#include <proto/session.h>
|
||||||
#include <proto/raw_sock.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)
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* 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 */
|
/* fetch the connection's source IPv4/IPv6 address */
|
||||||
static int
|
static int
|
||||||
smp_fetch_src(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
|
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;
|
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
|
#ifdef IPV6_V6ONLY
|
||||||
/* parse the "v4v6" bind keyword */
|
/* parse the "v4v6" bind keyword */
|
||||||
static int bind_parse_v4v6(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
|
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
|
#endif
|
||||||
|
|
||||||
static struct cfg_kw_list cfg_kws = {{ },{
|
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 },
|
{ CFG_LISTEN, "tcp-response", tcp_parse_tcp_rep },
|
||||||
{ 0, NULL, NULL },
|
{ 0, NULL, NULL },
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
|
||||||
/* Note: must not be declared <const> as its list will be overwritten.
|
/* Note: must not be declared <const> as its list will be overwritten.
|
||||||
* Please take care of keeping this list alphabetically sorted.
|
* Please take care of keeping this list alphabetically sorted.
|
||||||
*/
|
*/
|
||||||
static struct acl_kw_list acl_kws = {{ },{
|
static struct acl_kw_list acl_kws = {{ },{
|
||||||
{ "dst", acl_parse_ip, smp_fetch_dst, acl_match_ip, ACL_USE_TCP4_PERMANENT, 0 },
|
{ "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 },
|
{ "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 },
|
{ "src", acl_parse_ip, smp_fetch_src, acl_match_ip, ACL_USE_TCP4_PERMANENT, 0 },
|
||||||
{ "payload_lv", acl_parse_str, smp_fetch_payload_lv, acl_match_str, ACL_USE_L6REQ_VOLATILE, ARG3(2,UINT,UINT,SINT), val_payload_lv },
|
{ "src_port", acl_parse_int, smp_fetch_sport, acl_match_int, ACL_USE_TCP_PERMANENT, 0 },
|
||||||
{ "req_rdp_cookie", acl_parse_str, smp_fetch_rdp_cookie, acl_match_str, ACL_USE_L6REQ_VOLATILE, ARG1(0,STR) },
|
{ /* END */ },
|
||||||
{ "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 },
|
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
|
||||||
/* Note: must not be declared <const> as its list will be overwritten.
|
/* 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
|
* 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
|
* common denominator, the type that can be casted into all other ones. For
|
||||||
* instance v4/v6 must be declared v4.
|
* instance v4/v6 must be declared v4.
|
||||||
*/
|
*/
|
||||||
static struct sample_fetch_kw_list sample_fetch_keywords = {{ },{
|
static struct sample_fetch_kw_list sample_fetch_keywords = {{ },{
|
||||||
{ "dst", smp_fetch_dst, 0, NULL, SMP_T_IPV4, SMP_USE_L4CLI },
|
{ "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 },
|
{ "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 },
|
{ "src", smp_fetch_src, 0, NULL, SMP_T_IPV4, SMP_USE_L4CLI },
|
||||||
{ "payload_lv", smp_fetch_payload_lv, ARG3(2,UINT,UINT,SINT), val_payload_lv, SMP_T_CBIN, SMP_USE_L6REQ|SMP_USE_L6RES },
|
{ "src_port", smp_fetch_sport, 0, NULL, SMP_T_UINT, SMP_USE_L4CLI },
|
||||||
{ "rdp_cookie", smp_fetch_rdp_cookie, ARG1(1,STR), NULL, SMP_T_CSTR, SMP_USE_L6REQ },
|
{ /* END */ },
|
||||||
{ "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 },
|
|
||||||
}};
|
}};
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
Loading…
Reference in New Issue
Block a user