Support alias names and multiple listen statements per server block.
authorreyk <reyk@openbsd.org>
Sat, 3 Jan 2015 15:49:18 +0000 (15:49 +0000)
committerreyk <reyk@openbsd.org>
Sat, 3 Jan 2015 15:49:18 +0000 (15:49 +0000)
The implementation is done in the parser by expanding each
alias/listen into an independent server configuration; this makes it
easier to handle internally without adding additional loops or
conditions.

OK florian@

usr.sbin/httpd/config.c
usr.sbin/httpd/httpd.conf.5
usr.sbin/httpd/parse.y

index d651a02..14fe63e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: config.c,v 1.26 2014/12/21 00:54:49 guenther Exp $    */
+/*     $OpenBSD: config.c,v 1.27 2015/01/03 15:49:18 reyk Exp $        */
 
 /*
  * Copyright (c) 2011 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -174,7 +174,9 @@ config_setserver(struct httpd *env, struct server *srv)
                if ((what & CONFIG_SERVERS) == 0 || id == privsep_process)
                        continue;
 
-               DPRINTF("%s: sending server \"%s[%u]\" to %s fd %d", __func__,
+               DPRINTF("%s: sending %s \"%s[%u]\" to %s fd %d", __func__,
+                   (srv->srv_conf.flags & SRVFLAG_LOCATION) ?
+                   "location" : "server",
                    srv->srv_conf.name, srv->srv_conf.id,
                    ps->ps_title[id], srv->srv_s);
 
index 222b3dc..ff0152a 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: httpd.conf.5,v 1.40 2014/12/28 13:53:23 reyk Exp $
+.\"    $OpenBSD: httpd.conf.5,v 1.41 2015/01/03 15:49:18 reyk Exp $
 .\"
 .\" Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
 .\"
@@ -14,7 +14,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: December 28 2014 $
+.Dd $Mdocdate: January 3 2015 $
 .Dt HTTPD.CONF 5
 .Os
 .Sh NAME
@@ -135,6 +135,10 @@ must have a
 .Ar name
 and include one or more lines of the following syntax:
 .Bl -tag -width Ds
+.It Ic alias Ar name
+Specify an additional alias
+.Ar name
+for this server.
 .It Ic connection Ar option
 Set the specified options and limits for HTTP connections.
 Valid options are:
@@ -180,6 +184,7 @@ and defaults to
 .Pa /run/slowcgi.sock .
 .It Ic listen on Ar address Oo Ic tls Oc Ic port Ar number
 Set the listen address and port.
+This statement can be specified multiple times.
 .It Ic location Ar path Brq ...
 Specify server configuration rules for a specific location.
 The
@@ -391,6 +396,13 @@ If the same address is repeated multiple times in the
 statement,
 the server will be matched based on the requested host name.
 .Bd -literal -offset indent
+server "www.example.com" {
+       alias "example.com"
+       listen on * port 80
+       listen on * tls port 443
+       root "/htdocs/www.example.com"
+}
+
 server "www.a.example.com" {
        listen on 203.0.113.1 port 80
        root "/htdocs/www.a.example.com"
index 943e00a..145d99c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.46 2014/12/21 00:54:49 guenther Exp $     */
+/*     $OpenBSD: parse.y,v 1.47 2015/01/03 15:49:18 reyk Exp $ */
 
 /*
  * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -106,6 +106,8 @@ int          host_if(const char *, struct addresslist *,
 int             host(const char *, struct addresslist *,
                    int, struct portrange *, const char *, int);
 void            host_free(struct addresslist *);
+struct server  *server_inherit(struct server *, const char *,
+                   struct server_config *);
 int             getservice(char *);
 int             is_if_in_group(const char *, const char *);
 
@@ -125,10 +127,10 @@ typedef struct {
 
 %}
 
-%token ACCESS AUTO BACKLOG BODY BUFFER CERTIFICATE CHROOT CIPHERS COMMON
+%token ACCESS ALIAS AUTO BACKLOG BODY BUFFER CERTIFICATE CHROOT CIPHERS COMMON
 %token COMBINED CONNECTION DIRECTORY ERR FCGI INDEX IP KEY LISTEN LOCATION
 %token LOG LOGDIR MAXIMUM NO NODELAY ON PORT PREFORK REQUEST REQUESTS ROOT
-%token SACK SERVER SOCKET STYLE SYSLOG TCP TIMEOUT TLS TYPES 
+%token SACK SERVER SOCKET STYLE SYSLOG TCP TIMEOUT TLS TYPES
 %token ERROR INCLUDE
 %token <v.string>      STRING
 %token  <v.number>     NUMBER
@@ -247,8 +249,14 @@ server             : SERVER STRING         {
                        srv_conf = &srv->srv_conf;
 
                        SPLAY_INIT(&srv->srv_clients);
+                       TAILQ_INIT(&srv->srv_hosts);
+
+                       TAILQ_INSERT_TAIL(&srv->srv_hosts, srv_conf, entry);
                } '{' optnl serveropts_l '}'    {
-                       struct server   *s = NULL;
+                       struct server           *s = NULL, *sn;
+                       struct server_config    *a, *b;
+
+                       srv_conf = &srv->srv_conf;
 
                        TAILQ_FOREACH(s, conf->sc_servers, srv_entry) {
                                if ((s->srv_conf.flags &
@@ -290,6 +298,44 @@ server             : SERVER STRING         {
 
                        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->name, 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);
+                       }
+
                        srv = NULL;
                        srv_conf = NULL;
                }
@@ -302,7 +348,7 @@ serveropts_l        : serveropts_l serveroptsl nl
 serveroptsl    : LISTEN ON STRING opttls port {
                        struct addresslist       al;
                        struct address          *h;
-                       struct server           *s;
+                       struct server_config    *s_conf, *alias = NULL;
 
                        if (parentsrv != NULL) {
                                yyerror("listen %s inside location", $3);
@@ -311,11 +357,14 @@ serveroptsl       : LISTEN ON STRING opttls port {
                        }
 
                        if (srv->srv_conf.ss.ss_family != AF_UNSPEC) {
-                               yyerror("listen address already specified");
-                               free($3);
-                               YYERROR;
+                               if ((alias = calloc(1,
+                                   sizeof(*alias))) == NULL)
+                                       fatal("out of memory");
+
+                               /* Add as an alias */
+                               s_conf = alias;
                        } else
