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);
}