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/Makefile b/Makefile
index dd11aa0..f2ac373 100644
--- a/Makefile
+++ b/Makefile
@@ -634,7 +634,7 @@
        src/stream_interface.o src/dumpstats.o src/proto_tcp.o \
        src/session.o src/hdr_idx.o src/ev_select.o src/signal.o \
        src/acl.o src/sample.o src/memory.o src/freq_ctr.o src/auth.o \
-       src/compression.o
+       src/compression.o src/payload.o
 
 EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o \
               $(EBTREE_DIR)/eb32tree.o $(EBTREE_DIR)/eb64tree.o \
diff --git a/Makefile.bsd b/Makefile.bsd
index d347dfb..ac8bfd6 100644
--- a/Makefile.bsd
+++ b/Makefile.bsd
@@ -115,7 +115,7 @@
        src/session.o src/hdr_idx.o src/ev_select.o src/signal.o \
        src/lb_chash.o src/lb_fwlc.o src/lb_fwrr.o src/lb_map.o src/lb_fas.o \
        src/ev_poll.o src/ev_kqueue.o src/connection.o \
-       src/arg.o src/acl.o src/memory.o src/freq_ctr.o \
+       src/arg.o src/acl.o src/memory.o src/freq_ctr.o src/payload.o \
        src/auth.o src/stick_table.o src/sample.o src/compression.o
 
 EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o \
diff --git a/Makefile.osx b/Makefile.osx
index 5c30e0c..0348b86 100644
--- a/Makefile.osx
+++ b/Makefile.osx
@@ -111,7 +111,7 @@
        src/peers.o src/stream_interface.o src/dumpstats.o src/proto_tcp.o \
        src/session.o src/hdr_idx.o src/ev_select.o src/signal.o \
        src/lb_chash.o src/lb_fwlc.o src/lb_fwrr.o src/lb_map.o src/lb_fas.o \
-       src/ev_poll.o src/connection.o \
+       src/ev_poll.o src/connection.o src/payload.o \
        src/arg.o src/acl.o src/memory.o src/freq_ctr.o \
        src/auth.o src/stick_table.o src/sample.o src/compression.o
 
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 30f547d..2f6b869 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -8723,8 +8723,8 @@
   or superior) hello message and handshake type is equal to <integer>.
   This test was designed to be used with TCP response content inspection: a
   SSL session ID may be fetched. Note that this only applies to raw contents
-  found in the request buffer and not to contents deciphered via an SSL data
-  layer, so this will not work with "bind" lines having the "ssl" option.
+  found in the response buffer and not to contents deciphered via an SSL data
+  layer, so this will not work with "server" lines having the "ssl" option.
 
 req_len <integer>
   Returns true when the length of the data in the request buffer matches the
@@ -9542,6 +9542,12 @@
 
 The list of currently supported pattern fetch functions is the following :
 
+  always_false
+               Always returns the boolean "false" value.
+
+  always_true
+               Always returns the boolean "true" value.
+
   base         This returns the concatenation of the first Host header and the
                path part of the request, which starts at the first slash and
                ends before the question mark. It can be useful in virtual
@@ -9561,10 +9567,16 @@
                size of 8 or 20 bytes depending on the source address family.
                This can be used to track per-IP, per-URL counters.
 
-  src          This is the source IPv4 address of the client of the session.
-               It is of type IPv4 and works on both IPv4 and IPv6 tables.
-               On IPv6 tables, IPv4 address is mapped to its IPv6 equivalent,
-               according to RFC 4291.
+  cookie(<name>)
+               This extracts the last occurrence of the cookie name <name> on a
+               "Cookie" header line from the request, or a "Set-Cookie" header
+               from the response, and uses the corresponding value to match. A
+               typical use is to get multiple clients sharing a same profile
+               use the same server. This can be similar to what "appsession"
+               does with the "request-learn" statement, but with support for
+               multi-peer synchronization and state keeping across restarts.
+
+               See also : "appsession"
 
   dst          This is the destination IPv4 address of the session on the
                client side, which is the address the client connected to.
@@ -9610,6 +9622,97 @@
                <lengthoffset> + <lengthsize> else it is absolute.
                Ex: see SSL session id  example in "stick table" chapter.
 