-                               s = srv;
+                               s_conf = &srv->srv_conf;
                        if ($5.op != PF_OP_EQ) {
                                yyerror("invalid port");
                                free($3);
@@ -330,16 +379,43 @@ serveroptsl       : LISTEN ON STRING opttls port {
                        }
                        free($3);
                        h = TAILQ_FIRST(&al);
-                       memcpy(&srv->srv_conf.ss, &h->ss,
-                           sizeof(s->srv_conf.ss));
-                       s->srv_conf.port = h->port.val[0];
-                       s->srv_conf.prefixlen = h->prefixlen;
+                       memcpy(&s_conf->ss, &h->ss, sizeof(s_conf->ss));
+                       s_conf->port = h->port.val[0];
+                       s_conf->prefixlen = h->prefixlen;
                        host_free(&al);
 
                        if ($4) {
-                               s->srv_conf.flags |= SRVFLAG_TLS;
+                               s_conf->flags |= SRVFLAG_TLS;
+                       }
+
+                       if (alias != NULL) {
+                               TAILQ_INSERT_TAIL(&srv->srv_hosts,
+                                   alias, entry);
                        }
                }
+               | ALIAS STRING          {
+                       struct server_config    *alias;
+
+                       if (parentsrv != NULL) {
+                               yyerror("alias inside location");
+                               free($2);
+                               YYERROR;
+                       }
+
+                       if ((alias = calloc(1, sizeof(*alias))) == NULL)
+                               fatal("out of memory");
+
+                       if (strlcpy(alias->name, $2, sizeof(alias->name)) >=
+                           sizeof(alias->name)) {
+                               yyerror("server alias truncated");
+                               free($2);
+                               free(alias);
+                               YYERROR;
+                       }
+                       free($2);
+
+                       TAILQ_INSERT_TAIL(&srv->srv_hosts, alias, entry);
+               }
                | TCP                   {
                        if (parentsrv != NULL) {
                                yyerror("tcp flags inside location");
@@ -852,6 +928,7 @@ lookup(char *s)
        /* this has to be sorted always */
        static const struct keywords keywords[] = {
                { "access",             ACCESS },
+               { "alias",              ALIAS },
                { "auto",               AUTO },
                { "backlog",            BACKLOG },
                { "body",               BODY },
@@ -1646,6 +1723,99 @@ host_free(struct addresslist *al)
        }
 }
 
+struct server *
+server_inherit(struct server *src, const char *name,
+    struct server_config *addr)
+{
+       struct server   *dst, *s, *dstl;
+
+       if ((dst = calloc(1, sizeof(*dst))) == NULL)
+               fatal("out of memory");
+
+       /* Copy the source server and assign a new Id */
+       memcpy(&dst->srv_conf, &src->srv_conf, sizeof(dst->srv_conf));
+       if ((dst->srv_conf.tls_cert_file =
+           strdup(src->srv_conf.tls_cert_file)) == NULL)
+               fatal("out of memory");
+       if ((dst->srv_conf.tls_key_file =
+           strdup(src->srv_conf.tls_key_file)) == NULL)
+               fatal("out of memory");
+
+       dst->srv_conf.id = ++last_server_id;
+       if (last_server_id == INT_MAX) {
+               yyerror("too many servers defined");
+               free(dst);
+               return (NULL);
+       }
+
+       /* Now set alias and listen address */
+       strlcpy(dst->srv_conf.name, name, sizeof(dst->srv_conf.name));
+       memcpy(&dst->srv_conf.ss, &addr->ss, sizeof(dst->srv_conf.ss));
+       dst->srv_conf.port = addr->port;
+       dst->srv_conf.prefixlen = addr->prefixlen;
+       if (addr->flags & SRVFLAG_TLS)
+               dst->srv_conf.flags |= SRVFLAG_TLS;
+       else
+               dst->srv_conf.flags &= ~SRVFLAG_TLS;
+
+       if (server_tls_load_keypair(dst) == -1) {
+               yyerror("failed to load public/private keys "
+                   "for server %s", dst->srv_conf.name);
+               serverconfig_free(&dst->srv_conf);
+               free(dst);
+               return (NULL);
+       }
+
+       /* Check if the new server already exists */
+       TAILQ_FOREACH(s, conf->sc_servers, srv_entry) {
+               if ((s->srv_conf.flags &
+                   SRVFLAG_LOCATION) == 0 &&
+                   strcmp(s->srv_conf.name,
+                   dst->srv_conf.name) == 0 &&
+                   s->srv_conf.port == dst->srv_conf.port &&
+                   sockaddr_cmp(
+                   (struct sockaddr *)&s->srv_conf.ss,
+                   (struct sockaddr *)&dst->srv_conf.ss,
+                   s->srv_conf.prefixlen) == 0)
+                       break;
+       }
+       if (s != NULL) {
+               yyerror("server \"%s\" defined twice",
+                   dst->srv_conf.name);
+               serverconfig_free(&dst->srv_conf);
+               free(dst);
+               return (NULL);
+       }
+
+       /* Copy all the locations of the source server */
+       TAILQ_FOREACH(s, conf->sc_servers, srv_entry) {
+               if (!(s->srv_conf.flags & SRVFLAG_LOCATION &&
+                   s->srv_conf.id == src->srv_conf.id))
+                       continue;
+
+               if ((dstl = calloc(1, sizeof(*dstl))) == NULL)
+                       fatal("out of memory");
+
+               memcpy(&dstl->srv_conf, &s->srv_conf, sizeof(dstl->srv_conf));
+               strlcpy(dstl->srv_conf.name, name, sizeof(dstl->srv_conf.name));
+
+               /* Copy the new Id and listen address */
+               dstl->srv_conf.id = dst->srv_conf.id;
+               memcpy(&dstl->srv_conf.ss, &addr->ss,
+                   sizeof(dstl->srv_conf.ss));
+               dstl->srv_conf.port = addr->port;
+               dstl->srv_conf.prefixlen = addr->prefixlen;
+
+               DPRINTF("adding location \"%s\" for \"%s[%u]\"",
+                   dstl->srv_conf.location,
+                   dstl->srv_conf.name, dstl->srv_conf.id);
+
+               TAILQ_INSERT_TAIL(conf->sc_servers, dstl, srv_entry);
+       }
+
+       return (dst);
+}
+
 int
 getservice(char *n)
 {