MEDIUM: acl/pattern: switch rdp_cookie functions stack up-down

Previously, both pattern, backend and persist_rdp_cookie would build fake
ACL expressions to fetch an RDP cookie by calling acl_fetch_rdp_cookie().

Now we switch roles. The RDP cookie fetch function is provided as a sample
fetch function that all others rely on, including ACL. The code is exactly
the same, only the args handling moved from expr->args to args. The code
was moved to proto_tcp.c, but probably that a dedicated file would be more
suited to content handling.
diff --git a/include/proto/acl.h b/include/proto/acl.h
index 998ac43..131d799 100644
--- a/include/proto/acl.h
+++ b/include/proto/acl.h
@@ -177,10 +177,6 @@
 /* always return false */
 int acl_match_nothing(struct sample *smp, struct acl_pattern *pattern);
 
-/* Fetch the RDP cookie identified in the expression. */
-int acl_fetch_rdp_cookie(struct proxy *px, struct session *l4, void *l7, int dir,
-                         struct acl_expr *expr, struct sample *smp);
-
 /* Checks that the pattern matches the end of the tested string. */
 int acl_match_end(struct sample *smp, struct acl_pattern *pattern);
 
diff --git a/include/proto/backend.h b/include/proto/backend.h
index c8322b8..b4dfc83 100644
--- a/include/proto/backend.h
+++ b/include/proto/backend.h
@@ -37,6 +37,7 @@
 const char *backend_lb_algo_str(int algo);
 int backend_parse_balance(const char **args, char *err,
 			  int errlen, struct proxy *curproxy);
+int tcp_persist_rdp_cookie(struct session *s, struct buffer *req, int an_bit);
 
 int be_downtime(struct proxy *px);
 void recount_servers(struct proxy *px);
diff --git a/include/proto/proto_tcp.h b/include/proto/proto_tcp.h
index 1665f15..91573e9 100644
--- a/include/proto/proto_tcp.h
+++ b/include/proto/proto_tcp.h
@@ -33,8 +33,8 @@
 int tcp_connect_server(struct stream_interface *si);
 int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit);
 int tcp_inspect_response(struct session *s, struct buffer *rep, int an_bit);
-int tcp_persist_rdp_cookie(struct session *s, struct buffer *req, int an_bit);
 int tcp_exec_req_rules(struct session *s);
+int smp_fetch_rdp_cookie(struct proxy *px, struct session *l4, void *l7, int dir, const struct arg *args, struct sample *smp);
 
 /* Converts the TCP 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 5d86a3e..36b5307 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -453,118 +453,6 @@
 	return 0;
 }
 
-/* Fetch the RDP cookie identified in the expression.
- * 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
-acl_fetch_rdp_cookie(struct proxy *px, struct session *l4, void *l7, int dir,
-                     struct acl_expr *expr, 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->i;
-	if (bleft <= 11)
-		goto too_short;
-
-	data = (const unsigned char *)l4->req->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 (expr->args) {
-
-		if (bleft <= expr->args->data.str.len)
-			goto too_short;
-
-		if ((data[expr->args->data.str.len] != '=') ||
-		    strncasecmp(expr->args->data.str.str, (const char *)data, expr->args->data.str.len) != 0)
-			goto not_cookie;
-
-		data += expr->args->data.str.len + 1;
-		bleft -= expr->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;
-}
-
-static int
-acl_fetch_rdp_cookie_cnt(struct proxy *px, struct session *l4, void *l7, int dir,
-                         struct acl_expr *expr, struct sample *smp)
-{
-	int ret;
-
-	ret = acl_fetch_rdp_cookie(px, l4, l7, dir, expr, 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;
-}
-
-
 /*
  * These functions are exported and may be used by any other component.
  */
