From a916ec3792e702a65c7ab0f8019db6ec3e3da103 Mon Sep 17 00:00:00 2001 From: reyk Date: Wed, 30 Jul 2014 10:05:14 +0000 Subject: [PATCH] Add "location" keyword to specify path-specific configuration in servers, for example auto index for a sub-directory only. Internally, a "location" is just a special type of a "virtual" server. --- etc/examples/httpd.conf | 5 ++- usr.sbin/httpd/config.c | 48 ++++++++++++++++++--- usr.sbin/httpd/httpd.conf.5 | 12 +++++- usr.sbin/httpd/httpd.h | 9 +++- usr.sbin/httpd/parse.y | 82 +++++++++++++++++++++++++++++++++--- usr.sbin/httpd/server.c | 18 +++++++- usr.sbin/httpd/server_http.c | 12 ++++-- 7 files changed, 166 insertions(+), 20 deletions(-) diff --git a/etc/examples/httpd.conf b/etc/examples/httpd.conf index 826f0260f92..9c9513981f7 100644 --- a/etc/examples/httpd.conf +++ b/etc/examples/httpd.conf @@ -1,4 +1,4 @@ -# $OpenBSD: httpd.conf,v 1.3 2014/07/29 16:17:28 reyk Exp $ +# $OpenBSD: httpd.conf,v 1.4 2014/07/30 10:05:14 reyk Exp $ # Macros ext_addr="egress" @@ -9,6 +9,9 @@ ext_addr="egress" # Servers server "default" { listen on $ext_addr port 80 + location "/pub/*" { + directory auto index + } } # A name-based "virtual" server on the same address diff --git a/usr.sbin/httpd/config.c b/usr.sbin/httpd/config.c index 762a7a392a5..9e9d2c3754d 100644 --- a/usr.sbin/httpd/config.c +++ b/usr.sbin/httpd/config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.5 2014/07/25 23:30:58 reyk Exp $ */ +/* $OpenBSD: config.c,v 1.6 2014/07/30 10:05:14 reyk Exp $ */ /* * Copyright (c) 2011 - 2014 Reyk Floeter @@ -185,7 +185,8 @@ config_setserver(struct httpd *env, struct server *srv) iov[c].iov_base = &s; iov[c++].iov_len = sizeof(s); - if (id == PROC_SERVER) { + if (id == PROC_SERVER && + (srv->srv_conf.flags & SRVFLAG_LOCATION) == 0) { /* XXX imsg code will close the fd after 1st call */ n = -1; proc_range(ps, id, &n, &m); @@ -216,6 +217,7 @@ config_getserver_config(struct httpd *env, struct server *srv, #endif struct server_config *srv_conf; u_int8_t *p = imsg->data; + u_int f; if ((srv_conf = calloc(1, sizeof(*srv_conf))) == NULL) return (-1); @@ -223,11 +225,37 @@ config_getserver_config(struct httpd *env, struct server *srv, IMSG_SIZE_CHECK(imsg, srv_conf); memcpy(srv_conf, p, sizeof(*srv_conf)); - TAILQ_INSERT_TAIL(&srv->srv_hosts, srv_conf, entry); + if (srv_conf->flags & SRVFLAG_LOCATION) { + /* Inherit configuration from the parent */ + f = SRVFLAG_INDEX|SRVFLAG_NO_INDEX; + if ((srv_conf->flags & f) == 0) { + srv_conf->flags |= srv->srv_conf.flags & f; + (void)strlcpy(srv_conf->index, srv->srv_conf.index, + sizeof(srv_conf->index)); + } + + f = SRVFLAG_AUTO_INDEX|SRVFLAG_NO_AUTO_INDEX; + if ((srv_conf->flags & f) == 0) + srv_conf->flags |= srv->srv_conf.flags & f; + + f = SRVFLAG_DOCROOT; + if ((srv_conf->flags & f) == 0) { + (void)strlcpy(srv_conf->docroot, + srv->srv_conf.docroot, + sizeof(srv_conf->docroot)); + } + + DPRINTF("%s: %s %d received location \"%s\", parent \"%s\"", + __func__, ps->ps_title[privsep_process], ps->ps_instance, + srv_conf->location, srv->srv_conf.name); + } else { + /* Add a new "virtual" server */ + DPRINTF("%s: %s %d received server \"%s\", parent \"%s\"", + __func__, ps->ps_title[privsep_process], ps->ps_instance, + srv_conf->name, srv->srv_conf.name); + } - DPRINTF("%s: %s %d received configuration \"%s\", parent \"%s\"", - __func__, ps->ps_title[privsep_process], ps->ps_instance, - srv_conf->name, srv->srv_conf.name); + TAILQ_INSERT_TAIL(&srv->srv_hosts, srv_conf, entry); return (0); } @@ -247,6 +275,14 @@ config_getserver(struct httpd *env, struct imsg *imsg) memcpy(&srv_conf, p, sizeof(srv_conf)); s = sizeof(srv_conf); + if (srv_conf.flags & SRVFLAG_LOCATION) { + if ((srv = server_byname(srv_conf.name)) == NULL) { + log_warnx("%s: invalid location", __func__); + return (-1); + } + return (config_getserver_config(env, srv, imsg)); + } + /* Check if server with matching listening socket already exists */ if ((srv = server_byaddr((struct sockaddr *) &srv_conf.ss, srv_conf.port)) != NULL) { diff --git a/usr.sbin/httpd/httpd.conf.5 b/usr.sbin/httpd/httpd.conf.5 index 140e9bfbea7..9c056da7485 100644 --- a/usr.sbin/httpd/httpd.conf.5 +++ b/usr.sbin/httpd/httpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: httpd.conf.5,v 1.9 2014/07/30 09:51:40 reyk Exp $ +.\" $OpenBSD: httpd.conf.5,v 1.10 2014/07/30 10:05:14 reyk Exp $ .\" .\" Copyright (c) 2014 Reyk Floeter .\" @@ -130,6 +130,16 @@ Disable the directory index. will neither display nor generate a directory index. .It Ic listen on Ar address Ic port Ar number Set the listen address and port. +.It Ic location Ar path { ... } +Specify server configuration rules for a specific location. +The +.Ar path +argument will be matched against the URL path with shell globbing rules. +A location section may include all of the server configuration rules +except +.Ic listen on +and +.Ic location . .It Ic root Ar directory Set the document root of the server. The diff --git a/usr.sbin/httpd/httpd.h b/usr.sbin/httpd/httpd.h index 081e6387231..c15313243e4 100644 --- a/usr.sbin/httpd/httpd.h +++ b/usr.sbin/httpd/httpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: httpd.h,v 1.17 2014/07/29 16:17:28 reyk Exp $ */ +/* $OpenBSD: httpd.h,v 1.18 2014/07/30 10:05:14 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -276,9 +276,11 @@ SPLAY_HEAD(client_tree, client); #define SRVFLAG_NO_INDEX 0x02 #define SRVFLAG_AUTO_INDEX 0x04 #define SRVFLAG_NO_AUTO_INDEX 0x08 +#define SRVFLAG_DOCROOT 0x10 +#define SRVFLAG_LOCATION 0x20 #define SRVFLAG_BITS \ - "\10\01INDEX\02NO_INDEX\03AUTO_INDEX\04NO_AUTO_INDEX" + "\10\01INDEX\02NO_INDEX\03AUTO_INDEX\04NO_AUTO_INDEX\05LOCATION" #define TCPFLAG_NODELAY 0x01 #define TCPFLAG_NNODELAY 0x02 @@ -299,6 +301,7 @@ struct server_config { char name[MAXHOSTNAMELEN]; char docroot[MAXPATHLEN]; char index[NAME_MAX]; + char location[NAME_MAX]; in_port_t port; struct sockaddr_storage ss; @@ -399,6 +402,8 @@ int server_bufferevent_write(struct client *, void *, size_t); void server_inflight_dec(struct client *, const char *); struct server * server_byaddr(struct sockaddr *, in_port_t); +struct server * + server_byname(const char *); SPLAY_PROTOTYPE(client_tree, client, clt_nodes, server_client_cmp); diff --git a/usr.sbin/httpd/parse.y b/usr.sbin/httpd/parse.y index 97485e31a12..4844272b86e 100644 --- a/usr.sbin/httpd/parse.y +++ b/usr.sbin/httpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.8 2014/07/29 16:17:28 reyk Exp $ */ +/* $OpenBSD: parse.y,v 1.9 2014/07/30 10:05:14 reyk Exp $ */ /* * Copyright (c) 2007 - 2014 Reyk Floeter @@ -94,7 +94,7 @@ static int errors = 0; static int loadcfg = 0; uint32_t last_server_id = 0; -static struct server *srv = NULL; +static struct server *srv = NULL, *parentsrv = NULL; struct serverlist servers; struct media_type media; @@ -126,8 +126,8 @@ typedef struct { %} -%token ALL AUTO DIRECTORY INDEX LISTEN LOG NO ON PORT PREFORK ROOT SERVER -%token TYPES UPDATES VERBOSE +%token ALL AUTO DIRECTORY INDEX LISTEN LOCATION LOG NO ON PORT PREFORK ROOT +%token SERVER TYPES UPDATES VERBOSE %token ERROR INCLUDE %token STRING %token NUMBER @@ -229,9 +229,11 @@ server : SERVER STRING { YYERROR; } srv = s; - } '{' optnl serveropts_l '}' { + SPLAY_INIT(&srv->srv_clients); TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry); + } '{' optnl serveropts_l '}' { + srv = NULL; } ; @@ -244,6 +246,12 @@ serveroptsl : LISTEN ON STRING port { struct address *h; struct server *s; + if (parentsrv != NULL) { + yyerror("listen %s inside location", $3); + free($3); + YYERROR; + } + if (srv->srv_conf.ss.ss_family != AF_UNSPEC) { yyerror("listen address already specified"); free($3); @@ -279,9 +287,72 @@ serveroptsl : LISTEN ON STRING port { YYERROR; } free($2); + srv->srv_conf.flags |= SRVFLAG_DOCROOT; } | DIRECTORY dirflags | DIRECTORY '{' dirflags_l '}' + | LOCATION STRING { + struct server *s; + + if (parentsrv != NULL) { + yyerror("location %s inside location", $2); + free($2); + YYERROR; + } + + if (!loadcfg) { + free($2); + YYACCEPT; + } + + TAILQ_FOREACH(s, conf->sc_servers, srv_entry) + if (strcmp(s->srv_conf.name, + srv->srv_conf.name) == 0 && + strcmp(s->srv_conf.location, $2) == 0) + break; + if (s != NULL) { + yyerror("location %s defined twice", $2); + free($2); + YYERROR; + } + + if ((s = calloc(1, sizeof (*s))) == NULL) + fatal("out of memory"); + + if (strlcpy(s->srv_conf.location, $2, + sizeof(s->srv_conf.location)) >= + sizeof(s->srv_conf.location)) { + yyerror("server location truncated"); + free($2); + free(s); + YYERROR; + } + free($2); + + if (strlcpy(s->srv_conf.name, srv->srv_conf.name, + sizeof(s->srv_conf.name)) >= + sizeof(s->srv_conf.name)) { + yyerror("server name truncated"); + free(s); + YYERROR; + } + + s->srv_conf.id = ++last_server_id; + s->srv_conf.flags = SRVFLAG_LOCATION; + + if (last_server_id == INT_MAX) { + yyerror("too many servers/locations defined"); + free(s); + YYERROR; + } + parentsrv = srv; + srv = s; + SPLAY_INIT(&srv->srv_clients); + TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry); + } '{' optnl serveropts_l '}' { + srv = parentsrv; + parentsrv = NULL; + } ; dirflags_l : dirflags comma dirflags_l @@ -451,6 +522,7 @@ lookup(char *s) { "include", INCLUDE }, { "index", INDEX }, { "listen", LISTEN }, + { "location", LOCATION }, { "log", LOG }, { "no", NO }, { "on", ON }, diff --git a/usr.sbin/httpd/server.c b/usr.sbin/httpd/server.c index cff35e0607b..2b230111cd7 100644 --- a/usr.sbin/httpd/server.c +++ b/usr.sbin/httpd/server.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server.c,v 1.15 2014/07/29 16:38:34 reyk Exp $ */ +/* $OpenBSD: server.c,v 1.16 2014/07/30 10:05:14 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -96,6 +96,9 @@ server_shutdown(void) int server_privinit(struct server *srv) { + if (srv->srv_conf.flags & SRVFLAG_LOCATION) + return (0); + log_debug("%s: adding server %s", __func__, srv->srv_conf.name); if ((srv->srv_s = server_socket_listen(&srv->srv_conf.ss, @@ -196,6 +199,19 @@ server_byaddr(struct sockaddr *addr, in_port_t port) return (NULL); } +struct server * +server_byname(const char *name) +{ + struct server *srv; + + TAILQ_FOREACH(srv, env->sc_servers, srv_entry) { + if (strcmp(srv->srv_conf.name, name) == 0) + return (srv); + } + + return (NULL); +} + int server_socket_af(struct sockaddr_storage *ss, in_port_t port) { diff --git a/usr.sbin/httpd/server_http.c b/usr.sbin/httpd/server_http.c index bfc52725dd7..05e8bf5d755 100644 --- a/usr.sbin/httpd/server_http.c +++ b/usr.sbin/httpd/server_http.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server_http.c,v 1.19 2014/07/25 23:25:38 reyk Exp $ */ +/* $OpenBSD: server_http.c,v 1.20 2014/07/30 10:05:14 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -565,7 +565,8 @@ server_http_host(struct sockaddr_storage *ss, char *buf, size_t len) void server_abort_http(struct client *clt, u_int code, const char *msg) { - struct server_config *srv_conf = clt->clt_srv_conf; + struct server *srv = clt->clt_srv; + struct server_config *srv_conf = &srv->srv_conf; struct bufferevent *bev = clt->clt_bev; const char *httperr = NULL, *text = ""; char *httpmsg, *extraheader = NULL; @@ -716,8 +717,11 @@ server_response(struct httpd *httpd, struct client *clt) if (host != NULL) { /* XXX maybe better to turn srv_hosts into a tree */ TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) { - if (fnmatch(srv_conf->name, host->kv_value, - FNM_CASEFOLD) == 0) { + if (((srv_conf->flags & SRVFLAG_LOCATION) && + fnmatch(srv_conf->location, + desc->http_path, FNM_CASEFOLD) == 0) || + (fnmatch(srv_conf->name, host->kv_value, + FNM_CASEFOLD) == 0)) { /* Replace host configuration */ clt->clt_srv_conf = srv_conf; srv_conf = NULL; -- 2.20.1