[master] 89ca131 Add support for PROXY protocol version 1
Poul-Henning Kamp
phk at FreeBSD.org
Tue Apr 7 12:36:55 CEST 2015
commit 89ca131f5387631fa2de61a8a39c25aa0be51cfe
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date: Tue Apr 7 10:36:37 2015 +0000
Add support for PROXY protocol version 1
diff --git a/bin/varnishd/proxy/cache_proxy_proto.c b/bin/varnishd/proxy/cache_proxy_proto.c
index 9f5ca90..8c8eb07 100644
--- a/bin/varnishd/proxy/cache_proxy_proto.c
+++ b/bin/varnishd/proxy/cache_proxy_proto.c
@@ -29,13 +29,233 @@
#include "config.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+
#include "../cache/cache.h"
+#include "vend.h"
+#include "vsa.h"
+
+static const char vpx2_sig[] = {
+ '\r', '\n', '\r', '\n', '\0', '\r', '\n',
+ 'Q', 'U', 'I', 'T', '\n',
+};
+
+/**********************************************************************
+ * PROXY 1 protocol
+ */
+
+static const char vpx1_sig[] = {'P', 'R', 'O', 'X', 'Y'};
+
+static int
+vpx_proto1(const struct worker *wrk, struct req *req)
+{
+ const char *fld[5];
+ int i;
+ char *p, *q;
+ struct addrinfo hints, *res;
+ struct suckaddr *sa;
+ int pfam = 0;
+
+ CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+ CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+
+ VSL(SLT_Debug, req->sp->fd, "PROXY1");
+
+ q = strchr(req->htc->rxbuf_b, '\r');
+ AN(q);
+
+ *q++ = '\0';
+ /* Nuke the CRNL */
+ if (*q != '\n')
+ return (-1);
+ *q++ = '\0';
+
+ /* Split the fields */
+ p = req->htc->rxbuf_b;
+ for (i = 0; i < 5; i++) {
+ p = strchr(p, ' ');
+ if (p == NULL) {
+ VSLb(req->vsl, SLT_ProxyGarbage,
+ "PROXY1: Too few fields");
+ return (-1);
+ }
+ *p++ = '\0';
+ fld[i] = p;
+ }
+
+ if (strchr(p, ' ')) {
+ VSLb(req->vsl, SLT_ProxyGarbage,
+ "PROXY1: Too many fields");
+ return (-1);
+ }
+
+ VSL(SLT_Debug, req->sp->fd, "PROXY1 <%s> <%s> <%s> <%s> <%s>",
+ fld[0], fld[1], fld[2], fld[3], fld[4]);
+
+ if (!strcmp(fld[0], "TCP4"))
+ pfam = AF_INET;
+ else if (!strcmp(fld[0], "TCP6"))
+ pfam = AF_INET6;
+ else {
+ VSLb(req->vsl, SLT_ProxyGarbage,
+ "PROXY1: Wrong TCP[46] field");
+ return (-1);
+ }
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+
+ i = getaddrinfo(fld[1], fld[2], &hints, &res);
+ if (i != 0) {
+ VSLb(req->vsl, SLT_ProxyGarbage,
+ "PROXY1: Cannot resolve source address (%s)",
+ gai_strerror(i));
+ return (-1);
+ }
+ AZ(res->ai_next);
+ if (res->ai_family != pfam) {
+ VSLb(req->vsl, SLT_ProxyGarbage,
+ "PROXY1: %s got wrong protocol (%d)",
+ fld[0], res->ai_family);
+ freeaddrinfo(res);
+ return (-1);
+ }
+ SES_Reserve_client_addr(req->sp, &sa);
+ AN(VSA_Build(sa, res->ai_addr, res->ai_addrlen));
+ SES_Set_String_Attr(req->sp, SA_CLIENT_IP, fld[1]);
+ SES_Set_String_Attr(req->sp, SA_CLIENT_PORT, fld[2]);
+ freeaddrinfo(res);
+
+ i = getaddrinfo(fld[3], fld[4], &hints, &res);
+ if (i != 0) {
+ VSLb(req->vsl, SLT_ProxyGarbage,
+ "PROXY1: Cannot resolve destination address (%s)",
+ gai_strerror(i));
+ return (-1);
+ }
+ AZ(res->ai_next);
+ if (res->ai_family != pfam) {
+ VSLb(req->vsl, SLT_ProxyGarbage,
+ "PROXY1: %s got wrong protocol (%d)",
+ fld[0], res->ai_family);
+ freeaddrinfo(res);
+ return (-1);
+ }
+ SES_Reserve_server_addr(req->sp, &sa);
+ AN(VSA_Build(sa, res->ai_addr, res->ai_addrlen));
+ freeaddrinfo(res);
+
+ req->htc->pipeline_b = q;
+ return (0);
+}
+
+static int
+vpx_proto2(const struct worker *wrk, struct req *req)
+{
+ int l;
+
+ CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+ CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+ VSL(SLT_Debug, req->sp->fd, "PROXY2");
+
+ assert(req->htc->rxbuf_e - req->htc->rxbuf_b >= 16);
+ l = vbe16dec(req->htc->rxbuf_b + 14);
+ req->htc->pipeline_b = req->htc->rxbuf_b + 16 + l;
+ return (0);
+}
+
+static enum htc_status_e __match_proto__(htc_complete_f)
+vpx_complete(struct http_conn *htc)
+{
+ int i, l, j;
+ char *p;
+
+ CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
+ AZ(htc->pipeline_b);
+ AZ(htc->pipeline_e);
+
+ l = htc->rxbuf_e - htc->rxbuf_b;
+ p = htc->rxbuf_b;
+ j = 0x3;
+ for (i = 0; i < l; i++) {
+ if (i < sizeof vpx1_sig && p[i] != vpx1_sig[i])
+ j &= ~1;
+ if (i < sizeof vpx2_sig && p[i] != vpx2_sig[i])
+ j &= ~2;
+ if (j == 0)
+ return (HTC_S_JUNK);
+ if (j == 1 && i == sizeof vpx1_sig) {
+ if (strchr(p + i, '\n') == NULL)
+ return (HTC_S_MORE);
+ return (HTC_S_COMPLETE);
+ }
+ if (j == 2 && i == sizeof vpx2_sig) {
+ if (l < 16)
+ return (HTC_S_MORE);
+ j = vbe16dec(p + 14);
+ if (l < 16 + j)
+ return (HTC_S_MORE);
+ return (HTC_S_COMPLETE);
+ }
+ }
+ return (HTC_S_MORE);
+}
+
+
void __match_proto__(task_func_t)
VPX_Proto_Sess(struct worker *wrk, void *priv)
{
+ struct req *req;
+ struct sess *sp;
+ enum htc_status_e hs;
+ char *p;
+ int i;
+
+ CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+ CAST_OBJ_NOTNULL(req, priv, REQ_MAGIC);
+ sp = req->sp;
+
+ /* Per specifiction */
+ assert(sizeof vpx1_sig == 5);
+ assert(sizeof vpx2_sig == 12);
+
+ hs = SES_RxReq(wrk, req, vpx_complete);
+ if (hs != HTC_S_COMPLETE) {
+ Req_Release(req);
+ SES_Delete(sp, SC_RX_JUNK, NAN);
+ return;
+ }
+ p = req->htc->rxbuf_b;
+ if (p[0] == vpx1_sig[0])
+ i = vpx_proto1(wrk, req);
+ else if (p[0] == vpx2_sig[0])
+ i = vpx_proto2(wrk, req);
+ else
+ WRONG("proxy sig mismatch");
+
+ if (i) {
+ Req_Release(req);
+ SES_Delete(sp, SC_RX_JUNK, NAN);
+ return;
+ }
- (void)wrk;
- (void)priv;
- INCOMPL();
+ if (req->htc->rxbuf_e == req->htc->pipeline_b)
+ req->htc->pipeline_b = NULL;
+ else
+ req->htc->pipeline_e = req->htc->rxbuf_e;
+ WS_Release(req->htc->ws, 0);
+ SES_RxReInit(req->htc);
+ req->t_req = NAN;
+ req->t_first = NAN;
+ req->sp->sess_step = S_STP_H1NEWREQ;
+ wrk->task.func = SES_Proto_Req;
+ wrk->task.priv = req;
+ return;
}
diff --git a/bin/varnishtest/tests/o00000.vtc b/bin/varnishtest/tests/o00000.vtc
new file mode 100644
index 0000000..ef535e2
--- /dev/null
+++ b/bin/varnishtest/tests/o00000.vtc
@@ -0,0 +1,159 @@
+varnishtest "PROXY1 protocol tests"
+
+server s1 {
+ rxreq
+ txresp
+ rxreq
+ txresp
+} -start
+
+varnish v1 -proto "PROXY" -vcl+backend {
+ import ${vmod_std};
+
+ sub vcl_deliver {
+ set resp.http.li = local.ip;
+ set resp.http.lp = std.port(local.ip);
+ set resp.http.ri = remote.ip;
+ set resp.http.rp = std.port(remote.ip);
+ set resp.http.ci = client.ip;
+ set resp.http.cp = std.port(client.ip);
+ set resp.http.si = server.ip;
+ set resp.http.sp = std.port(server.ip);
+ }
+} -start
+
+client c1 {
+ send "XYZ\r\n"
+ expect_close
+} -run
+delay .1
+
+client c1 {
+ send "PROXY "
+ timeout 8
+ expect_close
+} -run
+delay .1
+
+client c1 {
+ send "PROXY A B C D\r\n"
+ timeout 8
+ expect_close
+} -run
+delay .1
+
+client c1 {
+ send "PROXY A B C D E F\r\n"
+ timeout 8
+ expect_close
+} -run
+delay .1
+
+client c1 {
+ send "PROXY A B C D E\r\n"
+ timeout 8
+ expect_close
+} -run
+delay .1
+
+client c1 {
+ send "PROXY TCP4 B C D E\r\n"
+ timeout 8
+ expect_close
+} -run
+delay .1
+
+client c1 {
+ send "PROXY TCP4 1.2.3.4 C D E\r\n"
+ timeout 8
+ expect_close
+} -run
+delay .1
+
+client c1 {
+ send "PROXY TCP4 1.2.3.4 1234 D E\r\n"
+ timeout 8
+ expect_close
+} -run
+delay .1
+
+client c1 {
+ send "PROXY TCP4 1.2.3.4 1234 5.6.7.8 E\r\n"
+ timeout 8
+ expect_close
+} -run
+delay .1
+
+client c1 {
+ send "PROXY TCP6 B C D E\r\n"
+ timeout 8
+ expect_close
+} -run
+delay .1
+
+client c1 {
+ send "PROXY TCP6 1:f::2 C D E\r\n"
+ timeout 8
+ expect_close
+} -run
+delay .1
+
+client c1 {
+ send "PROXY TCP6 1:f::2 1234 D E\r\n"
+ timeout 8
+ expect_close
+} -run
+delay .1
+
+client c1 {
+ send "PROXY TCP6 1:f::2 1234 5:a::8 E\r\n"
+ timeout 8
+ expect_close
+} -run
+delay .1
+
+client c1 {
+ send "PROXY TCP4 1:f::2 1234 5:a::8 5678\r\n"
+ timeout 8
+ expect_close
+} -run
+delay .1
+
+client c1 {
+ send "PROXY TCP6 1.2.3.4 1234 5.6.7.8 5678\r\n"
+ timeout 8
+ expect_close
+} -run
+delay .1
+
+# Finally try something which works...
+client c1 {
+ send "PROXY TCP4 1.2.3.4 1234 5.6.7.8 5678\r\n"
+ txreq
+ rxresp
+ expect resp.http.ci == "1.2.3.4"
+ expect resp.http.cp == "1234"
+ expect resp.http.si == "5.6.7.8"
+ expect resp.http.sp == "5678"
+ expect resp.http.li == ${v1_addr}
+ expect resp.http.lp == ${v1_port}
+ expect resp.http.ri != "1.2.3.4"
+ expect resp.http.rp != "1234"
+} -run
+delay .1
+
+client c1 {
+ send "PROXY TCP6 1:f::2 1234 5:a::8 5678\r\n"
+ txreq
+ rxresp
+ expect resp.http.ci == "1:f::2"
+ expect resp.http.cp == "1234"
+ expect resp.http.si == "5:a::8"
+ expect resp.http.sp == "5678"
+ expect resp.http.li == ${v1_addr}
+ expect resp.http.lp == ${v1_port}
+ expect resp.http.ri != "1:f::2"
+ expect resp.http.rp != "1234"
+} -run
+delay .1
+
diff --git a/include/tbl/vsl_tags.h b/include/tbl/vsl_tags.h
index 4080c5d..94d0910 100644
--- a/include/tbl/vsl_tags.h
+++ b/include/tbl/vsl_tags.h
@@ -138,6 +138,10 @@ SLTM(HttpGarbage, SLT_F_BINARY, "Unparseable HTTP request",
"Logs the content of unparseable HTTP requests.\n\n"
)
+SLTM(ProxyGarbage, 0, "Unparseable PROXY request",
+ "A PROXY protocol header was unparseable.\n\n"
+)
+
SLTM(Backend, 0, "Backend selected",
"Logged when a connection is selected for handling a backend"
" request.\n\n"
More information about the varnish-commit
mailing list