@@ -2159,8 +2047,6 @@
 	{ "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_rdp_cookie",      acl_parse_str,        acl_fetch_rdp_cookie,     acl_match_str,     ACL_USE_L6REQ_VOLATILE|ACL_MAY_LOOKUP, 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) },
 	{ "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|ACL_MAY_LOOKUP, 0 },
 	{ "req_ssl_ver",         acl_parse_dotted_ver, acl_fetch_req_ssl_ver,    acl_match_int,     ACL_USE_L6REQ_VOLATILE, 0 },
diff --git a/src/backend.c b/src/backend.c
index fc8f22f..c0c2412 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -402,7 +402,6 @@
 	unsigned long    len;
 	const char      *p;
 	int              ret;
-	struct acl_expr  expr;
 	struct sample    smp;
 	struct arg       args[2];
 
@@ -410,7 +409,6 @@
 	if (px->lbprm.tot_weight == 0)
 		return NULL;
 
-	memset(&expr, 0, sizeof(expr));
 	memset(&smp, 0, sizeof(smp));
 
 	args[0].type = ARGT_STR;
@@ -418,9 +416,7 @@
 	args[0].data.str.len = px->hh_len;
 	args[1].type = ARGT_STOP;
 
-	expr.args = args;
-
-	ret = acl_fetch_rdp_cookie(px, s, NULL, ACL_DIR_REQ, &expr, &smp);
+	ret = smp_fetch_rdp_cookie(px, s, NULL, ACL_DIR_REQ, args, &smp);
 	len = smp.data.str.len;
 
 	if (ret == 0 || (smp.flags & SMP_F_MAY_CHANGE) || len == 0)
@@ -1113,7 +1109,6 @@
 {
 	struct proxy    *px   = s->be;
 	int              ret;
-	struct acl_expr  expr;
 	struct sample    smp;
 	struct server *srv = px->srv;
 	struct sockaddr_in addr;
@@ -1132,7 +1127,6 @@
 	if (s->flags & SN_ASSIGNED)
 		goto no_cookie;
 
-	memset(&expr, 0, sizeof(expr));
 	memset(&smp, 0, sizeof(smp));
 
 	args[0].type = ARGT_STR;
@@ -1140,9 +1134,7 @@
 	args[0].data.str.len = s->be->rdp_cookie_len;
 	args[1].type = ARGT_STOP;
 
-	expr.args = args;
-
-	ret = acl_fetch_rdp_cookie(px, s, NULL, ACL_DIR_REQ, &expr, &smp);
+	ret = smp_fetch_rdp_cookie(px, s, NULL, ACL_DIR_REQ, args, &smp);
 	if (ret == 0 || (smp.flags & SMP_F_MAY_CHANGE) || smp.data.str.len == 0)
 		goto no_cookie;
 
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index bd46de2..317a5f3 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -1248,9 +1248,145 @@
 
 
 /************************************************************************/
+/*       All supported sample fetch functios 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 patterns. 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, int dir,
+                     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->i;
+	if (bleft <= 11)
+		goto too_short;
+
+	data = (const unsigned char *)l4->req->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;
+}
+
+static int
+pattern_fetch_rdp_cookie(struct proxy *px, struct session *l4, void *l7, int dir,
+                         const struct arg *arg_p, struct sample *smp)
+{
+	int ret;
+
+	/* sample type set by smp_fetch_rdp_cookie() */
+	ret = smp_fetch_rdp_cookie(px, l4, NULL, ACL_DIR_REQ, arg_p, smp);
+	if (ret == 0 || (smp->flags & SMP_F_MAY_CHANGE) || smp->data.str.len == 0)
+		return 0;
+	return 1;
+}
+
+/************************************************************************/
 /*           All supported ACL keywords must be declared here.          */
 /************************************************************************/
 
+static int
+acl_fetch_rdp_cookie(struct proxy *px, struct session *l4, void *l7, int dir,
+                     struct acl_expr *expr, struct sample *smp)
+{
+	return smp_fetch_rdp_cookie(px, l4, l7, dir, expr->args, smp);
+}
+
+/* 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, int dir,
+                         struct acl_expr *expr, struct sample *smp)
+{
+	int ret;
+
+	ret = smp_fetch_rdp_cookie(px, l4, l7, dir, expr->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;
+}
+
+
 /* copy the source IPv4/v6 address into temp_pattern */
 static int
 acl_fetch_src(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -1469,34 +1605,6 @@
 	smp->type = SMP_T_CBIN;
 	chunk_initlen(&smp->data.str, b->p + buf_offset, 0, buf_size);
 
-	return 1;
-}
-
-static int
-pattern_fetch_rdp_cookie(struct proxy *px, struct session *l4, void *l7, int dir,
-                         const struct arg *arg_p, struct sample *smp)
-{
-	int ret;
-	struct acl_expr  expr;
-	struct arg       args[2];
-
-	if (!l4)
-		return 0;
-
-	memset(&expr, 0, sizeof(expr));
-	memset(smp, 0, sizeof(*smp));
-
-	args[0].type = ARGT_STR;
-	args[0].data.str.str = arg_p[0].data.str.str;
-	args[0].data.str.len = arg_p[0].data.str.len;
-	args[1].type = ARGT_STOP;
-
-	expr.args = args;
-
-	/* type set by acl_fetch_rdp_cookie */
-	ret = acl_fetch_rdp_cookie(px, l4, NULL, ACL_DIR_REQ, &expr, smp);
-	if (ret == 0 || (smp->flags & SMP_F_MAY_CHANGE) || smp->data.str.len == 0)
-		return 0;
 	return 1;
 }
 
@@ -1556,6 +1664,8 @@
 static struct acl_kw_list acl_kws = {{ },{
 	{ "dst",        acl_parse_ip,    acl_fetch_dst,      acl_match_ip,  ACL_USE_TCP4_PERMANENT|ACL_MAY_LOOKUP, 0 },
 	{ "dst_port",   acl_parse_int,   acl_fetch_dport,    acl_match_int, ACL_USE_TCP_PERMANENT, 0  },
+	{ "req_rdp_cookie",     acl_parse_str, acl_fetch_rdp_cookie,     acl_match_str, ACL_USE_L6REQ_VOLATILE|ACL_MAY_LOOKUP, 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,    acl_fetch_src,      acl_match_ip,  ACL_USE_TCP4_PERMANENT|ACL_MAY_LOOKUP, 0 },
 	{ "src_port",   acl_parse_int,   acl_fetch_sport,    acl_match_int, ACL_USE_TCP_PERMANENT, 0  },
 	{ NULL, NULL, NULL, NULL },