[master] a2682c0 Support Unix domain sockets as -a listen addresses.

Geoff Simmons geoff at uplex.de
Thu Feb 22 09:23:09 UTC 2018


commit a2682c0951d6d7d441939574d0b7e0a85865119f
Author: Geoff Simmons <geoff at uplex.de>
Date:   Fri Feb 16 19:56:43 2018 +0100

    Support Unix domain sockets as -a listen addresses.
    
    Also adds the user, group and mode sub-args to -a, to set
    permissions on the path created by -a for UDS.
    
    Add the bogo_ip pseudo-VSA, representing IPv4 0.0.0.0:0, to be
    exposed in VCL for non-IP addresses.
    
    Also adding the field listen_sock to struct sess: pointer to the
    struct listen_sock that was created by the acceptor and lives in
    heritage.socks. This makes information like the endpoint name
    (named -a arg) and the UDS path available from an sp.

diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h
index ed5602f..a8aadd1 100644
--- a/bin/varnishd/cache/cache.h
+++ b/bin/varnishd/cache/cache.h
@@ -99,6 +99,7 @@ struct poolparam;
 struct sess;
 struct transport;
 struct worker;
+struct listen_sock;
 
 #define DIGEST_LEN		32
 
@@ -571,6 +572,7 @@ struct sess {
 #define SESS_MAGIC		0x2c2f9c5a
 
 	uint16_t		sattr[SA_LAST];
+	struct listen_sock	*listen_sock;
 	int			refcnt;
 	int			fd;
 	uint32_t		vxid;
diff --git a/bin/varnishd/cache/cache_acceptor.c b/bin/varnishd/cache/cache_acceptor.c
index bb1f000..b54c99a 100644
--- a/bin/varnishd/cache/cache_acceptor.c
+++ b/bin/varnishd/cache/cache_acceptor.c
@@ -37,6 +37,8 @@
 #include <stdlib.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 
 #include "cache_varnishd.h"
 
@@ -86,25 +88,26 @@ static struct tcp_opt {
 	socklen_t	sz;
 	void		*ptr;
 	int		need;
+	int		iponly;
 } tcp_opts[] = {
-#define TCPO(lvl, nam, sz) { lvl, nam, #nam, sizeof(sz), 0, 0},
+#define TCPO(lvl, nam, sz, ip) { lvl, nam, #nam, sizeof(sz), 0, 0, ip},
 
-	TCPO(SOL_SOCKET, SO_LINGER, struct linger)
-	TCPO(SOL_SOCKET, SO_KEEPALIVE, int)
-	TCPO(IPPROTO_TCP, TCP_NODELAY, int)
+	TCPO(SOL_SOCKET, SO_LINGER, struct linger, 0)
+	TCPO(SOL_SOCKET, SO_KEEPALIVE, int, 0)
+	TCPO(IPPROTO_TCP, TCP_NODELAY, int, 1)
 
 #ifdef SO_SNDTIMEO_WORKS
-	TCPO(SOL_SOCKET, SO_SNDTIMEO, struct timeval)
+	TCPO(SOL_SOCKET, SO_SNDTIMEO, struct timeval, 0)
 #endif
 
 #ifdef SO_RCVTIMEO_WORKS
-	TCPO(SOL_SOCKET, SO_RCVTIMEO, struct timeval)
+	TCPO(SOL_SOCKET, SO_RCVTIMEO, struct timeval, 0)
 #endif
 
 #ifdef HAVE_TCP_KEEP
-	TCPO(IPPROTO_TCP, TCP_KEEPIDLE, int)
-	TCPO(IPPROTO_TCP, TCP_KEEPCNT, int)
-	TCPO(IPPROTO_TCP, TCP_KEEPINTVL, int)
+	TCPO(IPPROTO_TCP, TCP_KEEPIDLE, int, 1)
+	TCPO(IPPROTO_TCP, TCP_KEEPCNT, int, 1)
+	TCPO(IPPROTO_TCP, TCP_KEEPINTVL, int, 1)
 #endif
 
 #undef TCPO
@@ -218,15 +221,18 @@ vca_tcp_opt_init(void)
 }
 
 static void
-vca_tcp_opt_test(int sock)
+vca_tcp_opt_test(struct listen_sock *ls)
 {
-	int i, n;
+	int i, n, sock;
 	struct tcp_opt *to;
 	socklen_t l;
 	void *ptr;
 
+	sock = ls->sock;
 	for (n = 0; n < n_tcp_opts; n++) {
 		to = &tcp_opts[n];
+		if (to->iponly && ls->uds)
+			continue;
 		to->need = 1;
 		ptr = calloc(1, to->sz);
 		AN(ptr);
@@ -241,13 +247,16 @@ vca_tcp_opt_test(int sock)
 }
 
 static void
-vca_tcp_opt_set(int sock, int force)
+vca_tcp_opt_set(struct listen_sock *ls, int force)
 {
-	int n;
+	int n, sock;
 	struct tcp_opt *to;
 
+	sock = ls->sock;
 	for (n = 0; n < n_tcp_opts; n++) {
 		to = &tcp_opts[n];
+		if (to->iponly && ls->uds)
+			continue;
 		if (to->need || force) {
 			VTCP_Assert(setsockopt(sock,
 			    to->level, to->optname, to->ptr, to->sz));
@@ -304,15 +313,57 @@ vca_pace_good(void)
  * Called from assigned worker thread
  */
 
+static void
+vca_mk_tcp(struct wrk_accept *wa, struct sess *sp, char *laddr, char *lport,
+	   char *raddr, char *rport)
+{
+	struct suckaddr *sa;
+	struct sockaddr_storage ss;
+	socklen_t sl;
+
+	SES_Reserve_remote_addr(sp, &sa);
+	AN(VSA_Build(sa, &wa->acceptaddr, wa->acceptaddrlen));
+	sp->sattr[SA_CLIENT_ADDR] = sp->sattr[SA_REMOTE_ADDR];
+
+	VTCP_name(sa, raddr, VTCP_ADDRBUFSIZE, rport, VTCP_PORTBUFSIZE);
+	SES_Set_String_Attr(sp, SA_CLIENT_IP, raddr);
+	SES_Set_String_Attr(sp, SA_CLIENT_PORT, rport);
+
+	sl = sizeof ss;
+	AZ(getsockname(sp->fd, (void*)&ss, &sl));
+	SES_Reserve_local_addr(sp, &sa);
+	AN(VSA_Build(sa, &ss, sl));
+	sp->sattr[SA_SERVER_ADDR] = sp->sattr[SA_LOCAL_ADDR];
+	VTCP_name(sa, laddr, VTCP_ADDRBUFSIZE, lport, VTCP_PORTBUFSIZE);
+}
+
+static void
+vca_mk_uds(struct wrk_accept *wa, struct sess *sp, char *laddr, char *lport,
+	   char *raddr, char *rport)
+{
+	struct suckaddr *sa;
+
+	(void) wa;
+	SES_Reserve_remote_addr(sp, &sa);
+	SES_Set_remote_addr(sp, bogo_ip);
+	sp->sattr[SA_CLIENT_ADDR] = sp->sattr[SA_REMOTE_ADDR];
+	sp->sattr[SA_LOCAL_ADDR] = sp->sattr[SA_REMOTE_ADDR];
+	sp->sattr[SA_SERVER_ADDR] = sp->sattr[SA_REMOTE_ADDR];
+	SES_Set_String_Attr(sp, SA_CLIENT_IP, "0.0.0.0");
+	SES_Set_String_Attr(sp, SA_CLIENT_PORT, "0");
+
+	strcpy(laddr, "0.0.0.0");
+	strcpy(raddr, "0.0.0.0");
+	strcpy(lport, "0");
+	strcpy(rport, "0");
+}
+
 static void v_matchproto_(task_func_t)
 vca_make_session(struct worker *wrk, void *arg)
 {
 	struct sess *sp;
 	struct req *req;
 	struct wrk_accept *wa;
-	struct sockaddr_storage ss;
-	struct suckaddr *sa;
-	socklen_t sl;
 	char laddr[VTCP_ADDRBUFSIZE];
 	char lport[VTCP_PORTBUFSIZE];
 	char raddr[VTCP_ADDRBUFSIZE];
@@ -353,24 +404,16 @@ vca_make_session(struct worker *wrk, void *arg)
 
 	sp->fd = wa->acceptsock;
 	wa->acceptsock = -1;
+	sp->listen_sock = wa->acceptlsock;
 
 	assert(wa->acceptaddrlen <= vsa_suckaddr_len);
-	SES_Reserve_remote_addr(sp, &sa);
-	AN(VSA_Build(sa, &wa->acceptaddr, wa->acceptaddrlen));
-	sp->sattr[SA_CLIENT_ADDR] = sp->sattr[SA_REMOTE_ADDR];
-
-	VTCP_name(sa, raddr, sizeof raddr, rport, sizeof rport);
-	SES_Set_String_Attr(sp, SA_CLIENT_IP, raddr);
-	SES_Set_String_Attr(sp, SA_CLIENT_PORT, rport);
-
-	sl = sizeof ss;
-	AZ(getsockname(sp->fd, (void*)&ss, &sl));
-	SES_Reserve_local_addr(sp, &sa);
-	AN(VSA_Build(sa, &ss, sl));
-	sp->sattr[SA_SERVER_ADDR] = sp->sattr[SA_LOCAL_ADDR];
 
-	VTCP_name(sa, laddr, sizeof laddr, lport, sizeof lport);
+	if (wa->acceptlsock->uds)
+		vca_mk_uds(wa, sp, laddr, lport, raddr, rport);
+	else
+		vca_mk_tcp(wa, sp, laddr, lport, raddr, rport);
 
+	AN(wa->acceptlsock->name);
 	VSL(SLT_Begin, sp->vxid, "sess 0 %s",
 	    wa->acceptlsock->transport->name);
 	VSL(SLT_SessOpen, sp->vxid, "%s %s %s %s %s %.6f %d",
@@ -383,10 +426,10 @@ vca_make_session(struct worker *wrk, void *arg)
 	wrk->stats->sess_conn++;
 
 	if (need_test) {
-		vca_tcp_opt_test(sp->fd);
+		vca_tcp_opt_test(wa->acceptlsock);
 		need_test = 0;
 	}
-	vca_tcp_opt_set(sp->fd, 0);
+	vca_tcp_opt_set(wa->acceptlsock, 0);
 
 	req = Req_New(wrk, sp);
 	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
@@ -562,7 +605,7 @@ vca_acct(void *arg)
 				    ls->sock, i, strerror(errno));
 		}
 		AZ(listen(ls->sock, cache_param->listen_depth));
-		vca_tcp_opt_set(ls->sock, 1);
+		vca_tcp_opt_set(ls, 1);
 		if (cache_param->accept_filter) {
 			int i;
 			i = VTCP_filter_http(ls->sock);
@@ -585,7 +628,7 @@ vca_acct(void *arg)
 				if (ls->sock == -2)
 					continue;	// VCA_Shutdown
 				assert (ls->sock > 0);
-				vca_tcp_opt_set(ls->sock, 1);
+				vca_tcp_opt_set(ls, 1);
 			}
 			AZ(pthread_mutex_unlock(&shut_mtx));
 		}
diff --git a/bin/varnishd/cache/cache_vrt_var.c b/bin/varnishd/cache/cache_vrt_var.c
index ef6da50..2ab2fdb 100644
--- a/bin/varnishd/cache/cache_vrt_var.c
+++ b/bin/varnishd/cache/cache_vrt_var.c
@@ -250,7 +250,6 @@ VRT_r_beresp_uncacheable(VRT_CTX)
 const char *
 VRT_r_client_identity(VRT_CTX)
 {
-
 	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
 	CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC);
 	if (ctx->req->client_identity != NULL)
diff --git a/bin/varnishd/common/heritage.h b/bin/varnishd/common/heritage.h
index 64131d6..7903cf6 100644
--- a/bin/varnishd/common/heritage.h
+++ b/bin/varnishd/common/heritage.h
@@ -35,6 +35,7 @@ struct suckaddr;
 struct listen_sock;
 struct transport;
 struct VCLS;
+struct uds_perms;
 
 struct listen_sock {
 	unsigned			magic;
@@ -42,10 +43,12 @@ struct listen_sock {
 	VTAILQ_ENTRY(listen_sock)	list;
 	VTAILQ_ENTRY(listen_sock)	arglist;
 	int				sock;
+	int				uds;
 	char				*endpoint;
 	const char			*name;
 	struct suckaddr			*addr;
 	const struct transport		*transport;
+	const struct uds_perms		*perms;
 };
 
 VTAILQ_HEAD(listen_sock_head, listen_sock);
diff --git a/bin/varnishd/mgt/mgt_acceptor.c b/bin/varnishd/mgt/mgt_acceptor.c
index af09013..64dad07 100644
--- a/bin/varnishd/mgt/mgt_acceptor.c
+++ b/bin/varnishd/mgt/mgt_acceptor.c
@@ -33,11 +33,15 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
 
 #include "mgt/mgt.h"
 #include "common/heritage.h"
@@ -47,6 +51,7 @@
 #include "vsa.h"
 #include "vss.h"
 #include "vtcp.h"
+#include "vus.h"
 
 struct listen_arg {
 	unsigned			magic;
@@ -56,6 +61,15 @@ struct listen_arg {
 	const char			*name;
 	VTAILQ_HEAD(,listen_sock)	socks;
 	const struct transport		*transport;
+	const struct uds_perms		*perms;
+};
+
+struct uds_perms {
+	unsigned	magic;
+#define UDS_PERMS_MAGIC 0x84fb5635
+	mode_t		mode;
+	uid_t		uid;
+	gid_t		gid;
 };
 
 static VTAILQ_HEAD(,listen_arg) listen_args =
@@ -65,18 +79,35 @@ static int
 mac_opensocket(struct listen_sock *ls)
 {
 	int fail;
+	struct sockaddr_un uds;
 
 	CHECK_OBJ_NOTNULL(ls, LISTEN_SOCK_MAGIC);
 	if (ls->sock > 0) {
 		MCH_Fd_Inherit(ls->sock, NULL);
 		closefd(&ls->sock);
 	}
-	ls->sock = VTCP_bind(ls->addr, NULL);
+	if (!ls->uds)
+		ls->sock = VTCP_bind(ls->addr, NULL);
+	else {
+		uds.sun_family = PF_UNIX;
+		strcpy(uds.sun_path, ls->endpoint);
+		ls->sock = VUS_bind(&uds, NULL);
+	}
 	fail = errno;
 	if (ls->sock < 0) {
 		AN(fail);
 		return (fail);
 	}
+	if (ls->perms != NULL) {
+		CHECK_OBJ(ls->perms, UDS_PERMS_MAGIC);
+		assert(ls->uds);
+		errno = 0;
+		if (ls->perms->mode != 0
+		    && chmod(ls->endpoint, ls->perms->mode) != 0)
+			return errno;
+		if (chown(ls->endpoint, ls->perms->uid, ls->perms->gid) != 0)
+			return errno;
+	}
 	MCH_Fd_Inherit(ls->sock, "sock");
 	return (0);
 }
@@ -109,22 +140,12 @@ MAC_reopen_sockets(void)
 
 /*--------------------------------------------------------------------*/
 
-static int v_matchproto_(vss_resolved_f)
-mac_callback(void *priv, const struct suckaddr *sa)
+static struct listen_sock *
+mk_listen_sock(struct listen_arg *la, const struct suckaddr *sa)
 {
-	struct listen_arg *la;
 	struct listen_sock *ls;
-	char abuf[VTCP_ADDRBUFSIZE], pbuf[VTCP_PORTBUFSIZE];
-	char nbuf[VTCP_ADDRBUFSIZE+VTCP_PORTBUFSIZE+2];
 	int fail;
 
-	CAST_OBJ_NOTNULL(la, priv, LISTEN_ARG_MAGIC);
-
-	VTAILQ_FOREACH(ls, &heritage.socks, list) {
-		if (!VSA_Compare(sa, ls->addr))
-			ARGV_ERR("-a arguments %s and %s have same address\n",
-			    ls->endpoint, la->endpoint);
-	}
 	ALLOC_OBJ(ls, LISTEN_SOCK_MAGIC);
 	AN(ls);
 	ls->sock = -1;
@@ -134,6 +155,9 @@ mac_callback(void *priv, const struct suckaddr *sa)
 	AN(ls->endpoint);
 	ls->name = la->name;
 	ls->transport = la->transport;
+	ls->perms = la->perms;
+	if (*la->endpoint == '/')
+		ls->uds = 1;
 	VJ_master(JAIL_MASTER_PRIVPORT);
 	fail = mac_opensocket(ls);
 	VJ_master(JAIL_MASTER_LOW);
@@ -144,8 +168,30 @@ mac_callback(void *priv, const struct suckaddr *sa)
 		if (fail != EAFNOSUPPORT)
 			ARGV_ERR("Could not get socket %s: %s\n",
 			    la->endpoint, strerror(fail));
-		return(0);
+		return(NULL);
 	}
+	return(ls);
+}
+
+static int v_matchproto_(vss_resolved_f)
+mac_tcp(void *priv, const struct suckaddr *sa)
+{
+	struct listen_arg *la;
+	struct listen_sock *ls;
+	char abuf[VTCP_ADDRBUFSIZE], pbuf[VTCP_PORTBUFSIZE];
+	char nbuf[VTCP_ADDRBUFSIZE+VTCP_PORTBUFSIZE+2];
+
+	CAST_OBJ_NOTNULL(la, priv, LISTEN_ARG_MAGIC);
+
+	VTAILQ_FOREACH(ls, &heritage.socks, list) {
+		if (!ls->uds && !VSA_Compare(sa, ls->addr))
+			ARGV_ERR("-a arguments %s and %s have same address\n",
+			    ls->endpoint, la->endpoint);
+	}
+	ls = mk_listen_sock(la, sa);
+	if (ls == NULL)
+		return(0);
+	AZ(ls->uds);
 	if (VSA_Port(ls->addr) == 0) {
 		/*
 		 * If the argv port number is zero, we adopt whatever
@@ -164,6 +210,28 @@ mac_callback(void *priv, const struct suckaddr *sa)
 	return (0);
 }
 
+static int v_matchproto_(vus_resolved_f)
+mac_uds(void *priv, const struct sockaddr_un *uds)
+{
+	struct listen_arg *la;
+	struct listen_sock *ls;
+
+	CAST_OBJ_NOTNULL(la, priv, LISTEN_ARG_MAGIC);
+
+	VTAILQ_FOREACH(ls, &heritage.socks, list) {
+		if (ls->uds && strcmp(uds->sun_path, ls->endpoint) == 0)
+			ARGV_ERR("-a arguments %s and %s have same address\n",
+			    ls->endpoint, la->endpoint);
+	}
+	ls = mk_listen_sock(la, bogo_ip);
+	if (ls == NULL)
+		return(0);
+	AN(ls->uds);
+	VTAILQ_INSERT_TAIL(&la->socks, ls, arglist);
+	VTAILQ_INSERT_TAIL(&heritage.socks, ls, list);
+	return (0);
+}
+
 void
 MAC_Arg(const char *spec)
 {
@@ -171,10 +239,14 @@ MAC_Arg(const char *spec)
 	struct listen_arg *la;
 	const char *err;
 	int error;
-	const struct transport *xp;
+	const struct transport *xp = NULL;
 	const char *name;
 	char name_buf[8];
 	static unsigned seq = 0;
+	struct passwd *pwd = NULL;
+	struct group *grp = NULL;
+	mode_t mode = 0;
+	struct uds_perms *perms;
 
 	av = MGT_NamedArg(spec, &name, "-a");
 	AN(av);
@@ -192,19 +264,107 @@ MAC_Arg(const char *spec)
 	}
 	la->name = name;
 
-	if (av[2] == NULL) {
-		xp = XPORT_Find("http");
-	} else {
-		xp = XPORT_Find(av[2]);
-		if (xp == NULL)
-			ARGV_ERR("Unknown protocol '%s'\n", av[2]);
-		if (av[3] != NULL)
-			ARGV_ERR("Too many sub-arguments to -a(%s)\n", av[2]);
+	if (*la->endpoint != '/' && strchr(la->endpoint, '/') != NULL)
+		ARGV_ERR("Unix domain socket addresses must be absolute paths "
+			 "in -a (%s)\n", la->endpoint);
+
+	for (int i = 2; av[i] != NULL; i++) {
+		char *eq, *val;
+		int len;
+
+		if ((eq = strchr(av[i], '=')) == NULL) {
+			if (xp != NULL)
+				ARGV_ERR("Too many protocol sub-args in -a "
+					 "(%s)\n", av[i]);
+			xp = XPORT_Find(av[i]);
+			if (xp == NULL)
+				ARGV_ERR("Unknown protocol '%s'\n", av[i]);
+			continue;
+		}
+		if (la->endpoint[0] != '/')
+			ARGV_ERR("Invalid sub-arg %s for IP addresses in -a\n",
+				 av[i]);
+
+		val = eq + 1;
+		len = eq - av[i];
+		assert(len >= 0);
+		if (len == 0)
+			ARGV_ERR("Invalid sub-arg %s in -a\n", av[i]);
+
+		if (strncmp(av[i], "user", len) == 0) {
+			if (pwd != NULL)
+				ARGV_ERR("Too many user sub-args in -a (%s)\n",
+					 av[i]);
+			pwd = getpwnam(val);
+			if (pwd == NULL)
+				ARGV_ERR("Unknown user %s in -a\n", val);
+			continue;
+		}
+
+		if (strncmp(av[i], "group", len) == 0) {
+			if (grp != NULL)
+				ARGV_ERR("Too many group sub-args in -a (%s)\n",
+					 av[i]);
+			grp = getgrnam(val);
+			if (grp == NULL)
+				ARGV_ERR("Unknown group %s in -a\n", val);
+			continue;
+		}
+
+		if (strncmp(av[i], "mode", len) == 0) {
+			long m;
+			char *p;
+
+			if (mode != 0)
+				ARGV_ERR("Too many mode sub-args in -a (%s)\n",
+					 av[i]);
+			if (*val == '\0')
+				ARGV_ERR("Empty mode sub-arg in -a\n");
+			errno = 0;
+			m = strtol(val, &p, 8);
+			if (*p != '\0')
+				ARGV_ERR("Invalid mode sub-arg %s in -a\n",
+					 val);
+			if (errno)
+				ARGV_ERR("Cannot parse mode sub-arg %s in -a: "
+					 "%s\n", val, strerror(errno));
+			if (m <= 0 || m > 0777)
+				ARGV_ERR("Mode sub-arg %s out of range in -a\n",
+					 val);
+			mode = (mode_t) m;
+			continue;
+		}
+
+		ARGV_ERR("Invalid sub-arg %s in -a\n", av[i]);
 	}
+
+	if (xp == NULL)
+		xp = XPORT_Find("http");
 	AN(xp);
 	la->transport = xp;
 
-	error = VSS_resolver(av[1], "80", mac_callback, la, &err);
+	if (pwd != NULL || grp != NULL || mode != 0) {
+		ALLOC_OBJ(perms, UDS_PERMS_MAGIC);
+		AN(perms);
+		if (pwd != NULL)
+			perms->uid = pwd->pw_uid;
+		else
+			perms->uid = (uid_t) -1;
+		if (grp != NULL)
+			perms->gid = grp->gr_gid;
+		else
+			perms->gid = (gid_t) -1;
+		perms->mode = mode;
+		la->perms = perms;
+	}
+	else
+		AZ(la->perms);
+
+	if (*la->endpoint != '/')
+		error = VSS_resolver(av[1], "80", mac_tcp, la, &err);
+	else
+		error = VUS_resolver(av[1], mac_uds, la, &err);
+
 	if (VTAILQ_EMPTY(&la->socks) || error)
 		ARGV_ERR("Got no socket(s) for %s\n", av[1]);
 	VAV_Free(av);
diff --git a/bin/varnishd/mgt/mgt_main.c b/bin/varnishd/mgt/mgt_main.c
index 8e968ed..275f6fb 100644
--- a/bin/varnishd/mgt/mgt_main.c
+++ b/bin/varnishd/mgt/mgt_main.c
@@ -41,6 +41,7 @@
 #include <syslog.h>
 #include <unistd.h>
 #include <sys/stat.h>
+#include <sys/socket.h>
 
 #include "mgt/mgt.h"
 #include "common/heritage.h"
@@ -59,6 +60,7 @@
 #include "vsub.h"
 #include "vtim.h"
 #include "waiter/mgt_waiter.h"
+#include "vsa.h"
 
 struct heritage		heritage;
 unsigned		d_flag = 0;
@@ -564,6 +566,9 @@ main(int argc, char * const *argv)
 
 	VJ_Init(j_arg);
 
+	/* Initialize the bogo-IP VSA */
+	VSA_Init();
+
 	optind = 1;
 	optreset = 1;
 	while ((o = getopt(argc, argv, opt_spec)) != -1) {
diff --git a/bin/varnishtest/tests/b00053.vtc b/bin/varnishtest/tests/b00053.vtc
new file mode 100644
index 0000000..09e3d20
--- /dev/null
+++ b/bin/varnishtest/tests/b00053.vtc
@@ -0,0 +1,34 @@
+varnishtest "Does anything get through Unix domain sockets at all ?"
+
+server s1 {
+	rxreq
+	txresp -body "012345\n"
+} -start
+
+varnish v1 -arg "-a ${tmpdir}/v1.sock" -vcl+backend {
+	sub vcl_backend_response {
+		set beresp.do_stream = false;
+	}
+} -start
+
+varnish v1 -cliok "param.set debug +workspace"
+varnish v1 -cliok "param.set debug +witness"
+
+varnish v1 -expect n_object == 0
+varnish v1 -expect sess_conn == 0
+varnish v1 -expect client_req == 0
+varnish v1 -expect cache_miss == 0
+
+client c1 -connect "${tmpdir}/v1.sock" {
+	txreq -url "/"
+	rxresp
+	expect resp.status == 200
+} -run
+
+varnish v1 -expect n_object == 1
+varnish v1 -expect sess_conn == 1
+varnish v1 -expect client_req == 1
+varnish v1 -expect cache_miss == 1
+varnish v1 -expect s_sess == 1
+varnish v1 -expect s_resp_bodybytes == 7
+varnish v1 -expect s_resp_hdrbytes == 178
diff --git a/bin/varnishtest/tests/b00054.vtc b/bin/varnishtest/tests/b00054.vtc
new file mode 100644
index 0000000..4850a56
--- /dev/null
+++ b/bin/varnishtest/tests/b00054.vtc
@@ -0,0 +1,18 @@
+varnishtest "Check poll acceptor on a UDS listen address"
+
+server s1 {
+	rxreq
+	txresp -hdr "Connection: close" -body "012345\n"
+} -start
+
+varnish v1 -arg "-a ${tmpdir}/v1.sock -Wpoll" -vcl+backend {} -start
+
+client c1 -connect "${tmpdir}/v1.sock" {
+	txreq -url "/"
+	rxresp
+	expect resp.status == 200
+	delay .1
+	txreq -url "/"
+	rxresp
+	expect resp.status == 200
+} -run
diff --git a/bin/varnishtest/tests/b00055.vtc b/bin/varnishtest/tests/b00055.vtc
new file mode 100644
index 0000000..bb3a36d
--- /dev/null
+++ b/bin/varnishtest/tests/b00055.vtc
@@ -0,0 +1,30 @@
+varnishtest "Check pipelining over a UDS listen address"
+
+server s1 {
+	rxreq
+	expect req.url == "/foo"
+	txresp -body "foo"
+	rxreq
+	expect req.url == "/bar"
+	txresp -body "foobar"
+} -start
+
+varnish v1 -arg "-a ${tmpdir}/v1.sock" -vcl+backend {} -start
+
+client c1 -connect "${tmpdir}/v1.sock" {
+	send "GET /foo HTTP/1.1\n\nGET /bar HTTP/1.1\n\nGET /bar HTTP/1.1\n\n"
+	rxresp
+	expect resp.status == 200
+	expect resp.bodylen == 3
+	expect resp.http.x-varnish == "1001"
+	rxresp
+	expect resp.status == 200
+	expect resp.bodylen == 6
+	expect resp.http.x-varnish == "1003"
+	rxresp
+	expect resp.status == 200
+	expect resp.bodylen == 6
+	expect resp.http.x-varnish == "1005 1004"
+} -run
+
+varnish v1 -expect sess_readahead == 2
diff --git a/bin/varnishtest/tests/b00056.vtc b/bin/varnishtest/tests/b00056.vtc
new file mode 100644
index 0000000..b875f0b
--- /dev/null
+++ b/bin/varnishtest/tests/b00056.vtc
@@ -0,0 +1,36 @@
+varnishtest "Check read-head / partial pipelining over a UDS listen address"
+
+server s1 {
+	rxreq
+	expect req.url == "/foo"
+	txresp -body "foo"
+	rxreq
+	expect req.url == "/bar"
+	txresp -body "foobar"
+} -start
+
+varnish v1 -arg "-a ${tmpdir}/v1.sock" -vcl+backend {}
+
+# NB: The accept_filter param may not exist.
+varnish v1 -cli "param.set accept_filter false"
+varnish v1 -start
+
+client c1 -connect "${tmpdir}/v1.sock" {
+	send "GET /foo HTTP/1.1\r\n\r\nGET "
+	rxresp
+	expect resp.status == 200
+	expect resp.bodylen == 3
+	expect resp.http.x-varnish == "1001"
+	send "/bar HTTP/1.1\n\nGET /bar "
+	rxresp
+	expect resp.status == 200
+	expect resp.bodylen == 6
+	expect resp.http.x-varnish == "1003"
+	send "HTTP/1.1\n\n"
+	rxresp
+	expect resp.status == 200
+	expect resp.bodylen == 6
+	expect resp.http.x-varnish == "1005 1004"
+} -run
+
+varnish v1 -expect sess_readahead == 2
diff --git a/bin/varnishtest/tests/b00057.vtc b/bin/varnishtest/tests/b00057.vtc
new file mode 100644
index 0000000..74c73cc
--- /dev/null
+++ b/bin/varnishtest/tests/b00057.vtc
@@ -0,0 +1,22 @@
+varnishtest "Test orderly connection closure of a UDS listen socket"
+
+
+server s1 {
+	rxreq
+	txresp -nolen -hdr "Transfer-encoding: chunked"
+	delay .2
+	chunkedlen 30000
+	delay .2
+	chunkedlen 100000
+	delay .2
+	chunkedlen 0
+} -start
+
+varnish v1 -arg "-a ${tmpdir}/v1.sock" -vcl+backend { } -start
+
+client c1 -connect "${tmpdir}/v1.sock" {
+	txreq -hdr "Connection: close"
+	delay 3
+	rxresp
+	expect resp.bodylen == 130000
+} -run
diff --git a/bin/varnishtest/tests/b00059.vtc b/bin/varnishtest/tests/b00059.vtc
new file mode 100644
index 0000000..00ce94f
--- /dev/null
+++ b/bin/varnishtest/tests/b00059.vtc
@@ -0,0 +1,52 @@
+varnishtest "Run a lot of transactions through Unix domain sockets"
+
+server s0 {
+	loop 10 {
+		rxreq
+		txresp -body "foo1"
+	}
+	rxreq
+	txresp -hdr "Connection: close" -body "foo1"
+	expect_close
+} -dispatch
+
+varnish v1 -arg "-a ${tmpdir}/v1.sock -Wpoll" -vcl+backend {
+	sub vcl_recv {
+		return (pass);
+	}
+	sub vcl_backend_fetch {
+		set bereq.backend = s0;
+	}
+
+} -start
+
+client c1 -connect "${tmpdir}/v1.sock" {
+	loop 20 {
+		txreq -url /c1
+		rxresp
+		expect resp.bodylen == 4
+		expect resp.status == 200
+	}
+} -start
+
+client c2 -connect "${tmpdir}/v1.sock" {
+	loop 20 {
+		txreq -url /c2
+		rxresp
+		expect resp.bodylen == 4
+		expect resp.status == 200
+	}
+} -start
+
+client c3 -connect "${tmpdir}/v1.sock" {
+	loop 20 {
+		txreq -url /c3
+		rxresp
+		expect resp.bodylen == 4
+		expect resp.status == 200
+	}
+} -start
+
+client c1 -wait
+client c2 -wait
+client c3 -wait
diff --git a/bin/varnishtest/tests/b00060.vtc b/bin/varnishtest/tests/b00060.vtc
new file mode 100644
index 0000000..6ecd156
--- /dev/null
+++ b/bin/varnishtest/tests/b00060.vtc
@@ -0,0 +1,22 @@
+varnishtest "VSL tags affected by the use of UDS addresses"
+
+varnish v1 -arg "-a foo=${tmpdir}/v1.sock" -vcl {
+	backend b { .host = "${bad_ip}"; }
+
+	sub vcl_recv { return(synth(200)); }
+} -start
+
+client c1 -connect "${tmpdir}/v1.sock" {
+	txreq
+	rxresp
+} -run
+
+logexpect l1 -v v1 -d 1 -g session {
+	expect 0 1000	Begin
+	expect 0 =	SessOpen	"^0.0.0.0 0 foo 0.0.0.0 0"
+} -run
+
+logexpect l2 -v v1 -d 1 -g vxid {
+	expect 0 1001	Begin
+	expect * =	ReqStart	"^0.0.0.0 0$"
+} -run
diff --git a/bin/varnishtest/tests/c00003.vtc b/bin/varnishtest/tests/c00003.vtc
index eca86dc..9dfb220 100644
--- a/bin/varnishtest/tests/c00003.vtc
+++ b/bin/varnishtest/tests/c00003.vtc
@@ -6,14 +6,108 @@ shell -err -match "have same address|already in use" {
 	varnishd -d -a 127.0.0.1:38484 -a 127.0.0.1:38484 -b localhost:80
 }
 
+shell -err -match "have same address|already in use" {
+	varnishd -d -a ${tmpdir}/vtc.sock -a ${tmpdir}/vtc.sock -b localhost:80
+}
+
 # -a bad protocol specs
-shell -err -expect "Too many sub-arguments" {
+shell -err -expect "Too many protocol sub-args" {
 	varnishd -a 127.0.0.1:80000,PROXY,FOO -d
 }
-shell -err -expect "Too many sub-arguments" {
+shell -err -expect "Too many protocol sub-args" {
 	varnishd -a 127.0.0.1:80000,HTTP,FOO -d
 }
 
+# -a relative path for a UDS address not permitted
+shell -err -expect "Unix domain socket addresses must be absolute paths" {
+	varnishd -a foo/bar.sock -d
+}
+
+# -a args for UDS permissions not permitted with IP addresses
+shell -err -expect "Invalid sub-arg user=u for IP addresses" {
+	varnishd -a 127.0.0.1:80000,user=u -d
+}
+
+shell -err -expect "Invalid sub-arg group=g for IP addresses" {
+	varnishd -a 127.0.0.1:80000,group=g -d
+}
+
+shell -err -expect "Invalid sub-arg mode=660 for IP addresses" {
+	varnishd -a 127.0.0.1:80000,mode=660 -d
+}
+
+# Illegal mode sub-args
+shell -err -expect "Too many mode sub-args" {
+	varnishd -a ${tmpdir}/vtc.sock,mode=660,mode=600 -d
+}
+
+shell -err -expect "Empty mode sub-arg" {
+	varnishd -a ${tmpdir}/vtc.sock,mode= -d
+}
+
+shell -err -expect "Invalid mode sub-arg 666devilish" {
+	varnishd -a ${tmpdir}/vtc.sock,mode=666devilish -d
+}
+
+shell -err -expect "Invalid mode sub-arg devilish" {
+	varnishd -a ${tmpdir}/vtc.sock,mode=devilish -d
+}
+
+shell -err -expect "Invalid mode sub-arg 999" {
+	varnishd -a ${tmpdir}/vtc.sock,mode=999 -d
+}
+
+shell -err -expect "Cannot parse mode sub-arg 7" {
+	varnishd -a ${tmpdir}/vtc.sock,mode=77777777777777777777777777777777 -d
+}
+
+shell -err -expect "Cannot parse mode sub-arg -7" {
+	varnishd -a ${tmpdir}/vtc.sock,mode=-77777777777777777777777777777777 -d
+}
+
+shell -err -expect "Mode sub-arg 1666 out of range" {
+	varnishd -a ${tmpdir}/vtc.sock,mode=1666 -d
+}
+
+shell -err -expect "Mode sub-arg 0 out of range" {
+	varnishd -a ${tmpdir}/vtc.sock,mode=0 -d
+}
+
+shell -err -expect "Mode sub-arg -1 out of range" {
+	varnishd -a ${tmpdir}/vtc.sock,mode=-1 -d
+}
+
+##
+## user and group sub-args tested in c00086.vtc, where the user and group
+## features are enabled.
+##
+
+# Invalid sub-arg
+shell -err -expect "Invalid sub-arg foo=bar" {
+	varnishd -a ${tmpdir}/vtc.sock,foo=bar -d
+}
+
+# A sub-arg without '=' is interpreted as a protocol name.
+shell -err -expect "Unknown protocol" {
+	varnishd -a ${tmpdir}/vtc.sock,foobar -d
+}
+
+shell -err -expect "Invalid sub-arg userfoo=u" {
+	varnishd -a ${tmpdir}/vtc.sock,userfoo=u -d
+}
+
+shell -err -expect "Invalid sub-arg groupfoo=g" {
+	varnishd -a ${tmpdir}/vtc.sock,groupfoo=g -d
+}
+
+shell -err -expect "Invalid sub-arg modefoo=666" {
+	varnishd -a ${tmpdir}/vtc.sock,modefoo=666 -d
+}
+
+shell -err -expect "Invalid sub-arg =foo" {
+	varnishd -a ${tmpdir}/vtc.sock,=foo -d
+}
+
 # This requires non-local binds to be disabled.  If you see this fail
 # and are on Linux, ensure /proc/net/ipv4/ip_nonlocal_bind is set to 0.
 
diff --git a/bin/varnishtest/tests/c00086.vtc b/bin/varnishtest/tests/c00086.vtc
new file mode 100644
index 0000000..e149437
--- /dev/null
+++ b/bin/varnishtest/tests/c00086.vtc
@@ -0,0 +1,53 @@
+varnishtest "-a sub-args user, group and mode"
+
+feature user_vcache
+feature group_varnish
+feature root
+
+shell -err -expect "Too many user sub-args" {
+	varnishd -a ${tmpdir}/vtc.sock,user=vcache,user=vcache -d
+}
+
+shell -err -expect "Too many group sub-args" {
+	varnishd -a ${tmpdir}/vtc.sock,group=varnish,group=varnish -d
+}
+
+# Assuming that empty user and group names always fail getpwnam and getgrnam
+shell -err -expect "Unknown user " {
+	varnishd -a ${tmpdir}/vtc.sock,user= -d
+}
+
+shell -err -expect "Unknown group " {
+	varnishd -a ${tmpdir}/vtc.sock,group= -d
+}
+
+server s1 {} -start
+
+varnish v1 -arg "-a ${tmpdir}/v1.sock,user=vcache,group=varnish,mode=660" \
+	-vcl+backend {}
+
+shell -match "rw-rw----.+vcache.+varnish" { ls -l ${tmpdir}/v1.sock }
+
+varnish v2 -arg "-a ${tmpdir}/v2.sock,user=vcache,mode=600" -vcl+backend {}
+
+shell -match "rw-------.+vcache" { ls -l ${tmpdir}/v2.sock }
+
+varnish v3 -arg "-a ${tmpdir}/v3.sock,group=varnish,mode=660" -vcl+backend {}
+
+shell -match "rw---- .+root.+varnish" { ls -l ${tmpdir}/v3.sock }
+
+varnish v4 -arg "-a ${tmpdir}/v4.sock,mode=666" -vcl+backend {}
+
+shell -match "rw-rw-rw-.+root" { ls -l ${tmpdir}/v4.sock }
+
+varnish v5 -arg "-a ${tmpdir}/v5.sock,user=vcache,group=varnish" -vcl+backend {}
+
+shell -match "vcache.+varnish" { ls -l ${tmpdir}/v5.sock }
+
+varnish v6 -arg "-a ${tmpdir}/v6.sock,user=vcache" -vcl+backend {}
+
+shell -match "vcache" { ls -l ${tmpdir}/v6.sock }
+
+varnish v7 -arg "-a ${tmpdir}/v7.sock,group=varnish" -vcl+backend {}
+
+shell -match "root.+varnish" { ls -l ${tmpdir}/v7.sock }
diff --git a/bin/varnishtest/tests/c00087.vtc b/bin/varnishtest/tests/c00087.vtc
new file mode 100644
index 0000000..5f55cdf
--- /dev/null
+++ b/bin/varnishtest/tests/c00087.vtc
@@ -0,0 +1,116 @@
+varnishtest "VCL *.ip vars as bogo-IPs when -a is UDS, and test ACL matches"
+
+server s1 {
+	rxreq
+	txresp
+} -start
+
+varnish v1 -arg "-a ${tmpdir}/v1.sock" -vcl+backend {
+	acl acl1 {
+		"${localhost}";
+	}
+
+	sub vcl_backend_response {
+		set beresp.http.b-client = client.ip;
+		set beresp.http.b-server = server.ip;
+		set beresp.http.b-local = local.ip;
+		set beresp.http.b-remote = remote.ip;
+		set beresp.http.b-compare = local.ip == remote.ip;
+	}
+
+	sub vcl_deliver {
+		set resp.http.c-client = client.ip;
+		set resp.http.c-server = server.ip;
+		set resp.http.c-local = local.ip;
+		set resp.http.c-remote = remote.ip;
+		set resp.http.c-compare = local.ip == remote.ip;
+		set resp.http.client_acl = client.ip ~ acl1;
+		set resp.http.server_acl = server.ip ~ acl1;
+		set resp.http.local_acl = local.ip ~ acl1;
+		set resp.http.remote_acl = remote.ip ~ acl1;
+	}
+} -start
+
+client c1 -connect "${tmpdir}/v1.sock" {
+	txreq
+	rxresp
+	expect resp.status == 200
+	expect resp.http.c-client == "0.0.0.0"
+	expect resp.http.c-server == "0.0.0.0"
+	expect resp.http.c-local == "0.0.0.0"
+	expect resp.http.c-remote == "0.0.0.0"
+	expect resp.http.c-compare == "true"
+	expect resp.http.b-client == "0.0.0.0"
+	expect resp.http.b-server == "0.0.0.0"
+	expect resp.http.b-local == "0.0.0.0"
+	expect resp.http.b-remote == "0.0.0.0"
+	expect resp.http.b-compare == "true"
+	expect resp.http.client_acl == "false"
+	expect resp.http.server_acl == "false"
+	expect resp.http.local_acl == "false"
+	expect resp.http.remote_acl == "false"
+} -run
+
+logexpect l1 -v v1 -d 1 -g vxid -q "VCL_acl" {
+	expect 0 * Begin req
+	expect * = VCL_acl "^NO_MATCH acl1$"
+	expect * = VCL_acl "^NO_MATCH acl1$"
+	expect * = VCL_acl "^NO_MATCH acl1$"
+	expect * = VCL_acl "^NO_MATCH acl1$"
+	expect * = End
+} -run
+
+varnish v1 -vcl {
+	backend b { .host = "${bad_ip}"; }
+
+	acl acl1 {
+		"0.0.0.0";
+	}
+
+	sub vcl_recv {
+		return(synth(200));
+	}
+
+	sub vcl_synth {
+		set resp.http.client = client.ip ~ acl1;
+		set resp.http.server = server.ip ~ acl1;
+		set resp.http.local = local.ip ~ acl1;
+		set resp.http.remote = remote.ip ~ acl1;
+	}
+}
+
+client c1 -connect "${tmpdir}/v1.sock" {
+	txreq -url "foo"
+	rxresp
+	expect resp.http.client == "true"
+	expect resp.http.server == "true"
+	expect resp.http.local == "true"
+	expect resp.http.remote == "true"
+} -run
+
+varnish v1 -errvcl {.../mask is not numeric.} {
+	backend b { .host = "${bad_ip}"; }
+
+	acl acl1 {
+		"${tmpdir}/v1.sock";
+	}
+}
+
+# client.ip == 0.0.0.0 appears in X-Forwarded-For
+server s1 {
+	rxreq
+	expect req.http.X-Forwarded-For == "0.0.0.0"
+	txresp
+	rxreq
+	expect req.http.X-Forwarded-For == "1.2.3.4, 0.0.0.0"
+	txresp
+} -start
+
+varnish v1 -vcl+backend {}
+
+client c1 -connect "${tmpdir}/v1.sock" {
+	txreq -url /1
+	rxresp
+	txreq -url /2 -hdr "X-forwarded-for: 1.2.3.4"
+	rxresp
+} -run
diff --git a/bin/varnishtest/tests/j00004.vtc b/bin/varnishtest/tests/j00004.vtc
new file mode 100644
index 0000000..a23f7d7
--- /dev/null
+++ b/bin/varnishtest/tests/j00004.vtc
@@ -0,0 +1,42 @@
+varnishtest "Listen at a Unix domain socket while in jail"
+
+feature user_varnish
+feature group_varnish
+feature root
+
+server s1 {
+	rxreq
+	txresp
+} -start
+
+varnish v1 -arg "-a ${tmpdir}/v1.sock" \
+	-jail "-junix,user=varnish,ccgroup=varnish" \
+	-vcl+backend {
+} -start
+
+# Socket is created as management owner before the child goes to jail
+shell -match "root" { ls -l ${tmpdir}/v1.sock }
+
+client c1 -connect "${tmpdir}/v1.sock" {
+	txreq
+	rxresp
+	expect resp.status == 200
+} -run
+
+server s1 {
+	rxreq
+	txresp
+} -start
+
+varnish v2 -arg "-a ${tmpdir}/v2.sock,user=varnish,group=varnish,mode=666" \
+	-jail "-junix,user=varnish,ccgroup=varnish" \
+	-vcl+backend {
+} -start
+
+shell -match "rw-rw-rw-.+varnish.+varnish" { ls -l ${tmpdir}/v2.sock }
+
+client c1 -connect "${tmpdir}/v2.sock" {
+	txreq
+	rxresp
+	expect resp.status == 200
+} -run
diff --git a/bin/varnishtest/tests/v00054.vtc b/bin/varnishtest/tests/v00054.vtc
new file mode 100644
index 0000000..a230fb8
--- /dev/null
+++ b/bin/varnishtest/tests/v00054.vtc
@@ -0,0 +1,28 @@
+varnishtest "client.identity is 0.0.0.0 if unset & client addr is UDS"
+
+varnish v1 -arg "-a ${tmpdir}/v1.sock" -vcl {
+	backend b { .host = "${bad_ip}"; }
+
+	sub vcl_recv {
+		if (req.url == "/nobody") {
+			set client.identity = "Samuel B. Nobody";
+		}
+		set req.http.id = client.identity;
+		return(synth(200));
+	}
+
+	sub vcl_synth {
+		set resp.http.id = req.http.id;
+	}
+} -start
+
+client c1 -connect "${tmpdir}/v1.sock" {
+	txreq -url "/nobody"
+	rxresp
+	expect resp.status == 200
+	expect resp.http.id == "Samuel B. Nobody"
+	txreq
+	rxresp
+	expect resp.status == 200
+	expect resp.http.id == "0.0.0.0"
+} -run
diff --git a/bin/varnishtest/vtc_client.c b/bin/varnishtest/vtc_client.c
index 8db014c..ea33c5a 100644
--- a/bin/varnishtest/vtc_client.c
+++ b/bin/varnishtest/vtc_client.c
@@ -179,9 +179,12 @@ client_uds_connect(struct vtclog *vl, const char *path, double tmo,
 
 	assert(tmo >= 0);
 
+	errno = 0;
 	fd = VUS_resolver(path, uds_open, &tmo, errp);
-	if (fd < 0)
+	if (fd < 0) {
+		*errp = strerror(errno);
 		return fd;
+	}
 	vtc_log(vl, 3, "connected fd %d to %s", fd, path);
 	return fd;
 }
diff --git a/include/vsa.h b/include/vsa.h
index c28a7e9..70e4e50 100644
--- a/include/vsa.h
+++ b/include/vsa.h
@@ -32,7 +32,9 @@
 
 struct suckaddr;
 extern const int vsa_suckaddr_len;
+extern const struct suckaddr *bogo_ip;
 
+void VSA_Init(void);
 int VSA_Sane(const struct suckaddr *);
 unsigned VSA_Port(const struct suckaddr *);
 int VSA_Compare(const struct suckaddr *, const struct suckaddr *);
diff --git a/lib/libvarnish/vsa.c b/lib/libvarnish/vsa.c
index c2e157b..1fc496f 100644
--- a/lib/libvarnish/vsa.c
+++ b/lib/libvarnish/vsa.c
@@ -36,7 +36,9 @@
 #include <string.h>
 #include <stdlib.h>
 #include <sys/socket.h>
+#include <sys/types.h>
 #include <netinet/in.h>
+#include <netdb.h>
 
 #include "vdef.h"
 #include "vas.h"
@@ -175,6 +177,27 @@ struct suckaddr {
 const int vsa_suckaddr_len = sizeof(struct suckaddr);
 
 /*
+ * Bogus IPv4 address 0.0.0.0:0 to be used for VCL *.ip variables when the
+ * "real" address is not IP (such as UDS addresses).
+ */
+static struct suckaddr bogo_ip_vsa;
+const struct suckaddr *bogo_ip = &bogo_ip_vsa;
+
+void
+VSA_Init()
+{
+	struct addrinfo hints, *res = NULL;
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = AF_INET;
+	hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+	AZ(getaddrinfo("0.0.0.0", "0", &hints, &res));
+	AN(VSA_Build(&bogo_ip_vsa, res->ai_addr, res->ai_addrlen));
+	assert(VSA_Sane(bogo_ip));
+	freeaddrinfo(res);
+}
+
+/*
  * This VRT interface is for the VCC generated ACL code, which needs
  * to know the address family and a pointer to the actual address.
  */


More information about the varnish-commit mailing list