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