On Mon, May 17, 2021 at 05:11:41PM +0200, Peter J. Philipp wrote:
> 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.
>
I don't think httpd should handle rdomain in its config but instead you
should run multiple httpd processes one for each rdomain.
Not every daemon needs to support running in multiple rdomains at once.
> 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;
>
--
:wq Claudio