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.
diff --git a/src/acl.c b/src/acl.c
index 86094c5..fe5c869 100644
--- a/src/acl.c
+++ b/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
@@ -60,400 +60,6 @@
/*
- * 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 @@
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 @@
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);
}
diff --git a/src/backend.c b/src/backend.c
index e550d35..dcec6b2 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -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>
diff --git a/src/payload.c b/src/payload.c
new file mode 100644
index 0000000..21d72a7
--- /dev/null
+++ b/src/payload.c
@@ -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:
+ */
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 7ad825e..3d2efcd 100644
--- a/src/proto_tcp.c
+++ b/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 @@
/************************************************************************/
-/* 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,
@@ -1594,142 +1478,8 @@
smp->flags = 0;
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 @@
#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 */ },
}};
/************************************************************************/