+  rdp_cookie(<name>)
+               This extracts the value of the rdp cookie <name> as a string
+               and uses this value to match. This enables implementation of
+               persistence based on the mstshash cookie. This is typically
+               done if there is no msts cookie present.
+
+               This differs from "balance rdp-cookie" in that any balancing
+               algorithm may be used and thus the distribution of clients
+               to backend servers is not linked to a hash of the RDP
+               cookie. It is envisaged that using a balancing algorithm
+               such as "balance roundrobin" or "balance leastconnect" will
+               lead to a more even distribution of clients to backend
+               servers than the hash used by "balance rdp-cookie".
+
+               Example :
+                listen tse-farm
+                    bind 0.0.0.0:3389
+                    # wait up to 5s for an RDP cookie in the request
+                    tcp-request inspect-delay 5s
+                    tcp-request content accept if RDP_COOKIE
+                    # apply RDP cookie persistence
+                    persist rdp-cookie
+                    # Persist based on the mstshash cookie
+                    # This is only useful makes sense if
+                    # balance rdp-cookie is not used
+                    stick-table type string size 204800
+                    stick on rdp_cookie(mstshash)
+                    server srv1 1.1.1.1:3389
+                    server srv1 1.1.1.2:3389
+
+               See also : "balance rdp-cookie", "persist rdp-cookie",
+               "tcp-request" and the "req_rdp_cookie" ACL.
+
+  rdp_cookie_cnt([name])
+               Tries to parse the request buffer as RDP protocol, then returns
+               an integer corresponding to the number of RDP cookies found. If
+               an optional cookie name is passed, only cookies matching this
+               name are considered. This is mostly used in ACL.
+
+  rep_ssl_hello_type
+               Returns an integer value containing the type of the SSL hello
+               message found in the response buffer. Note that this only
+               applies to raw contents found in the response buffer and not to
+               contents deciphered via an SSL data layer, so this will not work
+               with "server" lines having the "ssl" option. This is mostly used
+               in ACL.
+
+  req_len      Returns an integer value corresponding to the number of bytes
+               present in the request buffer. This is mostly used in ACL.
+
+  req_ssl_hello_type
+               Returns an integer value containing the type of the SSL hello
+               message found in the request buffer. Note that this only applies
+               to raw contents found in the request buffer and not to contents
+               deciphered via an SSL data layer, so this will not work with
+               "bind" lines having the "ssl" option. This is mostly used in
+               ACL.
+
+  req_ssl_sni  Returns a string containing the value of the Server Name TLS
+               extension sent by a client in a TLS stream passing through the
+               request buffer. Note that this only applies to raw contents
+               found in the request buffer and not to contents deciphered via
+               an SSL data layer, so this will not work with "bind" lines
+               having the "ssl" option. This is mostly used in ACL.
+
+  req_ssl_ver  Returns an integer value containing the version of the SSL/TLS
+               protocol of a stream present in the request buffer. The value is
+               composed of the major version multiplied by 65536, added to the
+               minor version. Note that this only applies to raw contents found
+               in the request buffer and not to contents deciphered via an SSL
+               data layer, so this will not work with "bind" lines having the
+               "ssl" option. This is mostly used in ACL.
+
+  set-cookie(<name>)
+               This fetch function is deprecated and has been superseded by the
+               "cookie" fetch which is capable of handling both requests and
+               responses. This keyword will disappear soon.
+
+               This extracts the last occurrence of the cookie name <name> on a
+               "Set-Cookie" header line from the response and uses the
+               corresponding value to match. This can be comparable to what
+               "appsession" does with default options, but with support for
+                multi-peer synchronization and state keeping across restarts.
+
+               See also : "appsession"
+
+  src          This is the source IPv4 address of the client of the session.
+               It is of type IPv4 and works on both IPv4 and IPv6 tables.
+               On IPv6 tables, IPv4 address is mapped to its IPv6 equivalent,
+               according to RFC 4291.
+
   src_port     This is the source TCP port of the session on the client side,
                which is the port the client connected from. It is very unlikely
                that this function will be useful but it's available at no cost.
@@ -9823,62 +9926,8 @@
                  # match http://example.com/foo;JSESSIONID=some_id
                  stick on url_param(JSESSIONID,;)
 
