Commit a2682c09 authored by Geoff Simmons's avatar Geoff Simmons

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.
parent 8bf0dd53
......@@ -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;
......
......@@ -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));
}
......
......@@ -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)
......
......@@ -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);
......
......@@ -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);
......
......@@ -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) {
......
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
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
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
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
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
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
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
......@@ -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.
......
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 }
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
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
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
......@@ -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;
}
......
......@@ -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 *);
......
......@@ -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"
......@@ -174,6 +176,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.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment