implement `listen on'

Listening by default on all the addresses is so bad I don't know
why I haven't changed this before.  Anyway.

Add a `listen on $hostname port $port' syntax to the config file
and deprecate the old "port" and "ipv6" global setting.  Still try
to honour them when no "listen on" directive is used for backward
compatibily, but this will go away in the next next version hopefully.

At the moment the `listen on' in server context don't filter the
host, i.e. one can still reach a host from a address not specified
in the corresponding `liste on', this will be added later.
This commit is contained in:
Omar Polo 2023-06-23 21:03:29 +00:00
parent 37df23d183
commit 509d0509a5
10 changed files with 259 additions and 156 deletions

168
config.c
View File

@ -37,9 +37,8 @@ config_new(void)
TAILQ_INIT(&conf->fcgi);
TAILQ_INIT(&conf->hosts);
TAILQ_INIT(&conf->pkis);
TAILQ_INIT(&conf->addrs);
conf->port = 1965;
conf->ipv6 = 0;
conf->protos = TLS_PROTOCOL_TLSv1_2 | TLS_PROTOCOL_TLSv1_3;
init_mime(&conf->mime);
@ -50,9 +49,6 @@ config_new(void)
conf->use_privsep_crypto = 1;
#endif
conf->sock4 = -1;
conf->sock6 = -1;
return conf;
}
@ -67,21 +63,12 @@ config_purge(struct conf *conf)
struct envlist *e, *te;
struct alist *a, *ta;
struct pki *pki, *tpki;
struct address *addr, *taddr;
int use_privsep_crypto;
ps = conf->ps;
use_privsep_crypto = conf->use_privsep_crypto;
if (conf->sock4 != -1) {
event_del(&conf->evsock4);
close(conf->sock4);
}
if (conf->sock6 != -1) {
event_del(&conf->evsock6);
close(conf->sock6);
}
free_mime(&conf->mime);
TAILQ_FOREACH_SAFE(f, &conf->fcgi, fcgi, tf) {
TAILQ_REMOVE(&conf->fcgi, f, fcgi);
@ -96,6 +83,11 @@ config_purge(struct conf *conf)
free(h->key);
free(h->ocsp);
TAILQ_FOREACH_SAFE(addr, &h->addrs, addrs, taddr) {
TAILQ_REMOVE(&h->addrs, addr, addrs);
free(addr);
}
TAILQ_FOREACH_SAFE(l, &h->locations, locations, tl) {
TAILQ_REMOVE(&h->locations, l, locations);
@ -139,11 +131,19 @@ config_purge(struct conf *conf)
free(pki);
}
TAILQ_FOREACH_SAFE(addr, &conf->addrs, addrs, taddr) {
TAILQ_REMOVE(&conf->addrs, addr, addrs);
if (addr->sock != -1) {
close(addr->sock);
event_del(&addr->evsock);
}
free(addr);
}
memset(conf, 0, sizeof(*conf));
conf->ps = ps;
conf->use_privsep_crypto = use_privsep_crypto;
conf->sock4 = conf->sock6 = -1;
conf->protos = TLS_PROTOCOL_TLSv1_2 | TLS_PROTOCOL_TLSv1_3;
init_mime(&conf->mime);
TAILQ_INIT(&conf->fcgi);
@ -222,83 +222,48 @@ config_send_kp(struct privsep *ps, int cert_type, int key_type,
return 0;
}
static int
make_socket(int port, int family)
{
int sock, v;
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
struct sockaddr *addr;
socklen_t len;
switch (family) {
case AF_INET:
memset(&addr4, 0, sizeof(addr4));
addr4.sin_family = family;
addr4.sin_port = htons(port);
addr4.sin_addr.s_addr = INADDR_ANY;
addr = (struct sockaddr*)&addr4;
len = sizeof(addr4);
break;
case AF_INET6:
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(port);
addr6.sin6_addr = in6addr_any;
addr = (struct sockaddr*)&addr6;
len = sizeof(addr6);
break;
default:
/* unreachable */
abort();
}
if ((sock = socket(family, SOCK_STREAM, 0)) == -1)
fatal("socket");
v = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)) == -1)
fatal("setsockopt(SO_REUSEADDR)");
v = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &v, sizeof(v)) == -1)
fatal("setsockopt(SO_REUSEPORT)");
mark_nonblock(sock);
if (bind(sock, addr, len) == -1)
fatal("bind");
if (listen(sock, 16) == -1)
fatal("listen");
return sock;
}
static int
config_send_socks(struct conf *conf)
{
struct privsep *ps = conf->ps;
int sock;
struct address *addr, a;
int sock, v;
if ((sock = make_socket(conf->port, AF_INET)) == -1)
return -1;
TAILQ_FOREACH(addr, &conf->addrs, addrs) {
sock = socket(addr->ai_family, addr->ai_socktype,
addr->ai_protocol);
if (sock == -1)
fatal("socket");
if (config_send_file(ps, PROC_SERVER, IMSG_RECONF_SOCK4, sock,
NULL, 0) == -1)
return -1;
v = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v))
== -1)
fatal("setsockopt(SO_REUSEADDR)");
if (!conf->ipv6)
return 0;
v = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &v, sizeof(v))
== -1)
fatal("setsockopt(SO_REUSEPORT)");
if ((sock = make_socket(conf->port, AF_INET6)) == -1)
return -1;
mark_nonblock(sock);
if (config_send_file(ps, PROC_SERVER, IMSG_RECONF_SOCK6, sock,
NULL, 0) == -1)
return -1;
if (bind(sock, (struct sockaddr *)&addr->ss, addr->slen)
== -1)
fatal("bind");
if (listen(sock, 16) == -1)
fatal("listen");
memcpy(&a, addr, sizeof(a));
a.conf = NULL;
a.sock = -1;
memset(&a.evsock, 0, sizeof(a.evsock));
memset(&a.addrs, 0, sizeof(a.addrs));
if (config_send_file(ps, PROC_SERVER, IMSG_RECONF_SOCK, sock,
&a, sizeof(a)) == -1)
return -1;
}
return 0;
}
@ -327,10 +292,6 @@ config_send(struct conf *conf)
&conf->protos, sizeof(conf->protos)) == -1)
return -1;
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_PORT,
&conf->port, sizeof(conf->port)) == -1)
return -1;
if (proc_flush_imsg(ps, PROC_SERVER, -1) == -1)
return -1;
@ -549,6 +510,7 @@ config_recv(struct conf *conf, struct imsg *imsg)
struct envlist *env;
struct alist *alias;
struct proxy *proxy;
struct address *addr;
uint8_t *d;
size_t len, datalen;
@ -577,29 +539,17 @@ config_recv(struct conf *conf, struct imsg *imsg)
memcpy(&conf->protos, imsg->data, datalen);
break;
case IMSG_RECONF_PORT:
IMSG_SIZE_CHECK(imsg, &conf->port);
memcpy(&conf->port, imsg->data, datalen);
break;
case IMSG_RECONF_SOCK4:
if (conf->sock4 != -1)
fatalx("socket ipv4 already recv'd");
case IMSG_RECONF_SOCK:
addr = xcalloc(1, sizeof(*addr));
IMSG_SIZE_CHECK(imsg, addr);
memcpy(addr, imsg->data, sizeof(*addr));
if (imsg->fd == -1)
fatalx("missing socket for IMSG_RECONF_SOCK4");
conf->sock4 = imsg->fd;
event_set(&conf->evsock4, conf->sock4, EV_READ|EV_PERSIST,
do_accept, conf);
break;
case IMSG_RECONF_SOCK6:
if (conf->sock6 != -1)
fatalx("socket ipv6 already recv'd");
if (imsg->fd == -1)
fatalx("missing socket for IMSG_RECONF_SOCK6");
conf->sock6 = imsg->fd;
event_set(&conf->evsock6, conf->sock6, EV_READ|EV_PERSIST,
do_accept, conf);
addr->conf = conf;
addr->sock = imsg->fd;
event_set(&addr->evsock, addr->sock, EV_READ|EV_PERSIST,
do_accept, addr);
TAILQ_INSERT_HEAD(&conf->addrs, addr, addrs);
break;
case IMSG_RECONF_FCGI:

