summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2014-07-30 10:05:15 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2014-07-30 10:05:15 +0000
commitbac000acea86b46f8c7c5176257dca0c9977a05c (patch)
treedc0109f11a5706092b8f3a5bf9a96892533b8ee5
parent229bb6da6741516656d06e0243c39d62fcb0c7b5 (diff)
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.
-rw-r--r--etc/examples/httpd.conf5
-rw-r--r--usr.sbin/httpd/config.c48
-rw-r--r--usr.sbin/httpd/httpd.conf.512
-rw-r--r--usr.sbin/httpd/httpd.h9
-rw-r--r--usr.sbin/httpd/parse.y82
-rw-r--r--usr.sbin/httpd/server.c18
-rw-r--r--usr.sbin/httpd/server_http.c12
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 <reyk@openbsd.org>
@@ -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 <reyk@openbsd.org>
.\"
@@ -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 <reyk@openbsd.org>
@@ -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 <reyk@openbsd.org>
@@ -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 <v.string> STRING
%token <v.number> 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 <reyk@openbsd.org>
@@ -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 <reyk@openbsd.org>
@@ -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;