Hi,
I found myself wanting this, this morning. I made a patch but then I put it
in the wrong spot, and noticed it needed rewriting of SERVER in parse.y.
Later in the day I found myself looking into this, and a better patch came out
of it. It works on a simple setup for me (mind the censored IPv6 address):
simple config-------->
prefork 2
server "default" {
log style combined
listen on 159.69.32.73 port 80
listen on 2a01:4f8:1c1c:89f1::1 port 80
root "/htdocs"
location "/" {
directory index index.html
}
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
}
server "default" rdomain 4 {
log style combined
listen on XXXXCensoredXXXX port 80
root "/htdocs"
location "/" {
directory index index.html
}
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
}
<--------------
Granted I did not test it with a difficult config. And I advise any committer
to test this fully before trusting my code. For me it's better than using
route and starting httpd twice, though.
Patch after my signature.
Best Regards,
-peter
Index: httpd.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.c,v
retrieving revision 1.71
diff -u -p -r1.71 httpd.c
--- httpd.c 27 Jan 2021 07:21:52 -0000 1.71
+++ httpd.c 17 May 2021 14:56:54 -0000
@@ -226,7 +226,7 @@ main(int argc, char *argv[])
if (ps->ps_noaction == 0)
log_info("startup");
- if (pledge("stdio rpath wpath cpath inet dns sendfd", NULL) == -1)
+ if (pledge("stdio rpath wpath cpath inet dns sendfd wroute", NULL) ==
-1)
fatal("pledge");
event_init();
Index: httpd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.conf.5,v
retrieving revision 1.115
diff -u -p -r1.115 httpd.conf.5
--- httpd.conf.5 6 Apr 2021 06:28:38 -0000 1.115
+++ httpd.conf.5 17 May 2021 14:56:54 -0000
@@ -143,13 +143,13 @@ Each
section starts with a declaration of the server
.Ar name :
.Bl -tag -width Ds
-.It Ic server Ar name Brq ...
+.It Ic server Ar name [ Ic rdomain Ar num ] Brq ...
Match the server name using shell globbing rules.
This can be an explicit name,
.Ar www.example.com ,
or a name including wildcards,
.Ar *.example.com .
-.It Ic server match Ar name Brq ...
+.It Ic server match Ar name [ Ic rdomain Ar num ] Brq ...
Match the server name using pattern matching,
see
.Xr patterns 7 .
Index: httpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.h,v
retrieving revision 1.155
diff -u -p -r1.155 httpd.h
--- httpd.h 10 Apr 2021 10:10:07 -0000 1.155
+++ httpd.h 17 May 2021 14:56:54 -0000
@@ -487,6 +487,7 @@ struct server_config {
in_port_t port;
struct sockaddr_storage ss;
+ u_int rdomain;
int prefixlen;
struct timeval timeout;
struct timeval requesttimeout;
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/parse.y,v
retrieving revision 1.125
diff -u -p -r1.125 parse.y
--- parse.y 10 Apr 2021 10:10:07 -0000 1.125
+++ parse.y 17 May 2021 14:56:54 -0000
@@ -120,6 +120,8 @@ int getservice(char *);
int is_if_in_group(const char *, const char *);
int get_fastcgi_dest(struct server_config *, const char *, char *);
void remove_locations(struct server_config *);
+struct server *serverfill(int, char *, u_int);
+int serveropts_fill(struct server *, int);
typedef struct {
union {
@@ -139,7 +141,7 @@ typedef struct {
%token LISTEN LOCATION LOG LOGDIR MATCH MAXIMUM NO NODELAY OCSP ON PORT PREFORK
%token PROTOCOLS REQUESTS ROOT SACK SERVER SOCKET STRIP STYLE SYSLOG TCP TICKET
%token TIMEOUT TLS TYPE TYPES HSTS MAXAGE SUBDOMAINS DEFAULT PRELOAD REQUEST
-%token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS REWRITE
+%token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS REWRITE RDOMAIN
%token CA CLIENT CRL OPTIONAL PARAM FORWARDED FOUND NOT
%token <v.string> STRING
%token <v.number> NUMBER
@@ -222,217 +224,42 @@ main : PREFORK NUMBER {
;
server : SERVER optmatch STRING {
- struct server *s;
- struct sockaddr_un *sun;
-
if (!loadcfg) {
free($3);
YYACCEPT;
}
-
- if ((s = calloc(1, sizeof (*s))) == NULL)
- fatal("out of memory");
-
- if (strlcpy(s->srv_conf.name, $3,
- sizeof(s->srv_conf.name)) >=
- sizeof(s->srv_conf.name)) {
- yyerror("server name truncated");
- free($3);
- free(s);
+ if ((srv = serverfill($2, $3, 0)) == NULL)
YYERROR;
- }
- free($3);
-
- strlcpy(s->srv_conf.root, HTTPD_DOCROOT,
- sizeof(s->srv_conf.root));
- strlcpy(s->srv_conf.index, HTTPD_INDEX,
- sizeof(s->srv_conf.index));
- strlcpy(s->srv_conf.accesslog, HTTPD_ACCESS_LOG,
- sizeof(s->srv_conf.accesslog));
- strlcpy(s->srv_conf.errorlog, HTTPD_ERROR_LOG,
- sizeof(s->srv_conf.errorlog));
- s->srv_conf.id = ++last_server_id;
- s->srv_conf.parent_id = s->srv_conf.id;
- s->srv_s = -1;
- s->srv_conf.timeout.tv_sec = SERVER_TIMEOUT;
- s->srv_conf.requesttimeout.tv_sec =
- SERVER_REQUESTTIMEOUT;
- s->srv_conf.maxrequests = SERVER_MAXREQUESTS;
- s->srv_conf.maxrequestbody = SERVER_MAXREQUESTBODY;
- s->srv_conf.flags = SRVFLAG_LOG;
- if ($2)
- s->srv_conf.flags |= SRVFLAG_SERVER_MATCH;
- s->srv_conf.logformat = LOG_FORMAT_COMMON;
- s->srv_conf.tls_protocols = TLS_PROTOCOLS_DEFAULT;
- if ((s->srv_conf.tls_cert_file =
- strdup(HTTPD_TLS_CERT)) == NULL)
- fatal("out of memory");
- if ((s->srv_conf.tls_key_file =
- strdup(HTTPD_TLS_KEY)) == NULL)
- fatal("out of memory");
- strlcpy(s->srv_conf.tls_ciphers,
- HTTPD_TLS_CIPHERS,
- sizeof(s->srv_conf.tls_ciphers));
- strlcpy(s->srv_conf.tls_dhe_params,
- HTTPD_TLS_DHE_PARAMS,
- sizeof(s->srv_conf.tls_dhe_params));
- strlcpy(s->srv_conf.tls_ecdhe_curves,
- HTTPD_TLS_ECDHE_CURVES,
- sizeof(s->srv_conf.tls_ecdhe_curves));
- sun = (struct sockaddr_un *)&s->srv_conf.fastcgi_ss;
- sun->sun_family = AF_UNIX;
- (void)strlcpy(sun->sun_path, HTTPD_FCGI_SOCKET,
- sizeof(sun->sun_path));
- sun->sun_len = sizeof(struct sockaddr_un);
-
- s->srv_conf.hsts_max_age = SERVER_HSTS_DEFAULT_AGE;
-
- if (last_server_id == INT_MAX) {
- yyerror("too many servers defined");
- free(s);
- YYERROR;
- }
- srv = s;
- srv_conf = &srv->srv_conf;
-
- SPLAY_INIT(&srv->srv_clients);
- TAILQ_INIT(&srv->srv_hosts);
- TAILQ_INIT(&srv_conf->fcgiparams);
-
- TAILQ_INSERT_TAIL(&srv->srv_hosts, srv_conf, entry);
} '{' optnl serveropts_l '}' {
- struct server *s, *sn;
- struct server_config *a, *b;
-
- srv_conf = &srv->srv_conf;
-
- /* Check if the new server already exists. */
- if (server_match(srv, 1) != NULL) {
- yyerror("server \"%s\" defined twice",
- srv->srv_conf.name);
- serverconfig_free(srv_conf);
- free(srv);
+ switch (serveropts_fill(srv, yylval.lineno)) {
+ case -1:
YYABORT;
- }
-
- if (srv->srv_conf.ss.ss_family == AF_UNSPEC) {
- yyerror("listen address not specified");
- serverconfig_free(srv_conf);
- free(srv);
- YYERROR;
- }
-
- if ((s = server_match(srv, 0)) != NULL) {
- if ((s->srv_conf.flags & SRVFLAG_TLS) !=
- (srv->srv_conf.flags & SRVFLAG_TLS)) {
- yyerror("server \"%s\": tls and "
- "non-tls on same address/port",
- srv->srv_conf.name);
- serverconfig_free(srv_conf);
- free(srv);
- YYERROR;
- }
- if (srv->srv_conf.flags & SRVFLAG_TLS &&
- server_tls_cmp(s, srv) != 0) {
- yyerror("server \"%s\": tls "
- "configuration mismatch on same "
- "address/port",
- srv->srv_conf.name);
- serverconfig_free(srv_conf);
- free(srv);
- YYERROR;
- }
- }
-
- if ((srv->srv_conf.flags & SRVFLAG_TLS) &&
- srv->srv_conf.tls_protocols == 0) {
- yyerror("server \"%s\": no tls protocols",
- srv->srv_conf.name);
- serverconfig_free(srv_conf);
- free(srv);
+ break;
+ case -2:
YYERROR;
- }
-
- if (server_tls_load_keypair(srv) == -1) {
- /* Soft fail as there may be no certificate. */
- log_warnx("%s:%d: server \"%s\": failed to "
- "load public/private keys", file->name,
- yylval.lineno, srv->srv_conf.name);
-
- remove_locations(srv_conf);
- serverconfig_free(srv_conf);
- srv_conf = NULL;
- free(srv);
- srv = NULL;
break;
}
- if (server_tls_load_ca(srv) == -1) {
- yyerror("server \"%s\": failed to load "
- "ca cert(s)", srv->srv_conf.name);
- serverconfig_free(srv_conf);
- free(srv);
- YYERROR;
+ srv = NULL;
+ srv_conf = NULL;
+ }
+ | SERVER optmatch STRING RDOMAIN NUMBER {
+ if (!loadcfg) {
+ free($3);
+ YYACCEPT;
}
-
- if (server_tls_load_crl(srv) == -1) {
- yyerror("server \"%s\": failed to load crl(s)",
- srv->srv_conf.name);
- serverconfig_free(srv_conf);
- free(srv);
+ if ((srv = serverfill($2, $3, $5)) == NULL)
YYERROR;
- }
- if (server_tls_load_ocsp(srv) == -1) {
- yyerror("server \"%s\": failed to load "
- "ocsp staple", srv->srv_conf.name);
- serverconfig_free(srv_conf);
- free(srv);
+ } '{' optnl serveropts_l '}' {
+ switch (serveropts_fill(srv, yylval.lineno)) {
+ case -1:
+ YYABORT;
+ break;
+ case -2:
YYERROR;
- }
-
- DPRINTF("adding server \"%s[%u]\"",
- srv->srv_conf.name, srv->srv_conf.id);
-
- TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry);
-
- /*
- * Add aliases and additional listen addresses as
- * individual servers.
- */
- TAILQ_FOREACH(a, &srv->srv_hosts, entry) {
- /* listen address */
- if (a->ss.ss_family == AF_UNSPEC)
- continue;
- TAILQ_FOREACH(b, &srv->srv_hosts, entry) {
- /* alias name */
- if (*b->name == '\0' ||
- (b == &srv->srv_conf && b == a))
- continue;
-
- if ((sn = server_inherit(srv,
- b, a)) == NULL) {
- serverconfig_free(srv_conf);
- free(srv);
- YYABORT;
- }
-
- DPRINTF("adding server \"%s[%u]\"",
- sn->srv_conf.name, sn->srv_conf.id);
-
- TAILQ_INSERT_TAIL(conf->sc_servers,
- sn, srv_entry);
- }
- }
-
- /* Remove temporary aliases */
- TAILQ_FOREACH_SAFE(a, &srv->srv_hosts, entry, b) {
- TAILQ_REMOVE(&srv->srv_hosts, a, entry);
- if (a == &srv->srv_conf)
- continue;
- serverconfig_free(a);
- free(a);
+ break;
}
srv = NULL;
@@ -1425,6 +1252,7 @@ lookup(char *s)
{ "prefork", PREFORK },
{ "preload", PRELOAD },
{ "protocols", PROTOCOLS },
+ { "rdomain", RDOMAIN },
{ "request", REQUEST },
{ "requests", REQUESTS },
{ "return", RETURN },
@@ -2509,4 +2337,227 @@ remove_locations(struct server_config *x
serverconfig_free(&s->srv_conf);
free(s);
}
+}
+
+struct server *
+serverfill(int optmatch, char *servername, u_int rdomain)
+{
+ struct server *ret = NULL;
+ struct server *s;
+ struct sockaddr_un *sun;
+
+
+ if ((s = calloc(1, sizeof (*s))) == NULL)
+ fatal("out of memory");
+
+ if (strlcpy(s->srv_conf.name, servername,
+ sizeof(s->srv_conf.name)) >=
+ sizeof(s->srv_conf.name)) {
+ yyerror("server name truncated");
+ free(servername);
+ free(s);
+ return (NULL);
+ }
+ free(servername);
+
+ strlcpy(s->srv_conf.root, HTTPD_DOCROOT,
+ sizeof(s->srv_conf.root));
+ strlcpy(s->srv_conf.index, HTTPD_INDEX,
+ sizeof(s->srv_conf.index));
+ strlcpy(s->srv_conf.accesslog, HTTPD_ACCESS_LOG,
+ sizeof(s->srv_conf.accesslog));
+ strlcpy(s->srv_conf.errorlog, HTTPD_ERROR_LOG,
+ sizeof(s->srv_conf.errorlog));
+ s->srv_conf.id = ++last_server_id;
+ s->srv_conf.parent_id = s->srv_conf.id;
+ s->srv_s = -1;
+ s->srv_conf.timeout.tv_sec = SERVER_TIMEOUT;
+ s->srv_conf.requesttimeout.tv_sec =
+ SERVER_REQUESTTIMEOUT;
+ s->srv_conf.maxrequests = SERVER_MAXREQUESTS;
+ s->srv_conf.maxrequestbody = SERVER_MAXREQUESTBODY;
+ s->srv_conf.flags = SRVFLAG_LOG;
+ if (optmatch)
+ s->srv_conf.flags |= SRVFLAG_SERVER_MATCH;
+ s->srv_conf.logformat = LOG_FORMAT_COMMON;
+ s->srv_conf.tls_protocols = TLS_PROTOCOLS_DEFAULT;
+ if ((s->srv_conf.tls_cert_file =
+ strdup(HTTPD_TLS_CERT)) == NULL)
+ fatal("out of memory");
+ if ((s->srv_conf.tls_key_file =
+ strdup(HTTPD_TLS_KEY)) == NULL)
+ fatal("out of memory");
+ strlcpy(s->srv_conf.tls_ciphers,
+ HTTPD_TLS_CIPHERS,
+ sizeof(s->srv_conf.tls_ciphers));
+ strlcpy(s->srv_conf.tls_dhe_params,
+ HTTPD_TLS_DHE_PARAMS,
+ sizeof(s->srv_conf.tls_dhe_params));
+ strlcpy(s->srv_conf.tls_ecdhe_curves,
+ HTTPD_TLS_ECDHE_CURVES,
+ sizeof(s->srv_conf.tls_ecdhe_curves));
+
+ sun = (struct sockaddr_un *)&s->srv_conf.fastcgi_ss;
+ sun->sun_family = AF_UNIX;
+ (void)strlcpy(sun->sun_path, HTTPD_FCGI_SOCKET,
+ sizeof(sun->sun_path));
+ sun->sun_len = sizeof(struct sockaddr_un);
+
+ s->srv_conf.hsts_max_age = SERVER_HSTS_DEFAULT_AGE;
+ s->srv_conf.rdomain = rdomain;
+
+ if (last_server_id == INT_MAX) {
+ yyerror("too many servers defined");
+ free(s);
+ return (NULL);
+ }
+ ret = s;
+ srv_conf = &ret->srv_conf;
+
+ SPLAY_INIT(&ret->srv_clients);
+ TAILQ_INIT(&ret->srv_hosts);
+ TAILQ_INIT(&srv_conf->fcgiparams);
+
+ TAILQ_INSERT_TAIL(&ret->srv_hosts, srv_conf, entry);
+
+ return (ret);
+}
+
+int
+serveropts_fill(struct server *ssrv, int lineno)
+{
+ struct server *s, *sn;
+ struct server_config *a, *b;
+
+ srv_conf = &ssrv->srv_conf;
+
+ /* Check if the new server already exists. */
+ if (server_match(ssrv, 1) != NULL) {
+ yyerror("server \"%s\" defined twice",
+ ssrv->srv_conf.name);
+ serverconfig_free(srv_conf);
+ free(ssrv);
+ return (-1);
+ }
+
+ if (ssrv->srv_conf.ss.ss_family == AF_UNSPEC) {
+ yyerror("listen address not specified");
+ serverconfig_free(srv_conf);
+ free(ssrv);
+ return (-2);
+ }
+
+ if ((s = server_match(ssrv, 0)) != NULL) {
+ if ((s->srv_conf.flags & SRVFLAG_TLS) !=
+ (ssrv->srv_conf.flags & SRVFLAG_TLS)) {
+ yyerror("server \"%s\": tls and "
+ "non-tls on same address/port",
+ ssrv->srv_conf.name);
+ serverconfig_free(srv_conf);
+ free(ssrv);
+ return (-2);
+ }
+ if (ssrv->srv_conf.flags & SRVFLAG_TLS &&
+ server_tls_cmp(s, ssrv) != 0) {
+ yyerror("server \"%s\": tls "
+ "configuration mismatch on same "
+ "address/port",
+ ssrv->srv_conf.name);
+ serverconfig_free(srv_conf);
+ free(ssrv);
+ return (-2);
+ }
+ }
+
+ if ((ssrv->srv_conf.flags & SRVFLAG_TLS) &&
+ ssrv->srv_conf.tls_protocols == 0) {
+ yyerror("server \"%s\": no tls protocols",
+ ssrv->srv_conf.name);
+ serverconfig_free(srv_conf);
+ free(ssrv);
+ return (-2);
+ }
+
+ if (server_tls_load_keypair(ssrv) == -1) {
+ /* Soft fail as there may be no certificate. */
+ log_warnx("%s:%d: server \"%s\": failed to "
+ "load public/private keys", file->name,
+ lineno, ssrv->srv_conf.name);
+
+ remove_locations(srv_conf);
+ serverconfig_free(srv_conf);
+ srv_conf = NULL;
+ free(ssrv);
+ ssrv = NULL;
+ return (0);
+ }
+
+ if (server_tls_load_ca(ssrv) == -1) {
+ yyerror("server \"%s\": failed to load "
+ "ca cert(s)", ssrv->srv_conf.name);
+ serverconfig_free(srv_conf);
+ free(ssrv);
+ return (-2);
+ }
+
+ if (server_tls_load_crl(ssrv) == -1) {
+ yyerror("server \"%s\": failed to load crl(s)",
+ ssrv->srv_conf.name);
+ serverconfig_free(srv_conf);
+ free(ssrv);
+ return (-2);
+ }
+
+ if (server_tls_load_ocsp(ssrv) == -1) {
+ yyerror("server \"%s\": failed to load "
+ "ocsp staple", ssrv->srv_conf.name);
+ serverconfig_free(srv_conf);
+ free(ssrv);
+ return (-2);
+ }
+
+ DPRINTF("adding server \"%s[%u]\"",
+ ssrv->srv_conf.name, ssrv->srv_conf.id);
+
+ TAILQ_INSERT_TAIL(conf->sc_servers, ssrv, srv_entry);
+
+ /*
+ * Add aliases and additional listen addresses as
+ * individual servers.
+ */
+ TAILQ_FOREACH(a, &ssrv->srv_hosts, entry) {
+ /* listen address */
+ if (a->ss.ss_family == AF_UNSPEC)
+ continue;
+ TAILQ_FOREACH(b, &ssrv->srv_hosts, entry) {
+ /* alias name */
+ if (*b->name == '\0' ||
+ (b == &ssrv->srv_conf && b == a))
+ continue;
+
+ if ((sn = server_inherit(ssrv,
+ b, a)) == NULL) {
+ serverconfig_free(srv_conf);
+ free(ssrv);
+ return (-1);
+ }
+
+ DPRINTF("adding server \"%s[%u]\"",
+ sn->srv_conf.name, sn->srv_conf.id);
+
+ TAILQ_INSERT_TAIL(conf->sc_servers,
+ sn, srv_entry);
+ }
+ }
+
+ /* Remove temporary aliases */
+ TAILQ_FOREACH_SAFE(a, &ssrv->srv_hosts, entry, b) {
+ TAILQ_REMOVE(&ssrv->srv_hosts, a, entry);
+ if (a == &ssrv->srv_conf)
+ continue;
+ serverconfig_free(a);
+ free(a);
+ }
+
+ return (0);
}
Index: server.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server.c,v
retrieving revision 1.125
diff -u -p -r1.125 server.c
--- server.c 10 Apr 2021 10:10:07 -0000 1.125
+++ server.c 17 May 2021 14:56:54 -0000
@@ -588,6 +588,8 @@ server_match(struct server *s2, int matc
}
if (s1->srv_conf.port != s2->srv_conf.port)
continue;
+ if (s1->srv_conf.rdomain != s2->srv_conf.rdomain)
+ continue;
if (sockaddr_cmp(
(struct sockaddr *)&s1->srv_conf.ss,
(struct sockaddr *)&s2->srv_conf.ss,
@@ -641,6 +643,7 @@ int
server_socket(struct sockaddr_storage *ss, in_port_t port,
struct server_config *srv_conf, int fd, int reuseport)
{
+ u_int rdomain = srv_conf->rdomain;
struct linger lng;
int s = -1, val;
@@ -655,6 +658,9 @@ server_socket(struct sockaddr_storage *s
/*
* Socket options
*/
+ if (setsockopt(s, SOL_SOCKET, SO_RTABLE, &rdomain,
+ sizeof(rdomain)) == -1)
+ goto bad;
memset(&lng, 0, sizeof(lng));
if (setsockopt(s, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng)) == -1)
goto bad;