-  rdp_cookie(<name>)
-               This extracts the value of the rdp cookie <name> as a string
-               and uses this value to match. This enables implementation of
-               persistence based on the mstshash cookie. This is typically
-               done if there is no msts cookie present.
-
-               This differs from "balance rdp-cookie" in that any balancing
-               algorithm may be used and thus the distribution of clients
-               to backend servers is not linked to a hash of the RDP
-               cookie. It is envisaged that using a balancing algorithm
-               such as "balance roundrobin" or "balance leastconnect" will
-               lead to a more even distribution of clients to backend
-               servers than the hash used by "balance rdp-cookie".
-
-               Example :
-                listen tse-farm
-                    bind 0.0.0.0:3389
-                    # wait up to 5s for an RDP cookie in the request
-                    tcp-request inspect-delay 5s
-                    tcp-request content accept if RDP_COOKIE
-                    # apply RDP cookie persistence
-                    persist rdp-cookie
-                    # Persist based on the mstshash cookie
-                    # This is only useful makes sense if
-                    # balance rdp-cookie is not used
-                    stick-table type string size 204800
-                    stick on rdp_cookie(mstshash)
-                    server srv1 1.1.1.1:3389
-                    server srv1 1.1.1.2:3389
-
-               See also : "balance rdp-cookie", "persist rdp-cookie",
-               "tcp-request" and the "req_rdp_cookie" ACL.
-
-  cookie(<name>)
-               This extracts the last occurrence of the cookie name <name> on a
-               "Cookie" header line from the request, or a "Set-Cookie" header
-               from the response, and uses the corresponding value to match. A
-               typical use is to get multiple clients sharing a same profile
-               use the same server. This can be similar to what "appsession"
-               does with the "request-learn" statement, but with support for
-               multi-peer synchronization and state keeping across restarts.
-
-               See also : "appsession"
-
-  set-cookie(<name>)
-               This fetch function is deprecated and has been superseded by the
-               "cookie" fetch which is capable of handling both requests and
-               responses. This keyword will disappear soon.
-
-               This extracts the last occurrence of the cookie name <name> on a
-               "Set-Cookie" header line from the response and uses the
-               corresponding value to match. This can be comparable to what
-               "appsession" does with default options, but with support for
-                multi-peer synchronization and state keeping across restarts.
-
-               See also : "appsession"
+  wait_end     Always returns true or does not fetch. This is only used for ACL
+               compatibility.
 
 
 The currently available list of transformations include :
diff --git a/include/proto/acl.h b/include/proto/acl.h
index 38ad604..569136a 100644
--- a/include/proto/acl.h
+++ b/include/proto/acl.h
@@ -2,7 +2,7 @@
  * include/proto/acl.h
  * This file provides interface definitions for ACL manipulation.
  *
- * Copyright (C) 2000-2011 Willy Tarreau - w@1wt.eu
+ * Copyright (C) 2000-2013 Willy Tarreau - w@1wt.eu
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -177,10 +177,6 @@
  */
 int acl_parse_ip(const char **text, struct acl_pattern *pattern, int *opaque, char **err);
 
-/* always fake a data retrieval */
-int acl_fetch_nothing(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
-                      const struct arg *args, struct sample *smp);
-
 /* always return false */
 int acl_match_nothing(struct sample *smp, struct acl_pattern *pattern);
 
diff --git a/include/proto/payload.h b/include/proto/payload.h
new file mode 100644
index 0000000..338ebc6
--- /dev/null
+++ b/include/proto/payload.h
@@ -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:
+ */
diff --git a/include/proto/proto_tcp.h b/include/proto/proto_tcp.h
index d55f4c5..08500db 100644
--- a/include/proto/proto_tcp.h
+++ b/include/proto/proto_tcp.h
@@ -2,7 +2,7 @@
  * include/proto/proto_tcp.h
  * This file contains TCP socket protocol definitions.
  *
- * Copyright (C) 2000-2010 Willy Tarreau - w@1wt.eu
+ * Copyright (C) 2000-2013 Willy Tarreau - w@1wt.eu
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -37,7 +37,6 @@
 int tcp_inspect_request(struct session *s, struct channel *req, int an_bit);
 int tcp_inspect_response(struct session *s, struct channel *rep, int an_bit);
 int tcp_exec_req_rules(struct session *s);
-int smp_fetch_rdp_cookie(struct proxy *px, struct session *l4, void *l7, unsigned int opt, const struct arg *args, struct sample *smp);
 
 /* Converts the INET/INET6 source address to a stick_table key usable for table
  * lookups. Returns either NULL if the source cannot be converted (eg: not
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 */ },
 }};
 
 /************************************************************************/