[MEDIUM] add support for RDP cookie persistence
The new statement "persist rdp-cookie" enables RDP cookie
persistence. The RDP cookie is then extracted from the RDP
protocol, and compared against available servers. If a server
matches the RDP cookie, then it gets the connection.
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 73446a4..114d876 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -824,6 +824,10 @@
curproxy->cookie_name = strdup(defproxy.cookie_name);
curproxy->cookie_len = defproxy.cookie_len;
+ if (defproxy.rdp_cookie_name)
+ curproxy->rdp_cookie_name = strdup(defproxy.rdp_cookie_name);
+ curproxy->rdp_cookie_len = defproxy.rdp_cookie_len;
+
if (defproxy.url_param_name)
curproxy->url_param_name = strdup(defproxy.url_param_name);
curproxy->url_param_len = defproxy.url_param_len;
@@ -891,6 +895,7 @@
*/
free(defproxy.check_req);
free(defproxy.cookie_name);
+ free(defproxy.rdp_cookie_name);
free(defproxy.url_param_name);
free(defproxy.hh_name);
free(defproxy.capture_name);
@@ -1222,6 +1227,49 @@
return -1;
}
}/* end else if (!strcmp(args[0], "cookie")) */
+ else if (!strcmp(args[0], "persist")) { /* persist */
+ if (*(args[1]) == 0) {
+ Alert("parsing [%s:%d] : missing persist method.\n",
+ file, linenum);
+ return -1;
+ }
+
+ if (!strncmp(args[1], "rdp-cookie", 10)) {
+ curproxy->options2 |= PR_O2_RDPC_PRST;
+
+ if (*(args[1] + 10 ) == '(') { /* cookie name */
+ const char *beg, *end;
+
+ beg = args[1] + 11;
+ end = strchr(beg, ')');
+
+ if (!end || end == beg) {
+ Alert("parsing [%s:%d] : persist rdp-cookie(name)' requires an rdp cookie name.\n",
+ file, linenum);
+ return -1;
+ }
+
+ free(curproxy->rdp_cookie_name);
+ curproxy->rdp_cookie_name = my_strndup(beg, end - beg);
+ curproxy->rdp_cookie_len = end-beg;
+ }
+ else if (*(args[1] + 10 ) == '\0') { /* default cookie name 'msts' */
+ free(curproxy->rdp_cookie_name);
+ curproxy->rdp_cookie_name = strdup("msts");
+ curproxy->rdp_cookie_len = strlen(curproxy->rdp_cookie_name);
+ }
+ else { /* syntax */
+ Alert("parsing [%s:%d] : persist rdp-cookie(name)' requires an rdp cookie name.\n",
+ file, linenum);
+ return -1;
+ }
+ }
+ else {
+ Alert("parsing [%s:%d] : unknown persist method.\n",
+ file, linenum);
+ return -1;
+ }
+ }
else if (!strcmp(args[0], "appsession")) { /* cookie name */
if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 93fc126..ff32726 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -446,6 +446,74 @@
return 1;
}
+/* Apply RDP cookie persistence to the current session. For this, the function
+ * tries to extract an RDP cookie from the request buffer, and look for the
+ * matching server in the list. If the server is found, it is assigned to the
+ * session. This always returns 1, and the analyser removes itself from the
+ * list. Nothing is performed if a server was already assigned.
+ */
+int tcp_persist_rdp_cookie(struct session *s, struct buffer *req, int an_bit)
+{
+ struct proxy *px = s->be;
+ int ret;
+ struct acl_expr expr;
+ struct acl_test test;
+ struct server *srv = px->srv;
+ struct sockaddr_in addr;
+ char *p;
+
+ DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
+ now_ms, __FUNCTION__,
+ s,
+ req,
+ req->rex, req->wex,
+ req->flags,
+ req->l,
+ req->analysers);
+
+ if (s->flags & SN_ASSIGNED)
+ goto no_cookie;
+
+ memset(&expr, 0, sizeof(expr));
+ memset(&test, 0, sizeof(test));
+
+ expr.arg.str = s->be->rdp_cookie_name;
+ expr.arg_len = s->be->rdp_cookie_len;
+
+ ret = acl_fetch_rdp_cookie(px, s, NULL, ACL_DIR_REQ, &expr, &test);
+ if (ret == 0 || (test.flags & ACL_TEST_F_MAY_CHANGE) || test.len == 0)
+ goto no_cookie;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+
+ /* Considering an rdp cookie detected using acl, test.ptr ended with <cr><lf> and should return */
+ addr.sin_addr.s_addr = strtoul(test.ptr, &p, 10);
+ if (*p != '.')
+ goto no_cookie;
+ p++;
+ addr.sin_port = (unsigned short)strtoul(p, &p, 10);
+ if (*p != '.')
+ goto no_cookie;
+
+ while (srv) {
+ if (memcmp(&addr, &(srv->addr), sizeof(addr)) == 0) {
+ if ((srv->state & SRV_RUNNING) || (px->options & PR_O_PERSIST)) {
+ /* we found the server and it is usable */
+ s->flags |= SN_DIRECT | SN_ASSIGNED;
+ s->srv = srv;
+ break;
+ }
+ }
+ srv = srv->next;
+ }
+
+no_cookie:
+ req->analysers &= ~an_bit;
+ req->analyse_exp = TICK_ETERNITY;
+ return 1;
+}
+
/* This function should be called to parse a line starting with the "tcp-request"
* keyword.
diff --git a/src/proxy.c b/src/proxy.c
index f6c142f..4e75b59 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -677,6 +677,12 @@
s->req->analysers |= AN_REQ_WAIT_HTTP | AN_REQ_HTTP_PROCESS_BE | AN_REQ_HTTP_INNER;
}
+ /* If the backend does requires RDP cookie persistence, we have to
+ * enable the corresponding analyser.
+ */
+ if (s->be->options2 & PR_O2_RDPC_PRST)
+ s->req->analysers |= AN_REQ_PRST_RDP_COOKIE;
+
return 1;
}
diff --git a/src/session.c b/src/session.c
index af6061f..d116ef2 100644
--- a/src/session.c
+++ b/src/session.c
@@ -850,6 +850,12 @@
if (!http_process_request_body(s, s->req, AN_REQ_HTTP_BODY))
break;
}
+
+ if (s->req->analysers & AN_REQ_PRST_RDP_COOKIE) {
+ last_ana |= AN_REQ_PRST_RDP_COOKIE;
+ if (!tcp_persist_rdp_cookie(s, s->req, AN_REQ_PRST_RDP_COOKIE))
+ break;
+ }
}
}