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;

Reply via email to