From a357341f2687ab20e50640ee9ba995a8a6ca443b Mon Sep 17 00:00:00 2001 From: Reyk Floeter Date: Fri, 25 Jul 2014 19:28:21 +0200 Subject: sync --- config.c | 53 ++++++++++++++++++++++++++++--- httpd.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- httpd.conf.5 | 14 ++++++-- httpd.h | 23 ++++++++++---- parse.y | 31 +++++++++++++++--- server.c | 56 ++++++++++++++++++++++++-------- server_file.c | 8 ++--- server_http.c | 28 +++++++++++++--- 8 files changed, 271 insertions(+), 42 deletions(-) diff --git a/config.c b/config.c index 8dd395d..eb9e003 100644 --- a/config.c +++ b/config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.3 2014/07/23 13:26:39 reyk Exp $ */ +/* $OpenBSD: config.c,v 1.4 2014/07/25 16:23:19 reyk Exp $ */ /* * Copyright (c) 2011 - 2014 Reyk Floeter @@ -46,6 +46,9 @@ #include "httpd.h" +int config_getserver_config(struct httpd *, struct server *, + struct imsg *); + int config_init(struct httpd *env) { @@ -204,6 +207,31 @@ config_setserver(struct httpd *env, struct server *srv) return (0); } +int +config_getserver_config(struct httpd *env, struct server *srv, + struct imsg *imsg) +{ +#ifdef DEBUG + struct privsep *ps = env->sc_ps; +#endif + struct server_config *srv_conf; + u_int8_t *p = imsg->data; + + if ((srv_conf = calloc(1, sizeof(*srv_conf))) == NULL) + return (-1); + + IMSG_SIZE_CHECK(imsg, srv_conf); + memcpy(srv_conf, p, sizeof(*srv_conf)); + + TAILQ_INSERT_TAIL(&srv->srv_hosts, srv_conf, entry); + + 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); + + return (0); +} + int config_getserver(struct httpd *env, struct imsg *imsg) { @@ -211,21 +239,36 @@ config_getserver(struct httpd *env, struct imsg *imsg) struct privsep *ps = env->sc_ps; #endif struct server *srv; + struct server_config srv_conf; u_int8_t *p = imsg->data; size_t s; + IMSG_SIZE_CHECK(imsg, &srv_conf); + memcpy(&srv_conf, p, sizeof(srv_conf)); + s = sizeof(srv_conf); + + /* Check if server with matching listening socket already exists */ + if ((srv = server_byaddr((struct sockaddr *) + &srv_conf.ss)) != NULL) { + /* Add "host" to existing listening server */ + close(imsg->fd); + return (config_getserver_config(env, + srv, imsg)); + } + + /* Otherwise create a new server */ if ((srv = calloc(1, sizeof(*srv))) == NULL) { close(imsg->fd); return (-1); } - IMSG_SIZE_CHECK(imsg, &srv->srv_conf); - memcpy(&srv->srv_conf, p, sizeof(srv->srv_conf)); - s = sizeof(srv->srv_conf); - + memcpy(&srv->srv_conf, &srv_conf, sizeof(srv->srv_conf)); srv->srv_s = imsg->fd; SPLAY_INIT(&srv->srv_clients); + TAILQ_INIT(&srv->srv_hosts); + + TAILQ_INSERT_TAIL(&srv->srv_hosts, &srv->srv_conf, entry); TAILQ_INSERT_TAIL(env->sc_servers, srv, srv_entry); DPRINTF("%s: %s %d received configuration \"%s\"", __func__, diff --git a/httpd.c b/httpd.c index c87c2ad..22ae5f9 100644 --- a/httpd.c +++ b/httpd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: httpd.c,v 1.6 2014/07/23 23:10:27 reyk Exp $ */ +/* $OpenBSD: httpd.c,v 1.8 2014/07/25 16:23:19 reyk Exp $ */ /* * Copyright (c) 2014 Reyk Floeter @@ -579,6 +579,102 @@ get_data(u_int8_t *ptr, size_t len) return (data); } +int +sockaddr_cmp(struct sockaddr *a, struct sockaddr *b, int prefixlen) +{ + struct sockaddr_in *a4, *b4; + struct sockaddr_in6 *a6, *b6; + u_int32_t av[4], bv[4], mv[4]; + + if (a->sa_family == AF_UNSPEC || b->sa_family == AF_UNSPEC) + return (0); + else if (a->sa_family > b->sa_family) + return (1); + else if (a->sa_family < b->sa_family) + return (-1); + + if (prefixlen == -1) + memset(&mv, 0xff, sizeof(mv)); + + switch (a->sa_family) { + case AF_INET: + a4 = (struct sockaddr_in *)a; + b4 = (struct sockaddr_in *)b; + + av[0] = a4->sin_addr.s_addr; + bv[0] = b4->sin_addr.s_addr; + if (prefixlen != -1) + mv[0] = prefixlen2mask(prefixlen); + + if ((av[0] & mv[0]) > (bv[0] & mv[0])) + return (1); + if ((av[0] & mv[0]) < (bv[0] & mv[0])) + return (-1); + break; + case AF_INET6: + a6 = (struct sockaddr_in6 *)a; + b6 = (struct sockaddr_in6 *)b; + + memcpy(&av, &a6->sin6_addr.s6_addr, 16); + memcpy(&bv, &b6->sin6_addr.s6_addr, 16); + if (prefixlen != -1) + prefixlen2mask6(prefixlen, mv); + + if ((av[3] & mv[3]) > (bv[3] & mv[3])) + return (1); + if ((av[3] & mv[3]) < (bv[3] & mv[3])) + return (-1); + if ((av[2] & mv[2]) > (bv[2] & mv[2])) + return (1); + if ((av[2] & mv[2]) < (bv[2] & mv[2])) + return (-1); + if ((av[1] & mv[1]) > (bv[1] & mv[1])) + return (1); + if ((av[1] & mv[1]) < (bv[1] & mv[1])) + return (-1); + if ((av[0] & mv[0]) > (bv[0] & mv[0])) + return (1); + if ((av[0] & mv[0]) < (bv[0] & mv[0])) + return (-1); + break; + } + + return (0); +} + +u_int32_t +prefixlen2mask(u_int8_t prefixlen) +{ + if (prefixlen == 0) + return (0); + + if (prefixlen > 32) + prefixlen = 32; + + return (htonl(0xffffffff << (32 - prefixlen))); +} + +struct in6_addr * +prefixlen2mask6(u_int8_t prefixlen, u_int32_t *mask) +{ + static struct in6_addr s6; + int i; + + if (prefixlen > 128) + prefixlen = 128; + + bzero(&s6, sizeof(s6)); + for (i = 0; i < prefixlen / 8; i++) + s6.s6_addr[i] = 0xff; + i = prefixlen % 8; + if (i) + s6.s6_addr[prefixlen / 8] = 0xff00 >> i; + + memcpy(mask, &s6, sizeof(s6)); + + return (&s6); +} + int accept_reserve(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int reserve, volatile int *counter) @@ -720,8 +816,6 @@ kv_purge(struct kvtree *keys) void kv_free(struct kv *kv) { - if (kv->kv_type == KEY_TYPE_NONE) - return; if (kv->kv_key != NULL) { free(kv->kv_key); } diff --git a/httpd.conf.5 b/httpd.conf.5 index afef082..cbd1004 100644 --- a/httpd.conf.5 +++ b/httpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: httpd.conf.5,v 1.5 2014/07/22 19:03:21 jmc Exp $ +.\" $OpenBSD: httpd.conf.5,v 1.6 2014/07/25 15:47:11 reyk Exp $ .\" .\" Copyright (c) 2014 Reyk Floeter .\" @@ -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: July 22 2014 $ +.Dd $Mdocdate: July 25 2014 $ .Dt HTTPD.CONF 5 .Os .Sh NAME @@ -115,6 +115,16 @@ The following general table options are available: .Bl -tag -width Ds .It Ic listen on Ar address Ic port Ar number Set the listen address and port. +.It Ic root Ar directory +Set the document root of the server. +The +.Ar directory +is a pathname within the +.Xr chroot 2 +root directory of +.Nm httpd . +If not specified, it defaults to +.Pa /htdocs . .El .Sh TYPES Configure the supported media types. diff --git a/httpd.h b/httpd.h index ae275bf..cc10bfb 100644 --- a/httpd.h +++ b/httpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: httpd.h,v 1.7 2014/07/23 19:03:56 reyk Exp $ */ +/* $OpenBSD: httpd.h,v 1.12 2014/07/25 16:23:19 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -123,6 +123,7 @@ struct portrange { struct address { struct sockaddr_storage ss; int ipproto; + int prefixlen; struct portrange port; char ifname[IFNAMSIZ]; TAILQ_ENTRY(address) entry; @@ -252,8 +253,9 @@ struct privsep_proc { struct client { u_int32_t clt_id; pid_t clt_pid; - void *clt_server; - u_int32_t clt_serverid; + void *clt_srv; + void *clt_srv_conf; + u_int32_t clt_srv_id; int clt_s; in_port_t clt_port; @@ -287,14 +289,20 @@ struct server_config { u_int32_t id; u_int32_t flags; char name[MAXHOSTNAMELEN]; + char docroot[MAXPATHLEN]; in_port_t port; struct sockaddr_storage ss; + int prefixlen; struct timeval timeout; + + TAILQ_ENTRY(server_config) entry; }; +TAILQ_HEAD(serverhosts, server_config); struct server { TAILQ_ENTRY(server) srv_entry; struct server_config srv_conf; + struct serverhosts srv_hosts; u_int8_t srv_tcpflags; int srv_tcpbufsiz; @@ -303,10 +311,6 @@ struct server { u_int8_t srv_tcpipminttl; int srv_s; - struct bufferevent *srv_bev; - int srv_dsts; - struct bufferevent *srv_dstbev; - struct event srv_ev; struct event srv_evt; @@ -382,6 +386,8 @@ int server_bufferevent_write_chunk(struct client *, int server_bufferevent_add(struct event *, int); int server_bufferevent_write(struct client *, void *, size_t); void server_inflight_dec(struct client *, const char *); +struct server * + server_byaddr(struct sockaddr *); SPLAY_PROTOTYPE(client_tree, client, clt_nodes, server_client_cmp); @@ -422,6 +428,9 @@ int imsg_compose_event(struct imsgev *, u_int16_t, u_int32_t, void socket_rlimit(int); char *get_string(u_int8_t *, size_t); void *get_data(u_int8_t *, size_t); +int sockaddr_cmp(struct sockaddr *, struct sockaddr *, int); +struct in6_addr *prefixlen2mask6(u_int8_t, u_int32_t *); +u_int32_t prefixlen2mask(u_int8_t); int accept_reserve(int, struct sockaddr *, socklen_t *, int, volatile int *); struct kv *kv_add(struct kvtree *, char *, char *); diff --git a/parse.y b/parse.y index be355af..1105e55 100644 --- a/parse.y +++ b/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.3 2014/07/23 22:02:02 reyk Exp $ */ +/* $OpenBSD: parse.y,v 1.7 2014/07/25 17:04:47 reyk Exp $ */ /* * Copyright (c) 2007 - 2014 Reyk Floeter @@ -126,7 +126,7 @@ typedef struct { %} -%token ALL PORT LISTEN PREFORK SERVER ERROR INCLUDE LOG VERBOSE ON TYPES +%token ALL PORT LISTEN PREFORK ROOT SERVER ERROR LOG VERBOSE ON TYPES %token UPDATES INCLUDE %token STRING %token NUMBER @@ -215,6 +215,8 @@ server : SERVER STRING { } free($2); + strlcpy(s->srv_conf.docroot, HTTPD_DOCROOT, + sizeof(s->srv_conf.docroot)); s->srv_conf.id = ++last_server_id; s->srv_conf.timeout.tv_sec = SERVER_TIMEOUT; @@ -262,8 +264,19 @@ serveroptsl : LISTEN ON STRING port { 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; host_free(&al); } + | ROOT STRING { + if (strlcpy(srv->srv_conf.docroot, $2, + sizeof(srv->srv_conf.docroot)) >= + sizeof(srv->srv_conf.docroot)) { + yyerror("document root too long"); + free($2); + YYERROR; + } + free($2); + } ; types : TYPES '{' optnl mediaopts_l '}' @@ -399,6 +412,7 @@ lookup(char *s) { "on", ON }, { "port", PORT }, { "prefork", PREFORK }, + { "root", ROOT }, { "server", SERVER }, { "types", TYPES }, { "updates", UPDATES } @@ -924,7 +938,10 @@ host_v4(const char *s) sain->sin_len = sizeof(struct sockaddr_in); sain->sin_family = AF_INET; sain->sin_addr.s_addr = ina.s_addr; - + if (sain->sin_addr.s_addr == INADDR_ANY) + h->prefixlen = 0; /* 0.0.0.0 address */ + else + h->prefixlen = -1; /* host address */ return (h); } @@ -950,7 +967,11 @@ host_v6(const char *s) sizeof(sa_in6->sin6_addr)); sa_in6->sin6_scope_id = ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; - + if (memcmp(&sa_in6->sin6_addr, &in6addr_any, + sizeof(sa_in6->sin6_addr)) == 0) + h->prefixlen = 0; /* any address */ + else + h->prefixlen = -1; /* host address */ freeaddrinfo(res); } @@ -1003,6 +1024,7 @@ host_dns(const char *s, struct addresslist *al, int max, if (ipproto != -1) h->ipproto = ipproto; h->ss.ss_family = res->ai_family; + h->prefixlen = -1; /* host address */ if (res->ai_family == AF_INET) { sain = (struct sockaddr_in *)&h->ss; @@ -1065,6 +1087,7 @@ host_if(const char *s, struct addresslist *al, int max, if (ipproto != -1) h->ipproto = ipproto; h->ss.ss_family = af; + h->prefixlen = -1; /* host address */ if (af == AF_INET) { sain = (struct sockaddr_in *)&h->ss; diff --git a/server.c b/server.c index 38151ea..21ccff2 100644 --- a/server.c +++ b/server.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server.c,v 1.7 2014/07/23 13:26:39 reyk Exp $ */ +/* $OpenBSD: server.c,v 1.11 2014/07/25 16:23:19 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -151,7 +151,8 @@ server_launch(void) void server_purge(struct server *srv) { - struct client *clt; + struct client *clt; + struct server_config *srv_conf; /* shutdown and remove server */ if (event_initialized(&srv->srv_ev)) @@ -167,9 +168,33 @@ server_purge(struct server *srv) SPLAY_ROOT(&srv->srv_clients)) != NULL) server_close(clt, NULL); + /* cleanup hosts */ + while ((srv_conf = + TAILQ_FIRST(&srv->srv_hosts)) != NULL) { + TAILQ_REMOVE(&srv->srv_hosts, srv_conf, entry); + + /* It might point to our own "default" entry */ + if (srv_conf != &srv->srv_conf) + free(srv_conf); + } + free(srv); } +struct server * +server_byaddr(struct sockaddr *addr) +{ + struct server *srv; + + TAILQ_FOREACH(srv, env->sc_servers, srv_entry) { + if (sockaddr_cmp((struct sockaddr *)&srv->srv_conf.ss, + addr, srv->srv_conf.prefixlen) == 0) + return (srv); + } + + return (NULL); +} + int server_socket_af(struct sockaddr_storage *ss, in_port_t port) { @@ -316,9 +341,9 @@ server_socket_listen(struct sockaddr_storage *ss, in_port_t port, void server_input(struct client *clt) { - struct server *srv = clt->clt_server; - evbuffercb inrd = server_read; - evbuffercb inwr = server_write; + struct server_config *srv_conf = clt->clt_srv_conf; + evbuffercb inrd = server_read; + evbuffercb inwr = server_write; if (server_httpdesc_init(clt) == -1) { server_close(clt, @@ -340,7 +365,7 @@ server_input(struct client *clt) } bufferevent_settimeout(clt->clt_bev, - srv->srv_conf.timeout.tv_sec, srv->srv_conf.timeout.tv_sec); + srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec); bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE); } @@ -472,9 +497,10 @@ server_accept(int fd, short event, void *arg) clt->clt_s = s; clt->clt_fd = -1; clt->clt_toread = TOREAD_UNLIMITED; - clt->clt_server = srv; + clt->clt_srv = srv; + clt->clt_srv_conf = &srv->srv_conf; clt->clt_id = ++server_cltid; - clt->clt_serverid = srv->srv_conf.id; + clt->clt_srv_id = srv->srv_conf.id; clt->clt_pid = getpid(); clt->clt_inflight = 1; switch (ss.ss_family) { @@ -545,11 +571,15 @@ server_inflight_dec(struct client *clt, const char *why) void server_close(struct client *clt, const char *msg) { - char ibuf[128], obuf[128], *ptr = NULL; - struct server *srv = clt->clt_server; + char ibuf[128], obuf[128], *ptr = NULL; + struct server *srv = clt->clt_srv; + struct server_config *srv_conf = clt->clt_srv_conf; SPLAY_REMOVE(client_tree, &srv->srv_clients, clt); + /* free the HTTP descriptors incl. headers */ + server_close_http(clt); + event_del(&clt->clt_ev); if (clt->clt_bev != NULL) bufferevent_disable(clt->clt_bev, EV_READ|EV_WRITE); @@ -560,13 +590,13 @@ server_close(struct client *clt, const char *msg) memset(&ibuf, 0, sizeof(ibuf)); memset(&obuf, 0, sizeof(obuf)); (void)print_host(&clt->clt_ss, ibuf, sizeof(ibuf)); - (void)print_host(&srv->srv_conf.ss, obuf, sizeof(obuf)); + (void)print_host(&srv_conf->ss, obuf, sizeof(obuf)); if (EVBUFFER_LENGTH(clt->clt_log) && evbuffer_add_printf(clt->clt_log, "\r\n") != -1) ptr = evbuffer_readline(clt->clt_log); log_info("server %s, " "client %d (%d active), %s -> %s:%d, " - "%s%s%s", srv->srv_conf.name, clt->clt_id, server_clients, + "%s%s%s", srv_conf->name, clt->clt_id, server_clients, ibuf, obuf, ntohs(clt->clt_port), msg, ptr == NULL ? "" : ",", ptr == NULL ? "" : ptr); if (ptr != NULL) @@ -575,7 +605,7 @@ server_close(struct client *clt, const char *msg) if (clt->clt_bev != NULL) bufferevent_free(clt->clt_bev); - else if (clt->clt_output != NULL) + if (clt->clt_output != NULL) evbuffer_free(clt->clt_output); if (clt->clt_file != NULL) diff --git a/server_file.c b/server_file.c index 980efa4..c4814a0 100644 --- a/server_file.c +++ b/server_file.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server_file.c,v 1.10 2014/07/23 22:20:37 reyk Exp $ */ +/* $OpenBSD: server_file.c,v 1.13 2014/07/25 13:10:18 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -119,14 +119,14 @@ int server_file(struct httpd *env, struct client *clt) { struct http_descriptor *desc = clt->clt_desc; - struct server *srv = clt->clt_server; + struct server_config *srv_conf = clt->clt_srv_conf; struct media_type *media; const char *errstr = NULL; int fd = -1, ret; char path[MAXPATHLEN]; struct stat st; - if (canonicalize_path(HTTPD_DOCROOT, + if (canonicalize_path(srv_conf->docroot, desc->http_path, path, sizeof(path)) == NULL) { /* Do not echo the uncanonicalized path */ server_abort_http(clt, 500, "invalid request path"); @@ -171,7 +171,7 @@ server_file(struct httpd *env, struct client *clt) } bufferevent_settimeout(clt->clt_file, - srv->srv_conf.timeout.tv_sec, srv->srv_conf.timeout.tv_sec); + srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec); bufferevent_enable(clt->clt_file, EV_READ); bufferevent_disable(clt->clt_bev, EV_READ); diff --git a/server_http.c b/server_http.c index 6e15456..98edb96 100644 --- a/server_http.c +++ b/server_http.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server_http.c,v 1.10 2014/07/23 21:43:12 reyk Exp $ */ +/* $OpenBSD: server_http.c,v 1.14 2014/07/25 16:23:19 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -110,6 +110,7 @@ server_httpdesc_free(struct http_descriptor *desc) desc->http_version = NULL; } kv_purge(&desc->http_headers); + desc->http_lastheader = NULL; } void @@ -517,7 +518,7 @@ server_reset_http(struct client *clt) void server_abort_http(struct client *clt, u_int code, const char *msg) { - struct server *srv = clt->clt_server; + struct server_config *srv_conf = clt->clt_srv_conf; struct bufferevent *bev = clt->clt_bev; const char *httperr = NULL, *text = ""; char *httpmsg, *extraheader = NULL; @@ -533,7 +534,7 @@ server_abort_http(struct client *clt, u_int code, const char *msg) goto done; /* Some system information */ - if (print_host(&srv->srv_conf.ss, hbuf, sizeof(hbuf)) == NULL) + if (print_host(&srv_conf->ss, hbuf, sizeof(hbuf)) == NULL) goto done; /* RFC 2616 "tolerates" asctime() */ @@ -595,7 +596,7 @@ server_abort_http(struct client *clt, u_int code, const char *msg) code, httperr, tmbuf, HTTPD_SERVERNAME, extraheader == NULL ? "" : extraheader, code, httperr, style, httperr, text, - HTTPD_SERVERNAME, hbuf, ntohs(srv->srv_conf.port)) == -1) + HTTPD_SERVERNAME, hbuf, ntohs(srv_conf->port)) == -1) goto done; /* Dump the message without checking for success */ @@ -627,6 +628,8 @@ int server_response(struct httpd *httpd, struct client *clt) { struct http_descriptor *desc = clt->clt_desc; + struct server *srv = clt->clt_srv; + struct server_config *srv_conf; struct kv *kv, key; int ret; @@ -656,6 +659,23 @@ server_response(struct httpd *httpd, struct client *clt) clt->clt_persist = 0; } + /* + * Do we have a Host header and matching configuration? + * XXX the Host can also appear in the URL path. + */ + key.kv_key = "Host"; + if ((kv = kv_find(&desc->http_headers, &key)) != NULL) { + /* XXX maybe better to turn srv_hosts into a tree */ + TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) { + if (fnmatch(srv_conf->name, kv->kv_value, + FNM_CASEFOLD) == 0) { + /* Replace host configuration */ + clt->clt_srv_conf = srv_conf; + break; + } + } + } + if ((ret = server_file(httpd, clt)) == -1) return (-1); -- cgit v1.2.3-54-g00ecf