2
fcgi.c
View File

@ -350,7 +350,7 @@ fcgi_req(struct client *c)
struct tm tminfo;
struct envlist *p;
e = getnameinfo((struct sockaddr*)&c->addr, sizeof(c->addr),
e = getnameinfo((struct sockaddr*)&c->raddr, c->raddrlen,
addr, sizeof(addr),
NULL, 0,
NI_NUMERICHOST);

47
ge.c
View File

@ -159,9 +159,14 @@ static int
serve(struct conf *conf, const char *host, int port, const char *dir)
{
struct addrinfo hints, *res, *res0;
struct vhost *vh = TAILQ_FIRST(&conf->hosts);
struct address *addr, *acp;
int r, error, saved_errno, sock = -1;
const char *cause = NULL;
char service[32];
int any = 0;
event_init();
r = snprintf(service, sizeof(service), "%d", port);
if (r < 0 || (size_t)r >= sizeof(service))
@ -193,24 +198,34 @@ serve(struct conf *conf, const char *host, int port, const char *dir)
if (listen(sock, 5) == -1)
fatal("listen");
/*
* for the time being, we're happy as soon as
* something binds.
*/
break;
any = 1;
addr = xcalloc(1, sizeof(*addr));
addr->ai_flags = res->ai_flags;
addr->ai_family = res->ai_family;
addr->ai_socktype = res->ai_socktype;
addr->ai_protocol = res->ai_protocol;
addr->slen = res->ai_addrlen;
memcpy(&addr->ss, res->ai_addr, res->ai_addrlen);
addr->conf = conf;
addr->sock = sock;
event_set(&addr->evsock, addr->sock, EV_READ|EV_PERSIST,
do_accept, addr);
TAILQ_INSERT_HEAD(&conf->addrs, addr, addrs);
acp = xcalloc(1, sizeof(*acp));
memcpy(acp, addr, sizeof(*acp));
acp->sock = -1;
memset(&acp->evsock, 0, sizeof(acp->evsock));
TAILQ_INSERT_HEAD(&vh->addrs, addr, addrs);
}
if (sock == -1)
if (!any)
fatal("%s", cause);
freeaddrinfo(res0);
event_init();
/* cheating */
conf->sock4 = sock;
event_set(&conf->evsock4, conf->sock4, EV_READ|EV_PERSIST,
do_accept, conf);
server_init(NULL, NULL, NULL);
if (server_configure_done(conf) == -1)
fatalx("server configuration failed");
@ -239,7 +254,7 @@ main(int argc, char **argv)
struct location *loc;
const char *errstr, *certs_dir = NULL, *hostname = "localhost";
char path[PATH_MAX];
int ch;
int ch, port = 1965;
setlocale(LC_CTYPE, "");
@ -262,7 +277,7 @@ main(int argc, char **argv)
usage();
break;
case 'p':
conf->port = strtonum(optarg, 0, UINT16_MAX, &errstr);
port = strtonum(optarg, 0, UINT16_MAX, &errstr);
if (errstr)
fatalx("port number is %s: %s", errstr,
optarg);
@ -316,5 +331,5 @@ main(int argc, char **argv)
/* start the server */
signal(SIGPIPE, SIG_IGN);
setproctitle("%s", loc->dir);
return serve(conf, hostname, conf->port, loc->dir);
return serve(conf, hostname, port, loc->dir);
}

