[master] 5c0b833 Support Unix domain sockets as addresses in backend definitions.

Geoff Simmons geoff at uplex.de
Mon Mar 12 09:45:10 UTC 2018


commit 5c0b833a0170f0f91631f852abdf6c7bf2aa7cbd
Author: Geoff Simmons <geoff at uplex.de>
Date:   Mon Feb 19 02:39:53 2018 +0100

    Support Unix domain sockets as addresses in backend definitions.

diff --git a/bin/varnishd/cache/cache_backend.c b/bin/varnishd/cache/cache_backend.c
index 11a35c9..74b29d6 100644
--- a/bin/varnishd/cache/cache_backend.c
+++ b/bin/varnishd/cache/cache_backend.c
@@ -212,6 +212,7 @@ vbe_dir_gethdrs(const struct director *d, struct worker *wrk,
 	int i, extrachance = 1;
 	struct backend *bp;
 	struct pfd *pfd;
+	char abuf[VTCP_ADDRBUFSIZE], pbuf[VTCP_PORTBUFSIZE];
 
 	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
@@ -234,8 +235,9 @@ vbe_dir_gethdrs(const struct director *d, struct worker *wrk,
 		if (PFD_State(pfd) != PFD_STATE_STOLEN)
 			extrachance = 0;
 
+		PFD_RemoteName(pfd, abuf, sizeof abuf, pbuf, sizeof pbuf);
 		i = V1F_SendReq(wrk, bo, &bo->acct.bereq_hdrbytes,
-				&bo->acct.bereq_bodybytes, 0);
+				&bo->acct.bereq_bodybytes, 0, abuf, pbuf);
 
 		if (PFD_State(pfd) != PFD_STATE_USED) {
 			if (VTP_Wait(wrk, pfd, VTIM_real() +
@@ -300,6 +302,7 @@ vbe_dir_http1pipe(const struct director *d, struct req *req, struct busyobj *bo)
 	struct backend *bp;
 	struct v1p_acct v1a;
 	struct pfd *pfd;
+	char abuf[VTCP_ADDRBUFSIZE], pbuf[VTCP_PORTBUFSIZE];
 
 	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
 	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
@@ -320,7 +323,9 @@ vbe_dir_http1pipe(const struct director *d, struct req *req, struct busyobj *bo)
 		retval = SC_TX_ERROR;
 	} else {
 		CHECK_OBJ_NOTNULL(bo->htc, HTTP_CONN_MAGIC);
-		i = V1F_SendReq(req->wrk, bo, &v1a.bereq, &v1a.out, 1);
+		PFD_RemoteName(pfd, abuf, sizeof abuf, pbuf, sizeof pbuf);
+		i = V1F_SendReq(req->wrk, bo, &v1a.bereq, &v1a.out, 1, abuf,
+				pbuf);
 		VSLb_ts_req(req, "Pipe", W_TIM_real(req->wrk));
 		if (i == 0)
 			V1P_Process(req, *PFD_Fd(pfd), &v1a);
@@ -437,7 +442,12 @@ VRT_new_backend_clustered(VRT_CTX, struct vsmw_cluster *vc,
 
 	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
 	CHECK_OBJ_NOTNULL(vrt, VRT_BACKEND_MAGIC);
-	assert(vrt->ipv4_suckaddr != NULL || vrt->ipv6_suckaddr != NULL);
+	if (vrt->path == NULL)
+		assert(vrt->ipv4_suckaddr != NULL
+		       || vrt->ipv6_suckaddr != NULL);
+	else
+		assert(vrt->ipv4_suckaddr == NULL
+		       && vrt->ipv6_suckaddr == NULL);
 
 	vcl = ctx->vcl;
 	AN(vcl);
@@ -480,7 +490,7 @@ VRT_new_backend_clustered(VRT_CTX, struct vsmw_cluster *vc,
 	VTAILQ_INSERT_TAIL(&backends, be, list);
 	VSC_C_main->n_backend++;
 	be->tcp_pool = VTP_Ref(vrt->ipv4_suckaddr, vrt->ipv6_suckaddr,
-	    vbe_proto_ident);
+	    vrt->path, vbe_proto_ident);
 	Lck_Unlock(&backends_mtx);
 
 	if (vbp != NULL) {
diff --git a/bin/varnishd/cache/cache_backend_probe.c b/bin/varnishd/cache/cache_backend_probe.c
index b5522dc..478ad18 100644
--- a/bin/varnishd/cache/cache_backend_probe.c
+++ b/bin/varnishd/cache/cache_backend_probe.c
@@ -283,7 +283,9 @@ vbp_poke(struct vbp_target *vt)
 	}
 
 	i = VSA_Get_Proto(sa);
-	if (i == AF_INET)
+	if (VSA_Compare(sa, bogo_ip) == 0)
+		vt->good_unix |= 1;
+	else if (i == AF_INET)
 		vt->good_ipv4 |= 1;
 	else if (i == AF_INET6)
 		vt->good_ipv6 |= 1;
diff --git a/bin/varnishd/cache/cache_tcp_pool.c b/bin/varnishd/cache/cache_tcp_pool.c
index c63ad64..e9fa812 100644
--- a/bin/varnishd/cache/cache_tcp_pool.c
+++ b/bin/varnishd/cache/cache_tcp_pool.c
@@ -38,6 +38,7 @@
 
 #include "vsa.h"
 #include "vtcp.h"
+#include "vus.h"
 #include "vtim.h"
 #include "waiter/waiter.h"
 
@@ -106,6 +107,7 @@ struct tcp_pool {
 
 	struct suckaddr				*ip4;
 	struct suckaddr				*ip6;
+	char					*uds;
 	struct conn_pool			cp[1];
 };
 
@@ -489,6 +491,20 @@ VCP_Wait(struct worker *wrk, struct pfd *pfd, double tmo)
 /*--------------------------------------------------------------------
  */
 
+struct vtp_cs {
+	unsigned			magic;
+#define VTP_CS_MAGIC			0xc1e40447
+	const struct suckaddr		*ip4;
+	const struct suckaddr		*ip6;
+	const char			*uds;
+};
+
+static inline int
+tmo2msec(double tmo)
+{
+	return ( (int)floor(tmo * 1000.0) );
+}
+
 static int v_matchproto_(cp_open_f)
 vtp_open(const struct conn_pool *cp, double tmo, const void **privp)
 {
@@ -499,7 +515,7 @@ vtp_open(const struct conn_pool *cp, double tmo, const void **privp)
 	CHECK_OBJ_NOTNULL(cp, CONN_POOL_MAGIC);
 	CAST_OBJ_NOTNULL(tp, cp->priv, TCP_POOL_MAGIC);
 
-	msec = (int)floor(tmo * 1000.0);
+	msec = tmo2msec(tmo);
 	if (cache_param->prefer_ipv6) {
 		*privp = tp->ip6;
 		s = VTCP_connect(tp->ip6, msec);
@@ -525,13 +541,6 @@ vtp_close(struct pfd *pfd)
 	VTCP_close(&pfd->fd);
 }
 
-struct vtp_cs {
-	unsigned			magic;
-#define VTP_CS_MAGIC			0xc1e40447
-	const struct suckaddr		*ip4;
-	const struct suckaddr		*ip6;
-};
-
 static int v_matchproto_(cp_cmp_f)
 vtp_cmp(const struct conn_pool *cp, const void *priv)
 {
@@ -581,23 +590,78 @@ static const struct cp_methods vtp_methods = {
 	.remote_name = vtp_remote_name,
 };
 
+/*--------------------------------------------------------------------
+ */
+
+static int v_matchproto_(cp_open_f)
+vus_open(const struct conn_pool *cp, double tmo, const void **privp)
+{
+	int s;
+	int msec;
+	struct tcp_pool *tp;
+
+	CHECK_OBJ_NOTNULL(cp, CONN_POOL_MAGIC);
+	CAST_OBJ_NOTNULL(tp, cp->priv, TCP_POOL_MAGIC);
+	AN(tp->uds);
+
+	msec = tmo2msec(tmo);
+	*privp = bogo_ip;
+	s = VUS_connect(tp->uds, msec);
+	return (s);
+}
+
+static int v_matchproto_(cp_cmp_f)
+vus_cmp(const struct conn_pool *cp, const void *priv)
+{
+	const struct vtp_cs *vcs;
+	const struct tcp_pool *tp;
+
+	CAST_OBJ_NOTNULL(vcs, priv, VTP_CS_MAGIC);
+	CAST_OBJ_NOTNULL(tp, cp->priv, TCP_POOL_MAGIC);
+	if (tp->uds != NULL && vcs->uds != NULL)
+		return (strcmp(tp->uds, vcs->uds));
+	return (1);
+}
+
+static void v_matchproto_(cp_name_f)
+vus_name(const struct pfd *pfd, char *addr, unsigned alen, char *pbuf,
+	 unsigned plen)
+{
+	(void) pfd;
+	assert(alen > strlen("0.0.0.0"));
+	assert(plen > 1);
+	strcpy(addr, "0.0.0.0");
+	strcpy(pbuf, "0");
+}
+
+static const struct cp_methods vus_methods = {
+	.open = vus_open,
+	.close = vtp_close,
+	.cmp = vus_cmp,
+	.local_name = vus_name,
+	.remote_name = vus_name,
+};
 
 /*--------------------------------------------------------------------
- * Reference a TCP pool given by {ip4, ip6} pair.  Create if it
- * doesn't exist already.
+ * Reference a TCP pool given by {ip4, ip6} pair or a UDS.  Create if
+ * it doesn't exist already.
  */
 
 struct tcp_pool *
-VTP_Ref(const struct suckaddr *ip4, const struct suckaddr *ip6, const void *id)
+VTP_Ref(const struct suckaddr *ip4, const struct suckaddr *ip6, const char *uds,
+	const void *id)
 {
 	struct tcp_pool *tp;
 	struct conn_pool *cp;
 	struct vtp_cs vcs;
+	const struct cp_methods *methods;
 
-	assert(ip4 != NULL || ip6 != NULL);
+	assert((uds != NULL && ip4 == NULL && ip6 == NULL)
+	       || (uds == NULL && (ip4 != NULL || ip6 != NULL)));
 	INIT_OBJ(&vcs, VTP_CS_MAGIC);
 	vcs.ip4 = ip4;
 	vcs.ip6 = ip6;
+	vcs.uds = uds;
 
 	cp = VCP_Ref(id, &vcs);
 	if (cp != NULL)
@@ -605,11 +669,18 @@ VTP_Ref(const struct suckaddr *ip4, const struct suckaddr *ip6, const void *id)
 
 	ALLOC_OBJ(tp, TCP_POOL_MAGIC);
 	AN(tp);
-	if (ip4 != NULL)
-		tp->ip4 = VSA_Clone(ip4);
-	if (ip6 != NULL)
-		tp->ip6 = VSA_Clone(ip6);
-	return(VCP_New(tp->cp, id, tp, &vtp_methods));
+	if (uds != NULL) {
+		methods = &vus_methods;
+		tp->uds = strdup(uds);
+	}
+	else {
+		methods = &vtp_methods;
+		if (ip4 != NULL)
+			tp->ip4 = VSA_Clone(ip4);
+		if (ip6 != NULL)
+			tp->ip6 = VSA_Clone(ip6);
+	}
+	return(VCP_New(tp->cp, id, tp, methods));
 }
 
 /*--------------------------------------------------------------------
@@ -638,6 +709,7 @@ VTP_Rel(struct tcp_pool **tpp)
 
 	free(tp->ip4);
 	free(tp->ip6);
+	free(tp->uds);
 	FREE_OBJ(tp);
 }
 
@@ -650,6 +722,8 @@ int
 VTP_Open(const struct tcp_pool *tp, double tmo, const void **privp)
 {
 
+	if (tp->uds != NULL)
+		return (vus_open(tp->cp, tmo, privp));
 	return (vtp_open(tp->cp, tmo, privp));
 }
 
diff --git a/bin/varnishd/cache/cache_tcp_pool.h b/bin/varnishd/cache/cache_tcp_pool.h
index 6c792c1..d554a36 100644
--- a/bin/varnishd/cache/cache_tcp_pool.h
+++ b/bin/varnishd/cache/cache_tcp_pool.h
@@ -51,11 +51,12 @@ void PFD_RemoteName(const struct pfd *, char *, unsigned, char *, unsigned);
  */
 
 struct tcp_pool *VTP_Ref(const struct suckaddr *ip4, const struct suckaddr *ip6,
-    const void *id);
+    const char *uds, const void *id);
 	/*
-	 * Get a reference to a TCP pool.  Either ip4 or ip6 arg must be
-	 * non-NULL. If recycling is to be used, the id pointer distinguishes
-	 * the pool per protocol.
+	 * Get a reference to a TCP pool. Either one or both of ip4 or
+	 * ip6 arg must be non-NULL, or uds must be non-NULL. If recycling
+	 * is to be used, the id pointer distinguishes the pool per
+	 * protocol.
 	 */
 
 void VTP_AddRef(struct tcp_pool *);
diff --git a/bin/varnishd/http1/cache_http1.h b/bin/varnishd/http1/cache_http1.h
index f3b3ed2..7dd0c62 100644
--- a/bin/varnishd/http1/cache_http1.h
+++ b/bin/varnishd/http1/cache_http1.h
@@ -31,7 +31,7 @@ struct VSC_vbe;
 
 /* cache_http1_fetch.c [V1F] */
 int V1F_SendReq(struct worker *, struct busyobj *, uint64_t *ctr_hdrbytes,
-    uint64_t *ctr_bodybytes, int onlycached);
+    uint64_t *ctr_bodybytes, int onlycached, char *addr, char *port);
 int V1F_FetchRespHdr(struct busyobj *);
 int V1F_Setup_Fetch(struct vfp_ctx *vfc, struct http_conn *htc);
 
diff --git a/bin/varnishd/http1/cache_http1_fetch.c b/bin/varnishd/http1/cache_http1_fetch.c
index a3b7b4f..1a77021 100644
--- a/bin/varnishd/http1/cache_http1_fetch.c
+++ b/bin/varnishd/http1/cache_http1_fetch.c
@@ -70,7 +70,7 @@ vbf_iter_req_body(void *priv, int flush, const void *ptr, ssize_t l)
 
 int
 V1F_SendReq(struct worker *wrk, struct busyobj *bo, uint64_t *ctr_hdrbytes,
-    uint64_t *ctr_bodybytes, int onlycached)
+    uint64_t *ctr_bodybytes, int onlycached, char *abuf, char *pbuf)
 {
 	struct http *hp;
 	int j;
@@ -78,8 +78,6 @@ V1F_SendReq(struct worker *wrk, struct busyobj *bo, uint64_t *ctr_hdrbytes,
 	uint64_t bytes, hdrbytes;
 	struct http_conn *htc;
 	int do_chunked = 0;
-	char abuf[VTCP_ADDRBUFSIZE];
-	char pbuf[VTCP_PORTBUFSIZE];
 
 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
 	CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
@@ -98,7 +96,6 @@ V1F_SendReq(struct worker *wrk, struct busyobj *bo, uint64_t *ctr_hdrbytes,
 		do_chunked = 1;
 	}
 
-	VTCP_hisname(*htc->rfd, abuf, sizeof abuf, pbuf, sizeof pbuf);
 	VSLb(bo->vsl, SLT_BackendStart, "%s %s", abuf, pbuf);
 
 	(void)VTCP_blocking(*htc->rfd);	/* XXX: we should timeout instead */
diff --git a/bin/varnishtest/tests/b00053.vtc b/bin/varnishtest/tests/b00053.vtc
index 09e3d20..0acb9da 100644
--- a/bin/varnishtest/tests/b00053.vtc
+++ b/bin/varnishtest/tests/b00053.vtc
@@ -1,6 +1,6 @@
 varnishtest "Does anything get through Unix domain sockets at all ?"
 
-server s1 {
+server s1 -listen "${tmpdir}/s1.sock" {
 	rxreq
 	txresp -body "012345\n"
 } -start
diff --git a/bin/varnishtest/tests/b00057.vtc b/bin/varnishtest/tests/b00057.vtc
index 74c73cc..9f75edb 100644
--- a/bin/varnishtest/tests/b00057.vtc
+++ b/bin/varnishtest/tests/b00057.vtc
@@ -1,7 +1,7 @@
 varnishtest "Test orderly connection closure of a UDS listen socket"
 
 
-server s1 {
+server s1 -listen "${tmpdir}/s1.sock" {
 	rxreq
 	txresp -nolen -hdr "Transfer-encoding: chunked"
 	delay .2
diff --git a/bin/varnishtest/tests/b00059.vtc b/bin/varnishtest/tests/b00059.vtc
index 00ce94f..c529a5f 100644
--- a/bin/varnishtest/tests/b00059.vtc
+++ b/bin/varnishtest/tests/b00059.vtc
@@ -1,6 +1,6 @@
 varnishtest "Run a lot of transactions through Unix domain sockets"
 
-server s0 {
+server s0 -listen "${tmpdir}/s1.sock" {
 	loop 10 {
 		rxreq
 		txresp -body "foo1"
diff --git a/bin/varnishtest/tests/b00060.vtc b/bin/varnishtest/tests/b00060.vtc
index 9e2750a..cdbb2d4 100644
--- a/bin/varnishtest/tests/b00060.vtc
+++ b/bin/varnishtest/tests/b00060.vtc
@@ -1,12 +1,13 @@
 varnishtest "VSL tags affected by the use of UDS addresses"
 
-varnish v1 -arg "-a foo=${tmpdir}/foo.sock -a bar=${tmpdir}/bar.sock" -vcl {
-	backend b { .host = "${bad_ip}"; }
-
-	sub vcl_recv { return(synth(200)); }
+server s1 -listen "${tmpdir}/s1.sock" {
+	rxreq
+	txresp
 } -start
 
-client c1 -connect "${tmpdir}/foo.sock" {
+varnish v1 -arg "-a foo=${tmpdir}/v1.sock" -vcl+backend {} -start
+
+client c1 -connect "${tmpdir}/v1.sock" {
 	txreq
 	rxresp
 } -run
@@ -16,45 +17,12 @@ logexpect l1 -v v1 -d 1 -g session {
 	expect 0 =	SessOpen	"^0.0.0.0 0 foo 0.0.0.0 0"
 } -run
 
-logexpect l2 -v v1 -d 1 -g vxid {
+logexpect l2 -v v1 -d 1 -g vxid -c {
 	expect 0 1001	Begin
-	expect * =	ReqStart	"^0.0.0.0 0 foo$"
-} -run
-
-logexpect l1 -v v1 -d 0 -g session {
-	expect 0 *	Begin
-	expect 0 =	SessOpen	"^0.0.0.0 0 bar 0.0.0.0 0"
-} -start
-
-logexpect l2 -v v1 -d 0 -g vxid {
-	expect 0 *	Begin
-	expect * =	ReqStart	"^0.0.0.0 0 bar"
-} -start
-
-client c1 -connect "${tmpdir}/bar.sock" {
-	txreq
-	rxresp
+	expect * =	ReqStart	"^0.0.0.0 0$"
 } -run
 
-logexpect l1 -wait
-logexpect l2 -wait
-
-varnish v1 -stop
-
-# For completeness, also test the endpoint name field in ReqStart when
-# Varnish listens at an IP address.
-varnish v2 -vcl {
-	backend b { .host = "${bad_ip}"; }
-
-	sub vcl_recv { return(synth(200)); }
-} -start
-
-client c2 -connect ${v2_sock} {
-	txreq
-	rxresp
-} -run
-
-logexpect l3 -v v2 -d 1 -g vxid {
-	expect 0 1001	Begin
-	expect * =	ReqStart	"^${v2_addr} [0-9]+ a0$"
+logexpect l2 -v v1 -d 1 -g vxid -b {
+	expect 0 1002	Begin
+	expect * =	BackendOpen	"${s1_sock} - - -$"
 } -run
diff --git a/bin/varnishtest/tests/c00087.vtc b/bin/varnishtest/tests/c00087.vtc
index 5f55cdf..761ba0d 100644
--- a/bin/varnishtest/tests/c00087.vtc
+++ b/bin/varnishtest/tests/c00087.vtc
@@ -1,6 +1,6 @@
 varnishtest "VCL *.ip vars as bogo-IPs when -a is UDS, and test ACL matches"
 
-server s1 {
+server s1 -listen "${tmpdir}/s1.sock" {
 	rxreq
 	txresp
 } -start
diff --git a/bin/varnishtest/tests/c00088.vtc b/bin/varnishtest/tests/c00088.vtc
new file mode 100644
index 0000000..b39171f
--- /dev/null
+++ b/bin/varnishtest/tests/c00088.vtc
@@ -0,0 +1,44 @@
+varnishtest "Dropping polling of a backend that listens at UDS"
+
+server s1 -listen "${tmpdir}/s1.sock" -repeat 40 {
+	rxreq
+	txresp
+} -start
+
+varnish v1 -vcl {
+	probe default {
+		.window = 8;
+		.initial = 7;
+		.threshold = 8;
+		.interval = 0.1s;
+	}
+	backend s1 {
+		.path = "${s1_sock}";
+	}
+} -start
+
+delay 1
+
+varnish v1 -vcl+backend { } -cliok "vcl.use vcl2" -cliok "vcl.discard vcl1"
+
+delay 1
+
+varnish v1 -cliok "vcl.list"
+varnish v1 -cliok "backend.list -p"
+
+server s1 -break {
+	rxreq
+	expect req.url == /foo
+	txresp -bodylen 4
+} -start
+
+delay 1
+
+client c1 {
+	txreq -url /foo
+	rxresp
+	txreq -url /foo
+	rxresp
+	expect resp.status == 200
+	expect resp.bodylen == 4
+} -run
diff --git a/bin/varnishtest/tests/c00089.vtc b/bin/varnishtest/tests/c00089.vtc
new file mode 100644
index 0000000..960741b
--- /dev/null
+++ b/bin/varnishtest/tests/c00089.vtc
@@ -0,0 +1,33 @@
+varnishtest "Backend close retry with a UDS"
+
+server s1 -listen "${tmpdir}/s1.sock" -repeat 1 {
+	rxreq
+	txresp -bodylen 5
+
+	rxreq
+	accept
+
+	rxreq
+	txresp -bodylen 6
+
+} -start
+
+varnish v1 -vcl+backend {
+	sub vcl_recv {
+		return(pass);
+	}
+} -start
+
+client c1 {
+	txreq
+	rxresp
+	expect resp.status == 200
+	expect resp.bodylen == 5
+
+	txreq
+	rxresp
+	expect resp.status == 200
+	expect resp.bodylen == 6
+} -run
+
+varnish v1 -expect backend_retry == 1
diff --git a/bin/varnishtest/tests/c00090.vtc b/bin/varnishtest/tests/c00090.vtc
new file mode 100644
index 0000000..189748b
--- /dev/null
+++ b/bin/varnishtest/tests/c00090.vtc
@@ -0,0 +1,65 @@
+varnishtest "Forcing health of backends listening at UDS"
+
+server s1 -listen "${tmpdir}/s1.sock" -repeat 3 {
+	rxreq
+	txresp
+} -start
+
+varnish v1 -vcl {
+	backend s1 {
+		.path = "${s1_sock}";
+		.probe = {
+			.window = 8;
+			.initial = 7;
+			.threshold = 8;
+			.interval = 10s;
+		}
+	}
+
+	sub vcl_recv {
+		return(pass);
+	}
+
+} -start
+
+delay 1
+
+varnish v1 -cliok "vcl.list"
+varnish v1 -cliok "backend.list -p"
+varnish v1 -cliok "backend.set_health s1 auto"
+varnish v1 -cliok "backend.list -p"
+
+client c1 {
+	txreq
+	rxresp
+	expect resp.status == 200
+} -run
+
+varnish v1 -cliok "backend.list"
+varnish v1 -cliok "backend.set_health s1 sick"
+varnish v1 -cliok "backend.list"
+
+client c1 {
+	txreq
+	rxresp
+	expect resp.status == 503
+} -run
+
+varnish v1 -cliok "backend.list"
+varnish v1 -cliok "backend.set_health s1 healthy"
+varnish v1 -cliok "backend.list"
+
+client c1 {
+	txreq
+	rxresp
+	expect resp.status == 200
+} -run
+
+varnish v1 -clierr 106 "backend.set_health s1 foo"
+varnish v1 -clierr 106 "backend.set_health s2 foo"
+varnish v1 -clierr 106 "backend.set_health s2 auto"
+varnish v1 -cliok "vcl.list"
+varnish v1 -cliok "backend.list *"
+varnish v1 -cliok "backend.list *.foo"
+varnish v1 -cliok "backend.list vcl1.*"
+
diff --git a/bin/varnishtest/tests/c00091.vtc b/bin/varnishtest/tests/c00091.vtc
new file mode 100644
index 0000000..59c63d1
--- /dev/null
+++ b/bin/varnishtest/tests/c00091.vtc
@@ -0,0 +1,50 @@
+varnishtest	"vcl_backend_response{} retry with a UDS backend"
+
+server s1 -listen "${tmpdir}/s1.sock" {
+	rxreq
+	txresp -hdr "foo: 1"
+	accept
+	rxreq
+	txresp -hdr "foo: 2"
+} -start
+
+varnish v1 -vcl+backend {
+	sub vcl_recv { return (pass); }
+	sub vcl_backend_response {
+		set beresp.http.bar = bereq.retries;
+		if (beresp.http.foo != bereq.http.stop) {
+			return (retry);
+		}
+	}
+} -start
+
+varnish v1 -cliok "param.set debug +syncvsl"
+
+client c1 {
+	txreq -hdr "stop: 2"
+	rxresp
+	expect resp.http.foo == 2
+} -run
+
+delay .1
+
+server s1 -listen "${tmpdir}/s1.sock" {
+	rxreq
+	txresp -hdr "foo: 1"
+	accept
+	rxreq
+	txresp -hdr "foo: 2"
+	accept
+	rxreq
+	txresp -hdr "foo: 3"
+} -start
+
+varnish v1 -cliok "param.set max_retries 2"
+
+client c1 {
+	txreq -hdr "stop: 3"
+	rxresp
+	expect resp.http.foo == 3
+} -run
+
+# XXX: Add a test which exceeds max_retries and gets 503 back
diff --git a/bin/varnishtest/tests/c00092.vtc b/bin/varnishtest/tests/c00092.vtc
new file mode 100644
index 0000000..df85d19
--- /dev/null
+++ b/bin/varnishtest/tests/c00092.vtc
@@ -0,0 +1,32 @@
+varnishtest "Check aborted backend body with a backend listening at UDS"
+
+barrier b1 cond 2
+barrier b2 cond 2
+
+server s1 -listen "${tmpdir}/s1.sock" {
+	rxreq
+	txresp -nolen -hdr "Transfer-encoding: chunked"
+	chunked {<HTML>}
+	barrier b1 sync
+	chunked {<HTML>}
+	barrier b2 sync
+} -start
+
+varnish v1 -cliok "param.set debug +syncvsl" -vcl+backend {
+
+} -start
+
+
+client c1 {
+	txreq
+	rxresphdrs
+	expect resp.status == 200
+	rxchunk
+	barrier b1 sync
+	rxchunk
+	barrier b2 sync
+	expect_close
+} -run
+
+
+
diff --git a/bin/varnishtest/tests/c00093.vtc b/bin/varnishtest/tests/c00093.vtc
new file mode 100644
index 0000000..625dc3d
--- /dev/null
+++ b/bin/varnishtest/tests/c00093.vtc
@@ -0,0 +1,39 @@
+varnishtest "Test resp.is_streaming with a UDS backend"
+
+server s1 -listen "${tmpdir}/s1.sock" {
+	rxreq
+	txresp -nolen -hdr "Content-Length: 10"
+	delay 1
+	send "1234567890"
+} -start
+
+varnish v1 -vcl+backend {
+	sub vcl_recv {
+		if (req.url == "/synth") {
+			return(synth(200, "OK"));
+		}
+	}
+	sub vcl_synth {
+		set resp.http.streaming = resp.is_streaming;
+	}
+	sub vcl_deliver {
+		set resp.http.streaming = resp.is_streaming;
+	}
+} -start
+
+client c1 {
+	txreq
+	rxresp
+	expect resp.http.streaming == "true"
+
+	delay 0.1
+
+	txreq
+	rxresp
+	expect resp.http.streaming == "false"
+
+	txreq -url /synth
+	rxresp
+	expect resp.http.streaming == "false"
+} -run
+
diff --git a/bin/varnishtest/tests/c00094.vtc b/bin/varnishtest/tests/c00094.vtc
new file mode 100644
index 0000000..546efb0
--- /dev/null
+++ b/bin/varnishtest/tests/c00094.vtc
@@ -0,0 +1,51 @@
+varnishtest "Test Backend Polling with a backend listening at a UDS"
+
+barrier b1 cond 2
+
+server s1 -listen "${tmpdir}/s1.sock" {
+	# Probes
+	loop 8 {
+		rxreq
+		expect req.url == "/"
+		txresp -hdr "Bar: foo" -body "foobar"
+		accept
+	}
+
+	loop 3 {
+		rxreq
+		expect req.url == "/"
+		txresp -status 404 -hdr "Bar: foo" -body "foobar"
+		accept
+	}
+	loop 2 {
+		rxreq
+		expect req.url == "/"
+		txresp -proto "FROBOZ" -status 200 -hdr "Bar: foo" -body "foobar"
+		accept
+	}
+	loop 2 {
+		rxreq
+		expect req.url == "/"
+		send "HTTP/1.1 200 \r\n"
+		accept
+	}
+
+	barrier b1 sync
+} -start
+
+varnish v1 -vcl {
+
+	backend foo {
+		.path = "${s1_sock}";
+		.probe = {
+			.timeout = 1 s;
+			.interval = 0.1 s;
+		}
+	}
+
+} -start
+
+barrier b1 sync
+
+varnish v1 -cliexpect "^CLI RX|  -+U+ Good UNIX" "backend.list -p"
+varnish v1 -cliexpect "^CLI RX|  -+H{10}-{5}H{2}-{0,5} Happy" "backend.list -p"
diff --git a/bin/varnishtest/tests/d00031.vtc b/bin/varnishtest/tests/d00031.vtc
new file mode 100644
index 0000000..fa11ee3
--- /dev/null
+++ b/bin/varnishtest/tests/d00031.vtc
@@ -0,0 +1,86 @@
+varnishtest "Test round robin director with UDS backends"
+
+server s1 -listen "${tmpdir}/s1.sock" {
+	rxreq
+	txresp -body "1"
+} -start
+
+server s2 -listen "${tmpdir}/s2.sock" {
+	rxreq
+	txresp -body "22"
+} -start
+
+server s3 -listen "${tmpdir}/s3.sock" {
+	rxreq
+	txresp -body "333"
+} -start
+
+server s4 -listen "${tmpdir}/s4.sock" {
+	rxreq
+	txresp -body "4444"
+} -start
+
+varnish v1 -vcl+backend {
+	import directors;
+
+	sub vcl_init {
+		new rr = directors.round_robin();
+		rr.add_backend(s1);
+		rr.add_backend(s2);
+		rr.add_backend(s3);
+		rr.add_backend(s4);
+	}
+
+	sub vcl_recv {
+		if (req.method == "DELETE") {
+			rr.remove_backend(s1);
+			rr.remove_backend(s2);
+			rr.remove_backend(s3);
+			return(synth(204));
+		}
+	}
+
+	sub vcl_backend_fetch {
+		set bereq.backend = rr.backend();
+	}
+} -start
+
+client c1 {
+	timeout 3
+	txreq -url "/foo1"
+	rxresp
+	expect resp.bodylen == 1
+	txreq -url "/foo2"
+	rxresp
+	expect resp.bodylen == 2
+	txreq -url "/foo3"
+	rxresp
+	expect resp.bodylen == 3
+	txreq -url "/foo4"
+	rxresp
+	expect resp.bodylen == 4
+} -run
+
+server s1 -start
+server s2 -start
+
+client c2 {
+	timeout 3
+	txreq -url "/foo11"
+	rxresp
+	expect resp.bodylen == 1
+	txreq -url "/foo22"
+	rxresp
+	expect resp.bodylen == 2
+} -run
+
+server s4 -start
+
+client c3 {
+	txreq -req "DELETE"
+	rxresp
+	expect resp.status == 204
+	txreq -url "/foo31"
+	rxresp
+	expect resp.bodylen == 4
+} -run
diff --git a/bin/varnishtest/tests/m00046.vtc b/bin/varnishtest/tests/m00046.vtc
index a5de275..dca4fba 100644
--- a/bin/varnishtest/tests/m00046.vtc
+++ b/bin/varnishtest/tests/m00046.vtc
@@ -21,7 +21,7 @@ client c1 -connect "${tmpdir}/v2.sock" {
 	expect resp.http.v1_addr == "0.0.0.0"
 } -run
 
-varnish v2 -errvcl {IP constant '"${v1_addr}"'} {
+varnish v2 -errvcl {Cannot convert to an IP address: '"${v1_addr}"'} {
 	import std;
 
 	sub vcl_recv {
diff --git a/bin/varnishtest/tests/t02013.vtc b/bin/varnishtest/tests/t02013.vtc
index 47a226c..3711111 100644
--- a/bin/varnishtest/tests/t02013.vtc
+++ b/bin/varnishtest/tests/t02013.vtc
@@ -1,6 +1,6 @@
 varnishtest	"Direct H2 start over Unix domain sockets"
 
-server s1 {
+server s1 -listen "${tmpdir}/s1.sock" {
 	rxreq
 	expect req.http.host == foo.bar
 	txresp \
diff --git a/bin/varnishtest/tests/v00038.vtc b/bin/varnishtest/tests/v00038.vtc
index f4a8c88..d93d4d5 100644
--- a/bin/varnishtest/tests/v00038.vtc
+++ b/bin/varnishtest/tests/v00038.vtc
@@ -87,7 +87,7 @@ varnish v1 -errvcl "Unknown field:" {
 	}
 }
 
-varnish v1 -errvcl "Mandatory field 'host' missing." {
+varnish v1 -errvcl "Expected .host or .path." {
 	backend b1 {
 		.port = "NONE";
 	}
@@ -109,3 +109,30 @@ varnish v1 -errvcl "Unused backend b1, defined:" {
 	backend b1 { .host = "127.0.0.1"; }
 	backend default { .host = "127.0.0.1"; }
 }
+
+varnish v1 -errvcl "Address redefinition at:" {
+	backend b1 {
+		.host = "127.0.0.1";
+		.path = "/path/to/uds";
+	}
+}
+
+varnish v1 -errvcl "Must be an absolute path:" {
+	backend b1 {
+		.path = "server.sock";
+	}
+}
+
+shell { rm -f ${tmpdir}/foo }
+
+varnish v1 -errvcl "Cannot stat:" {
+	backend b1 {
+		.path = "${tmpdir}/foo";
+	}
+}
+
+varnish v1 -errvcl "Not a socket:" {
+	backend b1 {
+		.path = "${tmpdir}";
+	}
+}
diff --git a/bin/varnishtest/tests/v00055.vtc b/bin/varnishtest/tests/v00055.vtc
new file mode 100644
index 0000000..4584f1b
--- /dev/null
+++ b/bin/varnishtest/tests/v00055.vtc
@@ -0,0 +1,41 @@
+varnishtest "Check backend connection limit with UDS backends"
+
+barrier b1 cond 2
+barrier b2 cond 2
+
+server s1 -listen "${tmpdir}/s1.sock" {
+	rxreq
+	barrier b1 sync
+	barrier b2 sync
+	txresp
+} -start
+
+varnish v1 -vcl {
+
+	backend default {
+		.path = "${s1_sock}";
+		.max_connections = 1;
+	}
+	sub vcl_recv {
+		return(pass);
+	}
+} -start
+
+client c1 {
+	txreq
+	rxresp
+	expect resp.status == 200
+} -start
+
+
+client c2 {
+	barrier b1 sync
+	txreq
+	rxresp
+	expect resp.status == 503
+} -run
+
+barrier b2 sync
+client c1 -wait
+
+varnish v1 -expect backend_busy == 1
diff --git a/bin/varnishtest/tests/v00056.vtc b/bin/varnishtest/tests/v00056.vtc
new file mode 100644
index 0000000..7d3d507
--- /dev/null
+++ b/bin/varnishtest/tests/v00056.vtc
@@ -0,0 +1,70 @@
+varnishtest "Check req.backend.healthy with UDS backends"
+
+barrier b1 cond 2
+barrier b2 cond 2
+barrier b3 cond 2
+barrier b4 cond 2
+
+server s1 -listen "${tmpdir}/s1.sock" {
+	rxreq
+	barrier b1 sync
+	expect req.url == "/"
+	txresp -body "slash"
+	accept
+	rxreq
+	barrier b2 sync
+	barrier b3 sync
+	expect req.url == "/"
+	txresp -body "slash"
+	accept
+	barrier b4 sync
+} -start
+
+varnish v1 -vcl {
+
+	import std;
+
+	probe foo {
+		.url = "/";
+		.timeout = 1s;
+		.interval = 1s;
+		.window = 3;
+		.threshold = 2;
+		.initial = 0;
+	}
+
+	backend default {
+		.path = "${s1_sock}";
+		.max_connections = 1;
+		.probe = foo;
+	}
+
+	sub vcl_recv {
+		if (std.healthy(default)) {
+			return(synth(200,"Backend healthy"));
+		} else {
+			return(synth(500,"Backend sick"));
+		}
+	}
+} -start
+
+varnish v1 -cliok "backend.list -p"
+
+client c1 {
+	txreq
+	rxresp
+	expect resp.status == 500
+
+	barrier b1 sync
+
+	barrier b2 sync
+	txreq
+	rxresp
+	expect resp.status == 500
+
+	barrier b3 sync
+	barrier b4 sync
+	txreq
+	rxresp
+	expect resp.status == 200
+} -run
diff --git a/bin/varnishtest/tests/v00057.vtc b/bin/varnishtest/tests/v00057.vtc
new file mode 100644
index 0000000..58d8de3
--- /dev/null
+++ b/bin/varnishtest/tests/v00057.vtc
@@ -0,0 +1,41 @@
+varnishtest "Test backend .hosthdr with UDS backends"
+
+server s1 -listen "${tmpdir}/s1.sock" {
+	rxreq
+	expect req.url == "/foo"
+	expect req.http.host == "snafu"
+	txresp -body "foo1"
+
+	rxreq
+	expect req.url == "/bar"
+	expect req.http.host == "0.0.0.0"
+	txresp -body "foo1"
+} -start
+
+varnish v1 -vcl+backend { } -start
+
+client c1 {
+	txreq -url "/foo" -hdr "Host: snafu"
+	rxresp
+	txreq -url "/bar"
+	rxresp
+} -run
+
+server s2 -listen "${tmpdir}/s2.sock" {
+	rxreq
+	expect req.url == "/barf"
+	expect req.http.host == "FOObar"
+	txresp -body "foo1"
+} -start
+
+varnish v1 -vcl {
+	backend b1 {
+		.path = "${s2_sock}";
+		.host_header = "FOObar";
+	}
+}
+
+client c1 {
+	txreq -url "/barf"
+	rxresp
+} -run
diff --git a/bin/varnishtest/vtc_server.c b/bin/varnishtest/vtc_server.c
index 9c084ba..9496046 100644
--- a/bin/varnishtest/vtc_server.c
+++ b/bin/varnishtest/vtc_server.c
@@ -392,9 +392,15 @@ cmd_server_genvcl(struct vsb *vsb)
 
 	AZ(pthread_mutex_lock(&server_mtx));
 	VTAILQ_FOREACH(s, &servers, list) {
-		VSB_printf(vsb,
-		    "backend %s { .host = \"%s\"; .port = \"%s\"; }\n",
-		    s->name, s->aaddr, s->aport);
+		if (*s->listen != '/')
+			VSB_printf(vsb,
+				   "backend %s { .host = \"%s\"; "
+				   ".port = \"%s\"; }\n",
+				   s->name, s->aaddr, s->aport);
+		else
+			VSB_printf(vsb,
+				   "backend %s { .path = \"%s\"; }\n",
+				   s->name, s->listen);
 	}
 	AZ(pthread_mutex_unlock(&server_mtx));
 }
diff --git a/include/tbl/backend_poll.h b/include/tbl/backend_poll.h
index a6fa339..5c1e207 100644
--- a/include/tbl/backend_poll.h
+++ b/include/tbl/backend_poll.h
@@ -31,6 +31,7 @@
 
 BITMAP(good_ipv4, '4', "Good IPv4", 0)
 BITMAP(good_ipv6, '6', "Good IPv6", 0)
+BITMAP(good_unix, 'U', "Good UNIX", 0)
 BITMAP( err_xmit, 'x', "Error Xmit", 0)
 BITMAP(good_xmit, 'X', "Good Xmit", 0)
 BITMAP( err_recv, 'r', "Error Recv", 0)
diff --git a/include/vrt.h b/include/vrt.h
index c29f8d4..5a25e6b 100644
--- a/include/vrt.h
+++ b/include/vrt.h
@@ -53,6 +53,7 @@
  *
  *
  * 6.2 (scheduled for: 2018-03-15)
+ *	path field added to struct vrt_backend
  *	VRT_Healthy() added
  *	VRT_VSC_Alloc() added
  *	VRT_VSC_Destroy() added
@@ -253,6 +254,7 @@ extern const void * const vrt_magic_string_unset;
 	rigid char			*ipv4_addr;		\
 	rigid char			*ipv6_addr;		\
 	rigid char			*port;			\
+	rigid char			*path;			\
 	rigid char			*hosthdr;		\
 	double				connect_timeout;	\
 	double				first_byte_timeout;	\
@@ -266,6 +268,7 @@ extern const void * const vrt_magic_string_unset;
 		DA(ipv4_addr);			\
 		DA(ipv6_addr);			\
 		DA(port);			\
+		DA(path);			\
 		DA(hosthdr);			\
 		DN(connect_timeout);		\
 		DN(first_byte_timeout);		\
diff --git a/include/vus.h b/include/vus.h
index 3747d92..ddaf331 100644
--- a/include/vus.h
+++ b/include/vus.h
@@ -33,3 +33,4 @@ typedef int vus_resolved_f(void *priv, const struct sockaddr_un *);
 int VUS_resolver(const char *path, vus_resolved_f *func, void *priv,
 		 const char **err);
 int VUS_bind(const struct sockaddr_un *uds, const char **errp);
+int VUS_connect(const char *path, int msec);
diff --git a/lib/libvarnish/vus.c b/lib/libvarnish/vus.c
index 8471d15..69a223d 100644
--- a/lib/libvarnish/vus.c
+++ b/lib/libvarnish/vus.c
@@ -32,10 +32,12 @@
 #include <unistd.h>
 #include <errno.h>
 #include <string.h>
+#include <poll.h>
 
 #include "vdef.h"
 #include "vas.h"
 #include "vus.h"
+#include "vtcp.h"
 
 int
 VUS_resolver(const char *path, vus_resolved_f *func, void *priv,
@@ -95,3 +97,60 @@ VUS_bind(const struct sockaddr_un *uds, const char **errp)
 	}
 	return (sd);
 }
+
+int
+VUS_connect(const char *path, int msec)
+{
+	int s, i;
+	struct pollfd fds[1];
+	struct sockaddr_un uds;
+	socklen_t sl = (socklen_t) sizeof(uds);
+
+	if (path == NULL)
+		return (-1);
+	/* Attempt the connect */
+	assert(strlen(path) + 1 <= sizeof(uds.sun_path));
+	uds.sun_family = PF_UNIX;
+	strcpy(uds.sun_path, path);
+	AN(sl);
+
+	s = socket(PF_UNIX, SOCK_STREAM, 0);
+	if (s < 0)
+		return (s);
+
+	/* Set the socket non-blocking */
+	if (msec != 0)
+		(void)VTCP_nonblocking(s);
+
+	i = connect(s, (const struct sockaddr *)&uds, sl);
+	if (i == 0)
+		return (s);
+	if (errno != EINPROGRESS) {
+		closefd(&s);
+		return (-1);
+	}
+
+	if (msec < 0) {
+		/*
+		 * Caller is responsible for waiting and
+		 * calling VTCP_connected
+		 */
+		return (s);
+	}
+
+	assert(msec > 0);
+	/* Exercise our patience, polling for write */
+	fds[0].fd = s;
+	fds[0].events = POLLWRNORM;
+	fds[0].revents = 0;
+	i = poll(fds, 1, msec);
+
+	if (i == 0) {
+		/* Timeout, close and give up */
+		closefd(&s);
+		errno = ETIMEDOUT;
+		return (-1);
+	}
+
+	return (VTCP_connected(s));
+}
diff --git a/lib/libvcc/vcc_backend.c b/lib/libvcc/vcc_backend.c
index db5ce71..640481d 100644
--- a/lib/libvcc/vcc_backend.c
+++ b/lib/libvcc/vcc_backend.c
@@ -81,20 +81,19 @@ Emit_Sockaddr(struct vcc *tl, const struct token *t_host,
 		Fb(tl, 0, "\t.ipv6_addr = \"%s\",\n", ipv6a);
 	}
 	Fb(tl, 0, "\t.port = \"%s\",\n", pa);
+	Fb(tl, 0, "\t.path = (void *) 0,\n");
 }
 
 /*--------------------------------------------------------------------
- * Parse a backend probe specification
+ * Disallow mutually exclusive field definitions
  */
 
 static void
-vcc_ProbeRedef(struct vcc *tl, struct token **t_did,
+vcc_Redef(struct vcc *tl, const char *redef, struct token **t_did,
     struct token *t_field)
 {
-	/* .url and .request are mutually exclusive */
-
 	if (*t_did != NULL) {
-		VSB_printf(tl->sb, "Probe request redefinition at:\n");
+		VSB_printf(tl->sb, "%s redefinition at:\n", redef);
 		vcc_ErrWhere(tl, t_field);
 		VSB_printf(tl->sb, "Previous definition:\n");
 		vcc_ErrWhere(tl, *t_did);
@@ -103,6 +102,10 @@ vcc_ProbeRedef(struct vcc *tl, struct token **t_did,
 	*t_did = t_field;
 }
 
+/*--------------------------------------------------------------------
+ * Parse a backend probe specification
+ */
+
 static void
 vcc_ParseProbeSpec(struct vcc *tl, const struct symbol *sym, char **name)
 {
@@ -152,7 +155,7 @@ vcc_ParseProbeSpec(struct vcc *tl, const struct symbol *sym, char **name)
 		vcc_IsField(tl, &t_field, fs);
 		ERRCHK(tl);
 		if (vcc_IdIs(t_field, "url")) {
-			vcc_ProbeRedef(tl, &t_did, t_field);
+			vcc_Redef(tl, "Probe request", &t_did, t_field);
 			ERRCHK(tl);
 			ExpectErr(tl, CSTR);
 			Fh(tl, 0, "\t.url = ");
@@ -160,7 +163,7 @@ vcc_ParseProbeSpec(struct vcc *tl, const struct symbol *sym, char **name)
 			Fh(tl, 0, ",\n");
 			vcc_NextToken(tl);
 		} else if (vcc_IdIs(t_field, "request")) {
-			vcc_ProbeRedef(tl, &t_did, t_field);
+			vcc_Redef(tl, "Probe request", &t_did, t_field);
 			ERRCHK(tl);
 			ExpectErr(tl, CSTR);
 			Fh(tl, 0, "\t.request =\n");
@@ -294,8 +297,10 @@ vcc_ParseHostDef(struct vcc *tl, const struct token *t_be, const char *vgcname)
 	struct token *t_val;
 	struct token *t_host = NULL;
 	struct token *t_port = NULL;
+	struct token *t_path = NULL;
 	struct token *t_hosthdr = NULL;
 	struct symbol *pb;
+	struct token *t_did = NULL;
 	struct fld_spec *fs;
 	struct inifin *ifp;
 	struct vsb *vsb;
@@ -304,8 +309,9 @@ vcc_ParseHostDef(struct vcc *tl, const struct token *t_be, const char *vgcname)
 	double t;
 
 	fs = vcc_FldSpec(tl,
-	    "!host",
+	    "?host",
 	    "?port",
+	    "?path",
 	    "?host_header",
 	    "?connect_timeout",
 	    "?first_byte_timeout",
@@ -345,6 +351,8 @@ vcc_ParseHostDef(struct vcc *tl, const struct token *t_be, const char *vgcname)
 		vcc_IsField(tl, &t_field, fs);
 		ERRCHK(tl);
 		if (vcc_IdIs(t_field, "host")) {
+			vcc_Redef(tl, "Address", &t_did, t_field);
+			ERRCHK(tl);
 			ExpectErr(tl, CSTR);
 			assert(tl->t->dec != NULL);
 			t_host = tl->t;
@@ -356,6 +364,14 @@ vcc_ParseHostDef(struct vcc *tl, const struct token *t_be, const char *vgcname)
 			t_port = tl->t;
 			vcc_NextToken(tl);
 			SkipToken(tl, ';');
+		} else if (vcc_IdIs(t_field, "path")) {
+			vcc_Redef(tl, "Address", &t_did, t_field);
+			ERRCHK(tl);
+			ExpectErr(tl, CSTR);
+			assert(tl->t->dec != NULL);
+			t_path = tl->t;
+			vcc_NextToken(tl);
+			SkipToken(tl, ';');
 		} else if (vcc_IdIs(t_field, "host_header")) {
 			ExpectErr(tl, CSTR);
 			assert(tl->t->dec != NULL);
@@ -430,9 +446,19 @@ vcc_ParseHostDef(struct vcc *tl, const struct token *t_be, const char *vgcname)
 	vcc_FieldsOk(tl, fs);
 	ERRCHK(tl);
 
-	/* Check that the hostname makes sense */
-	assert(t_host != NULL);
-	Emit_Sockaddr(tl, t_host, t_port);
+	if (t_host == NULL && t_path == NULL) {
+		VSB_printf(tl->sb, "Expected .host or .path.\n");
+		vcc_ErrWhere(tl, t_be);
+		return;
+	}
+
+	assert(t_host != NULL || t_path != NULL);
+	if (t_host != NULL)
+		/* Check that the hostname makes sense */
+		Emit_Sockaddr(tl, t_host, t_port);
+	else
+		/* Check that the path can be a legal UDS */
+		Emit_UDS_Path(tl, t_path, "Backend path");
 	ERRCHK(tl);
 
 	ExpectErr(tl, '}');
@@ -440,11 +466,14 @@ vcc_ParseHostDef(struct vcc *tl, const struct token *t_be, const char *vgcname)
 	/* We have parsed it all, emit the ident string */
 
 	/* Emit the hosthdr field, fall back to .host if not specified */
+	/* If .path is specified, set "0.0.0.0". */
 	Fb(tl, 0, "\t.hosthdr = ");
 	if (t_hosthdr != NULL)
 		EncToken(tl->fb, t_hosthdr);
-	else
+	else if (t_host != NULL)
 		EncToken(tl->fb, t_host);
+	else
+		Fb(tl, 0, "\"0.0.0.0\"");
 	Fb(tl, 0, ",\n");
 
 	/* Close the struct */
diff --git a/lib/libvcc/vcc_compile.h b/lib/libvcc/vcc_compile.h
index 2d9478b..f530095 100644
--- a/lib/libvcc/vcc_compile.h
+++ b/lib/libvcc/vcc_compile.h
@@ -327,6 +327,8 @@ void Resolve_Sockaddr(struct vcc *tl, const char *host, const char *defport,
     const char **ipv4, const char **ipv4_ascii, const char **ipv6,
     const char **ipv6_ascii, const char **p_ascii, int maxips,
     const struct token *t_err, const char *errid);
+void Emit_UDS_Path(struct vcc *tl, const struct token *t_path,
+    const char *errid);
 double vcc_TimeUnit(struct vcc *);
 void vcc_ByteVal(struct vcc *, double *);
 void vcc_NumVal(struct vcc *, double *, int *);
diff --git a/lib/libvcc/vcc_expr.c b/lib/libvcc/vcc_expr.c
index f67b141..17209dc 100644
--- a/lib/libvcc/vcc_expr.c
+++ b/lib/libvcc/vcc_expr.c
@@ -729,6 +729,19 @@ vcc_expr4(struct vcc *tl, struct expr **e, vcc_type_t fmt)
 	case CSTR:
 		assert(fmt != VOID);
 		if (fmt == IP) {
+			if (*tl->t->dec == '/') {
+				/*
+				 * On some platforms (e.g. FreeBSD),
+				 * getaddrinfo(3) may resolve a path to a
+				 * sockaddr_un if it happens to exist and
+				 * is a socket. So don't let that happen.
+				 */
+				VSB_printf(tl->sb,
+					"Cannot convert to an IP address: ");
+				vcc_ErrToken(tl, tl->t);
+				vcc_ErrWhere(tl, tl->t);
+				return;
+			}
 			Resolve_Sockaddr(tl, tl->t->dec, "80",
 			    &ip, NULL, &ip, NULL, NULL, 1,
 			    tl->t, "IP constant");
diff --git a/lib/libvcc/vcc_utils.c b/lib/libvcc/vcc_utils.c
index a2ae14d..987eb98 100644
--- a/lib/libvcc/vcc_utils.c
+++ b/lib/libvcc/vcc_utils.c
@@ -33,6 +33,10 @@
 #include <string.h>
 #include <stdlib.h>
 #include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
 
 #include "vcc_compile.h"
 
@@ -161,6 +165,7 @@ rs_callback(void *priv, const struct suckaddr *vsa)
 	assert(VSA_Sane(vsa));
 
 	v = VSA_Get_Proto(vsa);
+	assert(v != AF_UNIX);
 	VTCP_name(vsa, a, sizeof a, p, sizeof p);
 	VSB_printf(rss->vsb, "\t%s:%s\n", a, p);
 	if (v == AF_INET) {
@@ -252,6 +257,42 @@ Resolve_Sockaddr(struct vcc *tl,
 	FREE_OBJ(rss);
 }
 
+/*
+ * For UDS, we do not create a VSA. Check if it's an absolute path, can
+ * be accessed, and is a socket. If so, just emit the path field and set
+ * the IP suckaddrs to NULL.
+ */
+void
+Emit_UDS_Path(struct vcc *tl, const struct token *t_path, const char *errid)
+{
+	struct stat st;
+
+	AN(t_path);
+	AN(t_path->dec);
+
+	if (*t_path->dec != '/') {
+		VSB_printf(tl->sb,
+			   "%s: Must be an absolute path:\n", errid);
+		vcc_ErrWhere(tl, t_path);
+		return;
+	}
+	errno = 0;
+	if (stat(t_path->dec, &st) != 0) {
+		VSB_printf(tl->sb, "%s: Cannot stat: %s\n", errid,
+			   strerror(errno));
+		vcc_ErrWhere(tl, t_path);
+		return;
+	}
+	if (! S_ISSOCK(st.st_mode)) {
+		VSB_printf(tl->sb, "%s: Not a socket:\n", errid);
+		vcc_ErrWhere(tl, t_path);
+		return;
+	}
+	Fb(tl, 0, "\t.path = \"%s\",\n", t_path->dec);
+	Fb(tl, 0, "\t.ipv4_suckaddr = (void *) 0,\n");
+	Fb(tl, 0, "\t.ipv6_suckaddr = (void *) 0,\n");
+}
+
 /*--------------------------------------------------------------------
  * Recognize and convert units of time, return seconds.
  */


More information about the varnish-commit mailing list