2
gmid.c
View File

@ -484,9 +484,7 @@ main_print_conf(struct conf *conf)
if (*conf->chroot != '\0')
printf("chroot \"%s\"\n", conf->chroot);
printf("ipv6 %s\n", conf->ipv6 ? "on" : "off");
/* XXX: defined mimes? */
printf("port %d\n", conf->port);
printf("prefork %d\n", conf->prefork);
/* XXX: protocols? */
if (*conf->user != '\0')

34
gmid.h
View File

@ -104,6 +104,25 @@ struct parser {
const char *err;
};
struct conf;
TAILQ_HEAD(addrhead, address);
struct address {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
struct sockaddr_storage ss;
socklen_t slen;
int16_t port;
/* used in the server */
struct conf *conf;
int sock;
struct event evsock; /* set if sock != -1 */
TAILQ_ENTRY(address) addrs;
};
TAILQ_HEAD(fcgihead, fcgi);
struct fcgi {
int id;
@ -188,6 +207,8 @@ struct vhost {
TAILQ_ENTRY(vhost) vhosts;
struct addrhead addrs;
/*
* the first location rule is always '*' and holds the default
* settings for the vhost, then follows the "real" location
@ -220,8 +241,6 @@ struct pki {
struct conf {
struct privsep *ps;
int port;
int ipv6;
uint32_t protos;
struct mime mime;
char chroot[PATH_MAX];
@ -230,14 +249,10 @@ struct conf {
int reload;
int use_privsep_crypto;
int sock4;
struct event evsock4;
int sock6;
struct event evsock6;
struct fcgihead fcgi;
struct vhosthead hosts;
struct pkihead pkis;
struct addrhead addrs;
};
extern const char *config_path;
@ -258,6 +273,7 @@ enum {
struct client {
struct conf *conf;
struct address *addr;
uint32_t id;
struct tls *ctx;
char *req;
@ -324,9 +340,7 @@ enum imsg_type {
IMSG_RECONF_START, /* 7 */
IMSG_RECONF_MIME,
IMSG_RECONF_PROTOS,
IMSG_RECONF_PORT,
IMSG_RECONF_SOCK4,
IMSG_RECONF_SOCK6,
IMSG_RECONF_SOCK,
IMSG_RECONF_FCGI,
IMSG_RECONF_HOST,
IMSG_RECONF_CERT,

130
parse.y
View File

@ -37,6 +37,9 @@
struct conf *conf;
static const char *default_host = "*";
static uint16_t default_port = 1965;
TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
static struct file {
TAILQ_ENTRY(file) entry;
@ -96,6 +99,7 @@ void parsehp(char *, char **, const char **, const char *);
int fastcgi_conf(const char *, const char *);
void add_param(char *, char *);
int getservice(const char *);
void listen_on(const char *, const char *);
static struct vhost *host;
static struct location *loc;
@ -125,7 +129,7 @@ typedef struct {
%token FASTCGI FOR_HOST
%token INCLUDE INDEX IPV6
%token KEY
%token LANG LOCATION LOG
%token LANG LISTEN LOCATION LOG
%token OCSP OFF ON
%token PARAM PORT PREFORK PROTO PROTOCOLS PROXY
%token RELAY_TO REQUIRE RETURN ROOT
@ -220,8 +224,19 @@ option : CHROOT string {
yyerror("chroot path too long");
free($2);
}
| IPV6 bool { conf->ipv6 = $2; }
| PORT NUM { conf->port = check_port_num($2); }
| IPV6 bool {
yywarn("option `ipv6' is deprecated,"
" please use `listen on'");
if ($2)
default_host = "*";
else
default_host = "0.0.0.0";
}
| PORT NUM {
yywarn("option `port' is deprecated,"
" please use `listen on'");
default_port = $2;
}
| PREFORK NUM { conf->prefork = check_prefork_num($2); }
| PROTOCOLS string {
if (tls_config_parse_protocols(&conf->protos, $2) == -1)
@ -259,6 +274,20 @@ vhost : SERVER string {
host->key_path == NULL)
yyerror("invalid vhost definition: %s",
host->domain);
if (TAILQ_EMPTY(&host->addrs)) {
char portno[32];
int r;
r = snprintf(portno, sizeof(portno), "%d",
default_port);
if (r < 0 || (size_t)r >= sizeof(portno))
fatal("snprintf");
yywarn("missing `listen on' in server %s,"
" assuming %s port %d", $2, default_host,
default_port);
listen_on(default_host, portno);
}
}
| error '}' { yyerror("bad server directive"); }
;
@ -295,6 +324,22 @@ servopt : ALIAS string {
| PARAM string '=' string {
add_param($2, $4);
}
| LISTEN ON STRING PORT STRING {
listen_on($3, $5);
free($3);
free($5);
}
| LISTEN ON STRING PORT NUM {
char portno[32];
int r;
r = snprintf(portno, sizeof(portno), "%d", $5);
if (r < 0 || (size_t)r >= sizeof(portno))
fatal("snprintf");
listen_on($3, portno);
free($3);
}
| locopt
;
@ -515,6 +560,7 @@ static const struct keyword {
{"ipv6", IPV6},
{"key", KEY},
{"lang", LANG},
{"listen", LISTEN},
{"location", LOCATION},
{"log", LOG},
{"ocsp", OCSP},
@ -913,6 +959,9 @@ parse_conf(struct conf *c, const char *filename)
{
struct sym *sym, *next;
default_host = "*";
default_port = 1965;
conf = c;
file = pushfile(filename, 0);
@ -1153,3 +1202,78 @@ getservice(const char *n)
return ((unsigned short)llval);
}
static void
add_to_addr_queue(struct addrhead *a, struct addrinfo *ai)
{
struct address *addr;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
if (ai->ai_addrlen > sizeof(addr->ss))
fatalx("ai_addrlen larger than a sockaddr_storage");
TAILQ_FOREACH(addr, a, addrs) {
if (addr->ai_flags == ai->ai_flags &&
addr->ai_family == ai->ai_family &&
addr->ai_socktype == ai->ai_socktype &&
addr->ai_protocol == ai->ai_protocol &&
addr->slen == ai->ai_addrlen &&
!memcmp(&addr->ss, ai->ai_addr, addr->slen))
return;
}
addr = xcalloc(1, sizeof(*addr));
addr->ai_flags = ai->ai_flags;
addr->ai_family = ai->ai_family;
addr->ai_socktype = ai->ai_socktype;
addr->ai_protocol = ai->ai_protocol;
addr->slen = ai->ai_addrlen;
memcpy(&addr->ss, ai->ai_addr, ai->ai_addrlen);
/* for commodity */
switch (addr->ai_family) {
case AF_INET:
sin = (struct sockaddr_in *)&addr->ss;
addr->port = ntohs(sin->sin_port);
break;
case AF_INET6:
sin6 = (struct sockaddr_in6 *)&addr->ss;
addr->port = ntohs(sin6->sin6_port);
break;
default:
fatalx("unknown socket family %d", addr->ai_family);
}
addr->sock = -1;
TAILQ_INSERT_HEAD(a, addr, addrs);
}
void
listen_on(const char *hostname, const char *servname)
{
struct addrinfo hints, *res, *res0;
int error;
if (!strcmp(hostname, "*"))
hostname = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
error = getaddrinfo(hostname, servname, &hints, &res0);
if (error) {
yyerror("listen on \"%s\" port %s: %s", hostname, servname,
gai_strerror(errno));
return;
}
for (res = res0; res; res = res->ai_next) {
add_to_addr_queue(&host->addrs, res);
add_to_addr_queue(&conf->addrs, res);
}
freeaddrinfo(res0);
}

View File

@ -10,9 +10,7 @@ current_test=
run_test() {
ggflags=
port=10965
config_common="ipv6 off
port $port
"
config_common=""
hdr=
body=
dont_check_server_alive=no
@ -64,6 +62,7 @@ server "localhost" {
cert "$PWD/cert.pem"
key "$PWD/key.pem"
root "$PWD/testdata"
listen on localhost port $port
$2
}
EOF
@ -78,6 +77,7 @@ set_proxy() {
server "localhost.local" {
cert "$PWD/cert.pem"
key "$PWD/key.pem"
listen on localhost port $port
proxy {
relay-to localhost port $port
$1

View File

@ -239,13 +239,13 @@ test_fastcgi() {
test_macro_expansion() {
cat <<EOF > reg.conf
pwd = "$PWD"
$config_common
server "localhost" {
# the quoting of \$ is for sh
cert \$pwd "/cert.pem"
key \$pwd "/key.pem"
root \$pwd "/testdata"
listen on localhost port $port
}
EOF

View File

@ -489,7 +489,6 @@ strip_path(const char *path, int strip)
static void
fmt_sbuf(const char *fmt, struct client *c, const char *path)
{
struct conf *conf = c->conf;
size_t i;
char buf[32];
@ -519,7 +518,7 @@ fmt_sbuf(const char *fmt, struct client *c, const char *path)
strlcat(c->sbuf, c->iri.query, sizeof(c->sbuf));
break;
case 'P':
snprintf(buf, sizeof(buf), "%d", conf->port);
snprintf(buf, sizeof(buf), "%d", c->addr->port);
strlcat(c->sbuf, buf, sizeof(c->sbuf));
memset(buf, 0, sizeof(buf));
break;
@ -1313,7 +1312,7 @@ client_close(struct client *c)
void
do_accept(int sock, short et, void *d)
{
struct conf *conf = d;
struct address *addr = d;
struct client *c;
struct sockaddr_storage raddr;
struct sockaddr *sraddr;
@ -1332,7 +1331,8 @@ do_accept(int sock, short et, void *d)
mark_nonblock(fd);
c = xcalloc(1, sizeof(*c));
c->conf = conf;
c->conf = addr->conf;
c->addr = addr;
c->id = ++server_client_id;
c->fd = fd;
c->pfd = -1;
@ -1486,15 +1486,18 @@ server_init(struct privsep *ps, struct privsep_proc *p, void *arg)
int
server_configure_done(struct conf *conf)
{
struct address *addr;
if (load_default_mime(&conf->mime) == -1)
fatal("can't load default mime");
sort_mime(&conf->mime);
setup_tls(conf);
load_vhosts(conf);
if (conf->sock4 != -1)
event_add(&conf->evsock4, NULL);
if (conf->sock6 != -1)
event_add(&conf->evsock6, NULL);
TAILQ_FOREACH(addr, &conf->addrs, addrs) {
if (addr->sock != -1)
event_add(&addr->evsock, NULL);
}
return 0;
}
@ -1509,9 +1512,7 @@ server_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
case IMSG_RECONF_START:
case IMSG_RECONF_MIME:
case IMSG_RECONF_PROTOS:
case IMSG_RECONF_PORT:
case IMSG_RECONF_SOCK4:
case IMSG_RECONF_SOCK6:
case IMSG_RECONF_SOCK:
case IMSG_RECONF_FCGI:
case IMSG_RECONF_HOST:
case IMSG_RECONF_CERT:

View File

@ -346,6 +346,7 @@ new_vhost(void)
struct vhost *h;
h = xcalloc(1, sizeof(*h));
TAILQ_INIT(&h->addrs);
TAILQ_INIT(&h->locations);
TAILQ_INIT(&h->params);
TAILQ_INIT(&h->aliases);