aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReyk Floeter <reyk@esdenera.com>2014-08-18 09:43:48 +0200
committerReyk Floeter <reyk@esdenera.com>2014-08-18 09:43:48 +0200
commit5eb16802a795fa3c1fcb936f3261776db11a8a3c (patch)
tree43526a300b9ad28b87d0c1dcdc84dacd574c2cbc
parentdf9a638659941f0e786b66a8262d676a0d45a59f (diff)
downloadhttpd-5eb16802a795fa3c1fcb936f3261776db11a8a3c.tar.gz
httpd-5eb16802a795fa3c1fcb936f3261776db11a8a3c.zip
sync
-rw-r--r--Makefile12
-rw-r--r--config.c142
-rw-r--r--control.c7
-rw-r--r--http.h61
-rw-r--r--httpd.862
-rw-r--r--httpd.c107
-rw-r--r--httpd.conf.5206
-rw-r--r--httpd.h181
-rw-r--r--log.c4
-rw-r--r--logger.c310
-rw-r--r--parse.y478
-rw-r--r--proc.c9
-rw-r--r--server.c543
-rw-r--r--server_fcgi.c643
-rw-r--r--server_file.c285
-rw-r--r--server_http.c220
16 files changed, 2946 insertions, 324 deletions
diff --git a/Makefile b/Makefile
index a6c482f..63d50f4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,15 +1,15 @@
-# $OpenBSD: Makefile,v 1.20 2014/07/12 23:34:54 reyk Exp $
+# $OpenBSD: Makefile,v 1.25 2014/08/04 17:38:12 reyk Exp $
PROG= httpd
SRCS= parse.y
-SRCS+= config.c control.c httpd.c log.c proc.c
-SRCS+= server.c server_http.c server_file.c
+SRCS+= config.c control.c httpd.c log.c logger.c proc.c
+SRCS+= server.c server_http.c server_file.c server_fcgi.c
MAN= httpd.8 httpd.conf.5
-LDADD= -levent -lssl -lcrypto -lutil
-DPADD= ${LIBEVENT} ${LIBSSL} ${LIBCRYPTO} ${LIBUTIL}
+LDADD= -levent -lressl -lssl -lcrypto -lutil
+DPADD= ${LIBEVENT} ${LIBRESSL} ${LIBSSL} ${LIBCRYPTO} ${LIBUTIL}
#DEBUG= -g -DDEBUG=3
-CFLAGS+= -Wall -I${.CURDIR} -Werror
+CFLAGS+= -Wall -I${.CURDIR}
CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
CFLAGS+= -Wmissing-declarations
CFLAGS+= -Wshadow -Wpointer-arith
diff --git a/config.c b/config.c
index 762a7a3..7ab53b5 100644
--- a/config.c
+++ b/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.21 2014/08/06 18:21:14 reyk Exp $ */
/*
* Copyright (c) 2011 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -36,14 +36,13 @@
#include <event.h>
#include <limits.h>
#include <stdint.h>
+#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <ifaddrs.h>
-#include <openssl/ssl.h>
-
#include "httpd.h"
int config_getserver_config(struct httpd *, struct server *,
@@ -61,6 +60,7 @@ config_init(struct httpd *env)
ps->ps_what[PROC_PARENT] = CONFIG_ALL;
ps->ps_what[PROC_SERVER] = CONFIG_SERVERS|CONFIG_MEDIA;
+ ps->ps_what[PROC_LOGGER] = CONFIG_SERVERS;
}
/* Other configuration */
@@ -184,8 +184,17 @@ config_setserver(struct httpd *env, struct server *srv)
c = 0;
iov[c].iov_base = &s;
iov[c++].iov_len = sizeof(s);
+ if (srv->srv_conf.ssl_cert_len != 0) {
+ iov[c].iov_base = srv->srv_conf.ssl_cert;
+ iov[c++].iov_len = srv->srv_conf.ssl_cert_len;
+ }
+ if (srv->srv_conf.ssl_key_len != 0) {
+ iov[c].iov_base = srv->srv_conf.ssl_key;
+ iov[c++].iov_len = srv->srv_conf.ssl_key_len;
+ }
- 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 +225,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 +233,85 @@ 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_SOCKET|SRVFLAG_FCGI;
+ if ((srv_conf->flags & f) == SRVFLAG_FCGI) {
+ srv_conf->flags |= f;
+ (void)strlcpy(srv_conf->socket, HTTPD_FCGI_SOCKET,
+ sizeof(srv_conf->socket));
+ }
- 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);
+ f = SRVFLAG_ROOT;
+ if ((srv_conf->flags & f) == 0) {
+ srv_conf->flags |= srv->srv_conf.flags & f;
+ (void)strlcpy(srv_conf->root, srv->srv_conf.root,
+ sizeof(srv_conf->root));
+ }
+
+ f = SRVFLAG_FCGI|SRVFLAG_NO_FCGI;
+ if ((srv_conf->flags & f) == 0)
+ srv_conf->flags |= srv->srv_conf.flags & f;
+
+ f = SRVFLAG_LOG|SRVFLAG_NO_LOG;
+ if ((srv_conf->flags & f) == 0) {
+ srv_conf->flags |= srv->srv_conf.flags & f;
+ srv_conf->logformat = srv->srv_conf.logformat;
+ }
+
+ f = SRVFLAG_SYSLOG|SRVFLAG_NO_SYSLOG;
+ if ((srv_conf->flags & f) == 0)
+ srv_conf->flags |= srv->srv_conf.flags & f;
+
+ f = SRVFLAG_SSL;
+ srv_conf->flags |= srv->srv_conf.flags & f;
+
+ f = SRVFLAG_ACCESS_LOG;
+ if ((srv_conf->flags & f) == 0) {
+ srv_conf->flags |= srv->srv_conf.flags & f;
+ (void)strlcpy(srv_conf->accesslog,
+ srv->srv_conf.accesslog,
+ sizeof(srv_conf->accesslog));
+ }
+
+ f = SRVFLAG_ERROR_LOG;
+ if ((srv_conf->flags & f) == 0) {
+ srv_conf->flags |= srv->srv_conf.flags & f;
+ (void)strlcpy(srv_conf->errorlog,
+ srv->srv_conf.errorlog,
+ sizeof(srv_conf->errorlog));
+ }
+
+ memcpy(&srv_conf->timeout, &srv->srv_conf.timeout,
+ sizeof(srv_conf->timeout));
+ srv_conf->maxrequests = srv->srv_conf.maxrequests;
+ srv_conf->maxrequestbody = srv->srv_conf.maxrequestbody;
+
+ DPRINTF("%s: %s %d location \"%s\", "
+ "parent \"%s\", flags: %s",
+ __func__, ps->ps_title[privsep_process], ps->ps_instance,
+ srv_conf->location, srv->srv_conf.name,
+ printb_flags(srv_conf->flags, SRVFLAG_BITS));
+ } else {
+ /* Add a new "virtual" server */
+ DPRINTF("%s: %s %d server \"%s\", parent \"%s\", flags: %s",
+ __func__, ps->ps_title[privsep_process], ps->ps_instance,
+ srv_conf->name, srv->srv_conf.name,
+ printb_flags(srv_conf->flags, SRVFLAG_BITS));
+ }
+
+ TAILQ_INSERT_TAIL(&srv->srv_hosts, srv_conf, entry);
return (0);
}
@@ -238,7 +322,7 @@ config_getserver(struct httpd *env, struct imsg *imsg)
#ifdef DEBUG
struct privsep *ps = env->sc_ps;
#endif
- struct server *srv;
+ struct server *srv = NULL;
struct server_config srv_conf;
u_int8_t *p = imsg->data;
size_t s;
@@ -247,15 +331,24 @@ config_getserver(struct httpd *env, struct imsg *imsg)
memcpy(&srv_conf, p, sizeof(srv_conf));
s = sizeof(srv_conf);
+ if ((u_int)(IMSG_DATA_SIZE(imsg) - s) <
+ (srv_conf.ssl_cert_len + srv_conf.ssl_key_len)) {
+ log_debug("%s: invalid message length", __func__);
+ goto fail;
+ }
+
/* Check if server with matching listening socket already exists */
if ((srv = server_byaddr((struct sockaddr *)
&srv_conf.ss, srv_conf.port)) != NULL) {
/* Add "host" to existing listening server */
- close(imsg->fd);
- return (config_getserver_config(env,
- srv, imsg));
+ if (imsg->fd != -1)
+ close(imsg->fd);
+ return (config_getserver_config(env, srv, imsg));
}
+ if (srv_conf.flags & SRVFLAG_LOCATION)
+ fatalx("invalid location");
+
/* Otherwise create a new server */
if ((srv = calloc(1, sizeof(*srv))) == NULL) {
close(imsg->fd);
@@ -271,11 +364,32 @@ config_getserver(struct httpd *env, struct imsg *imsg)
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__,
+ DPRINTF("%s: %s %d configuration \"%s\", flags: %s", __func__,
ps->ps_title[privsep_process], ps->ps_instance,
- srv->srv_conf.name);
+ srv->srv_conf.name,
+ printb_flags(srv->srv_conf.flags, SRVFLAG_BITS));
+
+ if (srv->srv_conf.ssl_cert_len != 0) {
+ if ((srv->srv_conf.ssl_cert = get_data(p + s,
+ srv->srv_conf.ssl_cert_len)) == NULL)
+ goto fail;
+ s += srv->srv_conf.ssl_cert_len;
+ }
+ if (srv->srv_conf.ssl_key_len != 0) {
+ if ((srv->srv_conf.ssl_key = get_data(p + s,
+ srv->srv_conf.ssl_key_len)) == NULL)
+ goto fail;
+ s += srv->srv_conf.ssl_key_len;
+ }
return (0);
+
+ fail:
+ free(srv->srv_conf.ssl_cert);
+ free(srv->srv_conf.ssl_key);
+ free(srv);
+
+ return (-1);
}
int
diff --git a/control.c b/control.c
index 3a8552a..1988ba7 100644
--- a/control.c
+++ b/control.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: control.c,v 1.1 2014/07/12 23:34:54 reyk Exp $ */
+/* $OpenBSD: control.c,v 1.4 2014/08/04 15:49:28 reyk Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -16,8 +16,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <sys/types.h>
#include <sys/queue.h>
-#include <sys/param.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -32,8 +32,6 @@
#include <unistd.h>
#include <signal.h>
-#include <openssl/ssl.h>
-
#include "httpd.h"
#define CONTROL_BACKLOG 5
@@ -271,6 +269,7 @@ control_dispatch_imsg(int fd, short event, void *arg)
switch (imsg.hdr.type) {
case IMSG_CTL_SHUTDOWN:
case IMSG_CTL_RELOAD:
+ case IMSG_CTL_REOPEN:
proc_forward_imsg(env->sc_ps, &imsg, PROC_PARENT, -1);
break;
case IMSG_CTL_NOTIFY:
diff --git a/http.h b/http.h
index 994e98b..43ac089 100644
--- a/http.h
+++ b/http.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: http.h,v 1.4 2014/07/25 23:23:39 reyk Exp $ */
+/* $OpenBSD: http.h,v 1.7 2014/08/14 09:12:26 doug Exp $ */
/*
* Copyright (c) 2012 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -25,7 +25,7 @@
enum httpmethod {
HTTP_METHOD_NONE = 0,
- /* HTTP/1.1, RFC 2616 */
+ /* HTTP/1.1, RFC 7231 */
HTTP_METHOD_GET,
HTTP_METHOD_HEAD,
HTTP_METHOD_POST,
@@ -79,22 +79,39 @@ struct http_error {
int error_code;
const char *error_name;
};
+
+/*
+ * HTTP status codes based on IANA assignments (2014-06-11 version):
+ * https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
+ * plus legacy (306) and non-standard (420).
+ */
#define HTTP_ERRORS { \
{ 100, "Continue" }, \
{ 101, "Switching Protocols" }, \
+ { 102, "Processing" }, \
+ /* 103-199 unassigned */ \
{ 200, "OK" }, \
{ 201, "Created" }, \
{ 202, "Accepted" }, \
- { 203, "Non-Authorative Information" }, \
+ { 203, "Non-Authoritative Information" }, \
{ 204, "No Content" }, \
{ 205, "Reset Content" }, \
{ 206, "Partial Content" }, \
+ { 207, "Multi-Status" }, \
+ { 208, "Already Reported" }, \
+ /* 209-225 unassigned */ \
+ { 226, "IM Used" }, \
+ /* 227-299 unassigned */ \
{ 300, "Multiple Choices" }, \
{ 301, "Moved Permanently" }, \
- { 302, "Moved Temporarily" }, \
+ { 302, "Found" }, \
{ 303, "See Other" }, \
{ 304, "Not Modified" }, \
+ { 305, "Use Proxy" }, \
+ { 306, "Switch Proxy" }, \
{ 307, "Temporary Redirect" }, \
+ { 308, "Permanent Redirect" }, \
+ /* 309-399 unassigned */ \
{ 400, "Bad Request" }, \
{ 401, "Unauthorized" }, \
{ 402, "Payment Required" }, \
@@ -108,17 +125,37 @@ struct http_error {
{ 410, "Gone" }, \
{ 411, "Length Required" }, \
{ 412, "Precondition Failed" }, \
- { 413, "Request Entity Too Large" }, \
- { 414, "Request-URL Too Long" }, \
+ { 413, "Payload Too Large" }, \
+ { 414, "URI Too Long" }, \
{ 415, "Unsupported Media Type" }, \
- { 416, "Requested Range Not Satisfiable" }, \
+ { 416, "Range Not Satisfiable" }, \
{ 417, "Expectation Failed" }, \
+ /* 418-421 unassigned */ \
+ { 420, "Enhance Your Calm" }, \
+ { 422, "Unprocessable Entity" }, \
+ { 423, "Locked" }, \
+ { 424, "Failed Dependency" }, \
+ /* 425 unassigned */ \
+ { 426, "Upgrade Required" }, \
+ /* 427 unassigned */ \
+ { 428, "Precondition Required" }, \
+ { 429, "Too Many Requests" }, \
+ /* 430 unassigned */ \
+ { 431, "Request Header Fields Too Large" }, \
+ /* 432-499 unassigned */ \
{ 500, "Internal Server Error" }, \
{ 501, "Not Implemented" }, \
{ 502, "Bad Gateway" }, \
{ 503, "Service Unavailable" }, \
{ 504, "Gateway Timeout" }, \
{ 505, "HTTP Version Not Supported" }, \
+ { 506, "Variant Also Negotiates" }, \
+ { 507, "Insufficient Storage" }, \
+ { 508, "Loop Detected" }, \
+ /* 509 unassigned */ \
+ { 510, "Not Extended" }, \
+ { 511, "Network Authentication Required" }, \
+ /* 512-599 unassigned */ \
{ 0, NULL } \
}
@@ -127,7 +164,10 @@ struct http_mediatype {
char *media_type;
char *media_subtype;
};
-/* Some default media types */
+/*
+ * Some default media types based on (2014-08-04 version):
+ * https://www.iana.org/assignments/media-types/media-types.xhtml
+ */
#define MEDIA_TYPES { \
{ "css", "text", "css" }, \
{ "html", "text", "html" }, \
@@ -151,11 +191,14 @@ struct http_descriptor {
#define query_key http_matchquery.kv_key
#define query_val http_matchquery.kv_value
- char http_host[MAXHOSTNAMELEN];
+ char *http_host;
enum httpmethod http_method;
int http_chunked;
char *http_version;
+ /* Rewritten path remains NULL if not used */
+ char *http_path_alias;
+
/* A tree of headers and attached lists for repeated headers. */
struct kv *http_lastheader;
struct kvtree http_headers;
diff --git a/httpd.8 b/httpd.8
index a62378d..ab301e1 100644
--- a/httpd.8
+++ b/httpd.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: httpd.8,v 1.39 2014/07/22 19:03:21 jmc Exp $
+.\" $OpenBSD: httpd.8,v 1.48 2014/08/09 08:49:48 jmc 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: July 22 2014 $
+.Dd $Mdocdate: August 9 2014 $
.Dt HTTPD 8
.Os
.Sh NAME
@@ -26,20 +26,71 @@
.Op Fl D Ar macro Ns = Ns Ar value
.Op Fl f Ar file
.Sh DESCRIPTION
+The
+.Nm
+daemon is an HTTP server with FastCGI and SSL support.
+.Pp
+The FastCGI implementation has optional socket support.
.Nm
-is a simple HTTP server that serves static files.
+can log to
+.Xr syslog 3
+or per-server files with several standard formats.
+.Pp
+.Nm
+rereads its configuration file when it receives
+.Dv SIGHUP
+and reopens log files when it receives
+.Dv SIGUSR1 .
+.Pp
+The options are as follows:
+.Bl -tag -width Dssmacro=value
+.It Fl D Ar macro Ns = Ns Ar value
+Set a
+.Ar macro
+to a
+.Ar value .
+Macros can be referenced in the configuration files.
+.It Fl d
+Debug mode.
+Create one server and don't detach or become a daemon.
+This allows for easy monitoring of
+.Nm .
+.It Fl f Ar file
+Specifies the configuration file.
+The default is
+.Pa /etc/httpd.conf .
+.It Fl n
+Check that the configuration is valid, but don't start any servers.
+.It Fl v
+Verbose mode.
+Multiple
+.Fl v
+options increases the verbosity.
+.El
.Sh FILES
-.Bl -tag -width "/var/run/httpd.sockXX" -compact
+.Bl -tag -width "/etc/ssl/private/server.key" -compact
.It /etc/httpd.conf
Default configuration file.
+.It /etc/ssl/private/server.key
+Default SSL/TLS server key.
+.It /etc/ssl/server.crt
+Default SSL/TLS server certificate.
.It /var/run/httpd.sock
.Ux Ns -domain
socket used for communication with
.Nm .
+.It /var/www/logs/access.log
+Default access log file.
+.It /var/www/logs/error.log
+Default error log file.
.El
.Sh SEE ALSO
.Xr httpd.conf 5
.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Ox 5.6 .
.Nm
is based on
.Xr relayd 8 .
@@ -49,6 +100,3 @@ The
.Nm
program was written by
.An Reyk Floeter Aq Mt reyk@openbsd.org .
-.Sh CAVEATS
-.Nm
-is not finished yet.
diff --git a/httpd.c b/httpd.c
index 520ae5e..4b631ec 100644
--- a/httpd.c
+++ b/httpd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: httpd.c,v 1.10 2014/07/26 09:59:14 reyk Exp $ */
+/* $OpenBSD: httpd.c,v 1.19 2014/08/13 16:04:28 reyk Exp $ */
/*
* Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/hash.h>
@@ -42,8 +43,6 @@
#include <sha1.h>
#include <md5.h>
-#include <openssl/ssl.h>
-
#include "httpd.h"
__dead void usage(void);
@@ -51,15 +50,19 @@ __dead void usage(void);
int parent_configure(struct httpd *);
void parent_configure_done(struct httpd *);
void parent_reload(struct httpd *, u_int, const char *);
+void parent_reopen(struct httpd *);
void parent_sig_handler(int, short, void *);
void parent_shutdown(struct httpd *);
int parent_dispatch_server(int, struct privsep_proc *,
struct imsg *);
+int parent_dispatch_logger(int, struct privsep_proc *,
+ struct imsg *);
struct httpd *httpd_env;
static struct privsep_proc procs[] = {
- { "server", PROC_SERVER, parent_dispatch_server, server }
+ { "server", PROC_SERVER, parent_dispatch_server, server },
+ { "logger", PROC_LOGGER, parent_dispatch_logger, logger }
};
void
@@ -123,6 +126,11 @@ parent_sig_handler(int sig, short event, void *arg)
case SIGPIPE:
/* ignore */
break;
+ case SIGUSR1:
+ log_info("%s: reopen requested with SIGUSR1", __func__);
+
+ parent_reopen(ps->ps_env);
+ break;
default:
fatalx("unexpected signal");
}
@@ -142,6 +150,7 @@ int
main(int argc, char *argv[])
{
int c;
+ unsigned int proc;
int debug = 0, verbose = 0;
u_int32_t opts = 0;
struct httpd *env;
@@ -194,9 +203,6 @@ main(int argc, char *argv[])
if (parse_config(env->sc_conffile, env) == -1)
exit(1);
- if (debug)
- env->sc_opts |= HTTPD_OPT_LOGUPDATE;
-
if (geteuid())
errx(1, "need root privileges");
@@ -220,6 +226,11 @@ main(int argc, char *argv[])
ps->ps_instances[PROC_SERVER] = env->sc_prefork_server;
ps->ps_ninstances = env->sc_prefork_server;
+ if (env->sc_chroot == NULL)
+ env->sc_chroot = ps->ps_pw->pw_dir;
+ for (proc = 0; proc < nitems(procs); proc++)
+ procs[proc].p_chroot = env->sc_chroot;
+
proc_init(ps, procs, nitems(procs));
setproctitle("parent");
@@ -231,12 +242,14 @@ main(int argc, char *argv[])
signal_set(&ps->ps_evsigchld, SIGCHLD, parent_sig_handler, ps);
signal_set(&ps->ps_evsighup, SIGHUP, parent_sig_handler, ps);
signal_set(&ps->ps_evsigpipe, SIGPIPE, parent_sig_handler, ps);
+ signal_set(&ps->ps_evsigusr1, SIGUSR1, parent_sig_handler, ps);
signal_add(&ps->ps_evsigint, NULL);
signal_add(&ps->ps_evsigterm, NULL);
signal_add(&ps->ps_evsigchld, NULL);
signal_add(&ps->ps_evsighup, NULL);
signal_add(&ps->ps_evsigpipe, NULL);
+ signal_add(&ps->ps_evsigusr1, NULL);
proc_listen(ps, procs, nitems(procs));
@@ -282,7 +295,7 @@ parent_configure(struct httpd *env)
}
/* The servers need to reload their config. */
- env->sc_reload = env->sc_prefork_server;
+ env->sc_reload = env->sc_prefork_server + 1;
for (id = 0; id < PROC_MAX; id++) {
if (id == privsep_process)
@@ -334,6 +347,13 @@ parent_reload(struct httpd *env, u_int reset, const char *filename)
}
void
+parent_reopen(struct httpd *env)
+{
+ proc_compose_imsg(env->sc_ps, PROC_LOGGER, -1, IMSG_CTL_REOPEN,
+ -1, NULL, 0);
+}
+
+void
parent_configure_done(struct httpd *env)
{
int id;
@@ -387,6 +407,46 @@ parent_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
return (0);
}
+int
+parent_dispatch_logger(int fd, struct privsep_proc *p, struct imsg *imsg)
+{
+ struct httpd *env = p->p_env;
+ u_int v;
+ char *str = NULL;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_RESET:
+ IMSG_SIZE_CHECK(imsg, &v);
+ memcpy(&v, imsg->data, sizeof(v));
+ parent_reload(env, v, NULL);
+ break;
+ case IMSG_CTL_RELOAD:
+ if (IMSG_DATA_SIZE(imsg) > 0)
+ str = get_string(imsg->data, IMSG_DATA_SIZE(imsg));
+ parent_reload(env, CONFIG_RELOAD, str);
+ if (str != NULL)
+ free(str);
+ break;
+ case IMSG_CTL_SHUTDOWN:
+ parent_shutdown(env);
+ break;
+ case IMSG_CTL_REOPEN:
+ parent_reopen(env);
+ break;
+ case IMSG_CFG_DONE:
+ parent_configure_done(env);
+ break;
+ case IMSG_LOG_OPEN:
+ if (logger_open_priv(imsg) == -1)
+ fatalx("failed to open log file");
+ break;
+ default:
+ return (-1);
+ }
+
+ return (0);
+}
+
/*
* Utility functions
*/
@@ -493,7 +553,7 @@ canonicalize_path(const char *input, char *path, size_t len)
while (i[1] == '/')
i++;
continue;
- } else if (i[1] == '.' && i[2] == '.' &&
+ } else if (i[1] == '.' && i[2] == '.' &&
(i[3] == '/' || i[3] == '\0')) {
/* b) revert '..' to previous directory */
i += 3;
@@ -520,6 +580,35 @@ canonicalize_path(const char *input, char *path, size_t len)
return (path);
}
+size_t
+path_info(char *path)
+{
+ char *p, *start, *end, ch;
+ struct stat st;
+ int ret;
+
+ start = path;
+ end = start + strlen(path);
+
+ for (p = end; p > start; p--) {
+ /* Scan every path component from the end and at each '/' */
+ if (p < end && *p != '/')
+ continue;
+
+ /* Temporarily cut the path component out */
+ ch = *p;
+ *p = '\0';
+ ret = stat(path, &st);
+ *p = ch;
+
+ /* Break if the initial path component was found */
+ if (ret == 0)
+ break;
+ }
+
+ return (p - start);
+}
+
void
socket_rlimit(int maxfd)
{
diff --git a/httpd.conf.5 b/httpd.conf.5
index 2cb442f..8a539f2 100644
--- a/httpd.conf.5
+++ b/httpd.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: httpd.conf.5,v 1.7 2014/07/25 17:49:11 reyk Exp $
+.\" $OpenBSD: httpd.conf.5,v 1.32 2014/08/17 18:46:29 jmc 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: July 25 2014 $
+.Dd $Mdocdate: August 17 2014 $
.Dt HTTPD.CONF 5
.Os
.Sh NAME
@@ -80,27 +80,29 @@ Macros can be defined that will later be expanded in context.
Macro names must start with a letter, digit, or underscore,
and may contain any of those characters.
Macro names may not be reserved words (for example,
-.Ic table ,
-.Ic relay ,
+.Ic directory ,
+.Ic log ,
or
-.Ic timeout ) .
+.Ic root ) .
Macros are not expanded inside quotes.
.Pp
For example:
.Bd -literal -offset indent
ext_ip="10.0.0.1"
-server \*(Ltwww\*(Gt {
+server "default" {
listen on $ext_ip port 80
}
.Ed
.Sh GLOBAL CONFIGURATION
Here are the settings that can be set globally:
.Bl -tag -width Ds
-.It Xo
-.Ic log
-.Pq Ic updates Ns | Ns Ic all
-.Xc
-Set logging verbosity.
+.It Ic chroot Ar directory
+Set the
+.Xr chroot 2
+directory.
+If not specified, it defaults to
+.Pa /var/www ,
+the home directory of the www user.
.It Ic prefork Ar number
Run the specified number of server processes.
This increases the performance and prevents delays when connecting
@@ -111,10 +113,122 @@ runs 3 server processes by default.
.Sh SERVERS
The configured web servers.
.Pp
-The following general table options are available:
+Each
+.Ic server
+must have a
+.Ar name
+and include one or more lines of the following syntax:
.Bl -tag -width Ds
-.It Ic listen on Ar address Ic port Ar number
+.It Ic connection Ar option
+Set the specified options and limits for HTTP connections.
+Valid options are:
+.Bl -tag -width Ds
+.It Ic max request body Ar number
+Set the maximum body size in bytes that the client can send to the server.
+The default value is 1048576 bytes (1M).
+.It Ic max requests Ar number
+Set the maximum number of requests per persistent HTTP connection.
+Persistent connections are negotiated using the Keep-Alive header in
+HTTP/1.0 and enabled by default in HTTP/1.1.
+The default maximum number of requests per connection is 100.
+.It Ic timeout Ar seconds
+Specify the inactivity timeout in seconds for accepted sessions.
+The default timeout is 600 seconds (10 minutes).
+The maximum is 2147483647 seconds (68 years).
+.El
+.It Ic directory Ar option
+Set the specified options when serving or accessing directories.
+Valid options are:
+.Bl -tag -width Ds
+.It Oo Ic no Oc Ic auto index
+If no index file is found, automatically generate a directory listing.
+This is disabled by default.
+.It Ic index Ar string
+Set the directory index file.
+If not specified, it defaults to
+.Pa index.html .
+.It Ic no index
+Disable the directory index.
+.Xr httpd 8
+will neither display nor generate a directory index.
+.El
+.It Oo Ic no Oc Ic fastcgi Op Ic socket Ar socket
+Enable FastCGI instead of serving files.
+The
+.Ar socket
+is a local path name within the
+.Xr chroot 2
+root directory of
+.Xr httpd 8
+and defaults to
+.Pa /run/slowcgi.sock .
+.It Ic listen on Ar address Oo Ic ssl Oc Ic port Ar number
Set the listen address and port.
+.It Ic location Ar path Brq ...
+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 most of the server configuration rules
+except
+.Ic connection ,
+.Ic listen on ,
+.Ic location
+and
+.Ic tcp .
+.It Oo Ic no Oc Ic log Op Ar option
+Set the specified logging options.
+Logging is enabled by default using the standard
+.Ic access
+and
+.Ic error
+log files,
+but can be changed per server or location.
+Use the
+.Ic no log
+directive to disable logging of any requests.
+Valid options are:
+.Bl -tag -width Ds
+.It Ic access Ar name
+Set the
+.Ar name
+of the access log file relative to the log directory.
+If not specified, it defaults to
+.Pa access.log .
+.It Ic error Ar name
+Set the
+.Ar name
+of the error log file relative to the log directory.
+If not specified, it defaults to
+.Pa error.log .
+.It Ic style Ar style
+Set the logging style.
+The
+.Ar style
+can be
+.Cm common ,
+.Cm combined
+or
+.Cm connection .
+The styles
+.Cm common
+and
+.Cm combined
+write a log entry after each request similar to the standard Apache
+and nginx access log formats.
+The style
+.Cm connection
+writes a summarized log entry after each connection,
+that can have multiple requests,
+similar to the format that is used by
+.Xr relayd 8 .
+If not specified, the default is
+.Cm common .
+.It Oo Ic no Oc Ic syslog
+Enable or disable logging to
+.Xr syslog 3
+instead of the log files.
+.El
.It Ic root Ar directory
Set the document root of the server.
The
@@ -125,17 +239,77 @@ root directory of
.Nm httpd .
If not specified, it defaults to
.Pa /htdocs .
+.It Ic ssl Ar option
+Set the SSL configuration for the server.
+These options are only used if SSL has been enabled via the listen directive.
+Valid options are:
+.Bl -tag -width Ds
+.It Ic certificate Ar file
+Specify the certificate to use for this server.
+The
+.Ar file
+should contain a PEM encoded certificate.
+.It Ic ciphers Ar string
+Specify the SSL cipher string.
+If not specified, the default value
+.Qq HIGH:!aNULL
+will be used (strong crypto cipher suites without anonymous DH).
+See the CIPHERS section of
+.Xr openssl 1
+for information about SSL cipher suites and preference lists.
+.It Ic key Ar file
+Specify the private key to use for this server.
+The
+.Ar file
+should contain a PEM encoded private key and reside outside of the
+.Xr chroot 2
+root directory of
+.Nm httpd .
+.El
+.It Ic tcp Ar option
+Enable or disable the specified TCP/IP options; see
+.Xr tcp 4
+and
+.Xr ip 4
+for more information about the options.
+Valid options are:
+.Bl -tag -width Ds
+.It Ic backlog Ar number
+Set the maximum length the queue of pending connections may grow to.
+The backlog option is 10 by default and is limited by the
+.Va kern.somaxconn
+.Xr sysctl 8
+variable.
+.It Ic ip minttl Ar number
+This option for the underlying IP connection may be used to discard packets
+with a TTL lower than the specified value.
+This can be used to implement the
+Generalized TTL Security Mechanism (GTSM)
+according to RFC 5082.
+.It Ic ip ttl Ar number
+Change the default time-to-live value in the IP headers.
+.It Oo Ic no Oc Ic nodelay
+Enable the TCP NODELAY option for this connection.
+This is recommended to avoid delays in the relayed data stream,
+e.g. for SSH connections.
+.It Oo Ic no Oc Ic sack
+Use selective acknowledgements for this connection.
+.It Ic socket buffer Ar number
+Set the socket-level buffer size for input and output for this
+connection.
+This will affect the TCP window size.
+.El
.El
.Sh TYPES
Configure the supported media types.
-.Nm httpd
+.Xr httpd 8
will set the
.Ar Content-Type
of the response header based on the file extension listed in the
.Ic types
section.
If not specified,
-.Nm httpd
+.Xr httpd 8
will use built-in media types for
.Ar text/css ,
.Ar text/html ,
@@ -163,7 +337,7 @@ One or more names can be specified per line.
The following example will start one server that is pre-forked two
times and listening on the primary IP address of the network interface
that is a member of the
-.Ar egress
+.Qq egress
group.
It additionally defines some media types overriding the defaults.
.Bd -literal -offset indent
diff --git a/httpd.h b/httpd.h
index df34b47..371dd1c 100644
--- a/httpd.h
+++ b/httpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: httpd.h,v 1.15 2014/07/25 23:30:58 reyk Exp $ */
+/* $OpenBSD: httpd.h,v 1.53 2014/08/13 16:04:28 reyk Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -26,6 +26,7 @@
#include <sys/param.h> /* MAXHOSTNAMELEN */
#include <limits.h>
#include <imsg.h>
+#include <ressl.h>
#define CONF_FILE "/etc/httpd.conf"
#define HTTPD_SOCKET "/var/run/httpd.sock"
@@ -33,6 +34,13 @@
#define HTTPD_SERVERNAME "OpenBSD httpd"
#define HTTPD_DOCROOT "/htdocs"
#define HTTPD_INDEX "index.html"
+#define HTTPD_FCGI_SOCKET "/run/slowcgi.sock"
+#define HTTPD_LOGROOT "/logs"
+#define HTTPD_ACCESS_LOG "access.log"
+#define HTTPD_ERROR_LOG "error.log"
+#define HTTPD_SSL_CERT "/etc/ssl/server.crt"
+#define HTTPD_SSL_KEY "/etc/ssl/private/server.key"
+#define HTTPD_SSL_CIPHERS "HIGH:!aNULL"
#define FD_RESERVE 5
#define SERVER_MAX_CLIENTS 1024
@@ -41,6 +49,8 @@
#define SERVER_NUMPROC 3
#define SERVER_MAXPROC 32
#define SERVER_MAXHEADERLENGTH 8192
+#define SERVER_MAXREQUESTS 100 /* max requests per connection */
+#define SERVER_MAXREQUESTBODY 1048576 /* 1M */
#define SERVER_BACKLOG 10
#define SERVER_OUTOF_FD_RETRIES 5
@@ -52,19 +62,7 @@
#define CONFIG_SERVERS 0x02
#define CONFIG_ALL 0xff
-#define TCPFLAG_NODELAY 0x01
-#define TCPFLAG_NNODELAY 0x02
-#define TCPFLAG_SACK 0x04
-#define TCPFLAG_NSACK 0x08
-#define TCPFLAG_BUFSIZ 0x10
-#define TCPFLAG_IPTTL 0x20
-#define TCPFLAG_IPMINTTL 0x40
-#define TCPFLAG_NSPLICE 0x80
-#define TCPFLAG_DEFAULT 0x00
-
-#define TCPFLAG_BITS \
- "\10\01NODELAY\02NO_NODELAY\03SACK\04NO_SACK" \
- "\05SOCKET_BUFFER_SIZE\06IP_TTL\07IP_MINTTL\10NO_SPLICE"
+#define FCGI_CONTENT_SIZE 65535
enum httpchunk {
TOREAD_UNLIMITED = -1,
@@ -189,20 +187,25 @@ enum imsg_type {
IMSG_CTL_NOTIFY,
IMSG_CTL_END,
IMSG_CTL_START,
+ IMSG_CTL_REOPEN,
IMSG_CFG_SERVER,
IMSG_CFG_MEDIA,
- IMSG_CFG_DONE
+ IMSG_CFG_DONE,
+ IMSG_LOG_ACCESS,
+ IMSG_LOG_ERROR,
+ IMSG_LOG_OPEN
};
enum privsep_procid {
PROC_ALL = -1,
PROC_PARENT = 0,
PROC_SERVER,
+ PROC_LOGGER,
PROC_MAX
} privsep_process;
/* Attach the control socket to the following process */
-#define PROC_CONTROL PROC_PARENT
+#define PROC_CONTROL PROC_LOGGER
struct privsep_pipes {
int *pp_pipes[PROC_MAX];
@@ -230,6 +233,7 @@ struct privsep {
struct event ps_evsigchld;
struct event ps_evsighup;
struct event ps_evsigpipe;
+ struct event ps_evsigusr1;
int ps_noaction;
struct passwd *ps_pw;
@@ -250,6 +254,12 @@ struct privsep_proc {
struct httpd *p_env;
};
+enum fcgistate {
+ FCGI_READ_HEADER,
+ FCGI_READ_CONTENT,
+ FCGI_READ_PADDING
+};
+
struct client {
u_int32_t clt_id;
pid_t clt_pid;
@@ -262,19 +272,29 @@ struct client {
in_port_t clt_port;
struct sockaddr_storage clt_ss;
struct bufferevent *clt_bev;
+ char *clt_buf;
+ size_t clt_buflen;
struct evbuffer *clt_output;
struct event clt_ev;
void *clt_desc;
+ int clt_sndbufsiz;
int clt_fd;
- struct bufferevent *clt_file;
+ struct ressl *clt_ressl_ctx;
+ struct bufferevent *clt_srvbev;
off_t clt_toread;
size_t clt_headerlen;
- int clt_persist;
+ u_int clt_persist;
int clt_line;
int clt_done;
+ int clt_chunk;
int clt_inflight;
+ enum fcgistate clt_fcgi_state;
+ int clt_fcgi_toread;
+ int clt_fcgi_padding_len;
+ int clt_fcgi_type;
+ struct evbuffer *clt_srvevb;
struct evbuffer *clt_log;
struct timeval clt_timeout;
@@ -286,15 +306,91 @@ struct client {
};
SPLAY_HEAD(client_tree, client);
+#define SRVFLAG_INDEX 0x0001
+#define SRVFLAG_NO_INDEX 0x0002
+#define SRVFLAG_AUTO_INDEX 0x0004
+#define SRVFLAG_NO_AUTO_INDEX 0x0008
+#define SRVFLAG_ROOT 0x0010
+#define SRVFLAG_LOCATION 0x0020
+#define SRVFLAG_FCGI 0x0040
+#define SRVFLAG_NO_FCGI 0x0080
+#define SRVFLAG_LOG 0x0100
+#define SRVFLAG_NO_LOG 0x0200
+#define SRVFLAG_SOCKET 0x0400
+#define SRVFLAG_SYSLOG 0x0800
+#define SRVFLAG_NO_SYSLOG 0x1000
+#define SRVFLAG_SSL 0x2000
+#define SRVFLAG_ACCESS_LOG 0x4000
+#define SRVFLAG_ERROR_LOG 0x8000
+
+#define SRVFLAG_BITS \
+ "\10\01INDEX\02NO_INDEX\03AUTO_INDEX\04NO_AUTO_INDEX" \
+ "\05ROOT\06LOCATION\07FCGI\10NO_FCGI\11LOG\12NO_LOG\13SOCKET" \
+ "\14SYSLOG\15NO_SYSLOG\16SSL\17ACCESS_LOG\20ERROR_LOG"
+
+#define TCPFLAG_NODELAY 0x01
+#define TCPFLAG_NNODELAY 0x02
+#define TCPFLAG_SACK 0x04
+#define TCPFLAG_NSACK 0x08
+#define TCPFLAG_BUFSIZ 0x10
+#define TCPFLAG_IPTTL 0x20
+#define TCPFLAG_IPMINTTL 0x40
+#define TCPFLAG_NSPLICE 0x80
+#define TCPFLAG_DEFAULT 0x00
+
+#define TCPFLAG_BITS \
+ "\10\01NODELAY\02NO_NODELAY\03SACK\04NO_SACK" \
+ "\05SOCKET_BUFFER_SIZE\06IP_TTL\07IP_MINTTL\10NO_SPLICE"
+
+enum log_format {
+ LOG_FORMAT_COMMON,
+ LOG_FORMAT_COMBINED,
+ LOG_FORMAT_CONNECTION
+};
+
+struct log_file {
+ char log_name[NAME_MAX];
+ int log_fd;
+ u_int32_t log_id;
+ TAILQ_ENTRY(log_file) log_entry;
+};
+TAILQ_HEAD(log_files, log_file) log_files;
+
struct server_config {
u_int32_t id;
- u_int32_t flags;
char name[MAXHOSTNAMELEN];
- char docroot[MAXPATHLEN];
+ char location[NAME_MAX];
+ char index[NAME_MAX];
+ char root[MAXPATHLEN];
+ char socket[MAXPATHLEN];
+ char accesslog[NAME_MAX];
+ char errorlog[NAME_MAX];
+
in_port_t port;
struct sockaddr_storage ss;
int prefixlen;
struct timeval timeout;
+ u_int32_t maxrequests;
+ size_t maxrequestbody;
+
+ char *ssl_cert;
+ off_t ssl_cert_len;
+ char *ssl_cert_file;
+ char ssl_ciphers[NAME_MAX];
+ char *ssl_key;
+ off_t ssl_key_len;
+ char *ssl_key_file;
+
+ u_int16_t flags;
+ u_int8_t tcpflags;
+ int tcpbufsiz;
+ int tcpbacklog;
+ u_int8_t tcpipttl;
+ u_int8_t tcpipminttl;
+
+ enum log_format logformat;
+ struct log_file *logaccess;
+ struct log_file *logerror;
TAILQ_ENTRY(server_config) entry;
};
@@ -305,16 +401,13 @@ struct server {
struct server_config srv_conf;
struct serverhosts srv_hosts;
- u_int8_t srv_tcpflags;
- int srv_tcpbufsiz;
- int srv_tcpbacklog;
- u_int8_t srv_tcpipttl;
- u_int8_t srv_tcpipminttl;
-
int srv_s;
struct event srv_ev;
struct event srv_evt;
+ struct ressl *srv_ressl_ctx;
+ struct ressl_config *srv_ressl_config;
+
struct client_tree srv_clients;
};
TAILQ_HEAD(serverlist, server);
@@ -335,6 +428,8 @@ struct httpd {
struct event sc_ev;
u_int16_t sc_prefork_server;
u_int16_t sc_id;
+ int sc_paused;
+ char *sc_chroot;
struct serverlist *sc_servers;
struct mediatypes *sc_mediatypes;
@@ -345,9 +440,6 @@ struct httpd {
#define HTTPD_OPT_VERBOSE 0x01
#define HTTPD_OPT_NOACTION 0x04
-#define HTTPD_OPT_LOGUPDATE 0x08
-#define HTTPD_OPT_LOGNOTIFY 0x10
-#define HTTPD_OPT_LOGALL 0x18
/* control.c */
int control_init(struct privsep *, struct control_sock *);
@@ -368,14 +460,20 @@ int cmdline_symset(char *);
/* server.c */
pid_t server(struct privsep *, struct privsep_proc *);
+int server_ssl_load_keypair(struct server *);
int server_privinit(struct server *);
void server_purge(struct server *);
int server_socket_af(struct sockaddr_storage *, in_port_t);
in_port_t
server_socket_getport(struct sockaddr_storage *);
+int server_socket_connect(struct sockaddr_storage *, in_port_t,
+ struct server_config *);
void server_write(struct bufferevent *, void *);
void server_read(struct bufferevent *, void *);
void server_error(struct bufferevent *, short, void *);
+void server_log(struct client *, const char *);
+void server_sendlog(struct server_config *, int, const char *, ...)
+ __attribute__((__format__ (printf, 3, 4)));
void server_close(struct client *, const char *);
void server_dump(struct client *, const void *, size_t);
int server_client_cmp(struct client *, struct client *);
@@ -389,6 +487,10 @@ 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_config *
+ serverconfig_byid(u_int32_t);
+int server_foreach(int (*)(struct server *,
+ struct server_config *, void *), void *);
SPLAY_PROTOTYPE(client_tree, client, clt_nodes, server_client_cmp);
@@ -405,19 +507,29 @@ const char
*server_httperror_byid(u_int);
void server_read_httpcontent(struct bufferevent *, void *);
void server_read_httpchunks(struct bufferevent *, void *);
-int server_writeheader_kv(struct client *, struct kv *);
-int server_writeheader_http(struct client *);
+int server_writeheader_http(struct client *clt, struct kv *, void *);
+int server_headers(struct client *,
+ int (*)(struct client *, struct kv *, void *), void *);
int server_writeresponse_http(struct client *);
int server_response_http(struct client *, u_int, struct media_type *,
size_t);
void server_reset_http(struct client *);
void server_close_http(struct client *);
int server_response(struct httpd *, struct client *);
+struct server_config *
+ server_getlocation(struct client *, const char *);
const char *
server_http_host(struct sockaddr_storage *, char *, size_t);
+void server_http_date(char *, size_t);
+int server_log_http(struct client *, u_int, size_t);
/* server_file.c */
int server_file(struct httpd *, struct client *);
+void server_file_error(struct bufferevent *, short, void *);
+
+/* server_fcgi.c */
+int server_fcgi(struct httpd *, struct client *);
+int fcgi_add_stdin(struct client *, struct evbuffer *);
/* httpd.c */
void event_again(struct event *, int, short,
@@ -425,6 +537,7 @@ void event_again(struct event *, int, short,
struct timeval *, struct timeval *, void *);
const char *canonicalize_host(const char *, char *, size_t);
const char *canonicalize_path(const char *, char *, size_t);
+size_t path_info(char *);
void imsg_event_add(struct imsgev *);
int imsg_compose_event(struct imsgev *, u_int16_t, u_int32_t,
pid_t, int, void *, u_int16_t);
@@ -435,7 +548,7 @@ 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 *);
+ volatile int *);
struct kv *kv_add(struct kvtree *, char *, char *);
int kv_set(struct kv *, char *, ...);
int kv_setkey(struct kv *, char *, ...);
@@ -508,4 +621,8 @@ int config_getserver(struct httpd *, struct imsg *);
int config_setmedia(struct httpd *, struct media_type *);
int config_getmedia(struct httpd *, struct imsg *);
+/* logger.c */
+pid_t logger(struct privsep *, struct privsep_proc *);
+int logger_open_priv(struct imsg *);
+
#endif /* _HTTPD_H */
diff --git a/log.c b/log.c
index 059cac6..c500d2e 100644
--- a/log.c
+++ b/log.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: log.c,v 1.1 2014/07/12 23:34:54 reyk Exp $ */
+/* $OpenBSD: log.c,v 1.2 2014/08/04 11:09:25 reyk Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -37,8 +37,6 @@
#include <netdb.h>
#include <ctype.h>
-#include <openssl/ssl.h>
-
#include "httpd.h"
int debug;
diff --git a/logger.c b/logger.c
new file mode 100644
index 0000000..665db75
--- /dev/null
+++ b/logger.c
@@ -0,0 +1,310 @@
+/* $OpenBSD: logger.c,v 1.5 2014/08/06 12:56:58 reyk Exp $ */
+
+/*
+ * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+
+#include <net/if.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <event.h>
+
+#include "httpd.h"
+
+int logger_dispatch_parent(int, struct privsep_proc *,
+ struct imsg *);
+int logger_dispatch_server(int, struct privsep_proc *,
+ struct imsg *);
+void logger_shutdown(void);
+void logger_close(void);
+struct log_file *logger_open_file(const char *);
+int logger_open_fd(struct imsg *);
+int logger_open(struct server *, struct server_config *, void *);
+void logger_init(struct privsep *, struct privsep_proc *p, void *);
+int logger_start(void);
+int logger_log(struct imsg *);
+
+static struct httpd *env = NULL;
+int proc_id;
+static u_int32_t last_log_id = 0;
+
+static struct privsep_proc procs[] = {
+ { "parent", PROC_PARENT, logger_dispatch_parent },
+ { "server", PROC_SERVER, logger_dispatch_server }
+};
+
+pid_t
+logger(struct privsep *ps, struct privsep_proc *p)
+{
+ env = ps->ps_env;
+ return (proc_run(ps, p, procs, nitems(procs), logger_init, NULL));
+}
+
+void
+logger_shutdown(void)
+{
+ logger_close();
+ config_purge(env, CONFIG_ALL);
+}
+
+void
+logger_init(struct privsep *ps, struct privsep_proc *p, void *arg)
+{
+ if (config_init(ps->ps_env) == -1)
+ fatal("failed to initialize configuration");
+
+ /* Set to current prefork id */
+ proc_id = p->p_instance;
+
+ /* We use a custom shutdown callback */
+ p->p_shutdown = logger_shutdown;
+
+ TAILQ_INIT(&log_files);
+}
+
+void
+logger_close(void)
+{
+ struct log_file *log, *next;
+
+ TAILQ_FOREACH_SAFE(log, &log_files, log_entry, next) {
+ if (log->log_fd != -1) {
+ close(log->log_fd);
+ log->log_fd = -1;
+ }
+ TAILQ_REMOVE(&log_files, log, log_entry);
+ }
+}
+
+struct log_file *
+logger_open_file(const char *name)
+{
+ struct log_file *log;
+ struct iovec iov[2];
+
+ if ((log = calloc(1, sizeof(*log))) == NULL) {
+ log_warn("failed to allocate log %s", name);
+ return (NULL);
+ }
+
+ log->log_id = ++last_log_id;
+ (void)strlcpy(log->log_name, name, sizeof(log->log_name));
+
+ /* The file will be opened by the parent process */
+ log->log_fd = -1;
+
+ iov[0].iov_base = &log->log_id;
+ iov[0].iov_len = sizeof(log->log_id);
+ iov[1].iov_base = log->log_name;
+ iov[1].iov_len = strlen(log->log_name) + 1;
+
+ proc_composev_imsg(env->sc_ps, PROC_PARENT, -1, IMSG_LOG_OPEN, -1,
+ iov, 2);
+
+ TAILQ_INSERT_TAIL(&log_files, log, log_entry);
+
+ return (log);
+}
+
+int
+logger_open_fd(struct imsg *imsg)
+{
+ struct log_file *log;
+ u_int32_t id;
+
+ IMSG_SIZE_CHECK(imsg, &id);
+ memcpy(&id, imsg->data, sizeof(id));
+
+ TAILQ_FOREACH(log, &log_files, log_entry) {
+ if (log->log_id == id) {
+ DPRINTF("%s: received log fd %d, file %s",
+ __func__, imsg->fd, log->log_name);
+ log->log_fd = imsg->fd;
+ return (0);
+ }
+ }
+
+ return (-1);
+}
+
+int
+logger_open_priv(struct imsg *imsg)
+{
+ char path[MAXPATHLEN];
+ char name[NAME_MAX], *p;
+ u_int32_t id;
+ size_t len;
+ int fd;
+
+ /* called from the priviled process */
+ IMSG_SIZE_CHECK(imsg, &id);
+ memcpy(&id, imsg->data, sizeof(id));
+ p = (char *)imsg->data + sizeof(id);
+
+ if ((size_t)snprintf(name, sizeof(name), "/%s", p) >= sizeof(name))
+ return (-1);
+ if ((len = (size_t)snprintf(path, sizeof(path), "%s%s",
+ env->sc_chroot, HTTPD_LOGROOT)) >= sizeof(path))
+ return (-1);
+
+ p = path + len;
+ len = sizeof(path) - len;
+
+ if (canonicalize_path(name, p, len) == NULL) {
+ log_warnx("invalid log name");
+ return (-1);
+ }
+
+ if ((fd = open(path, O_WRONLY|O_APPEND|O_CREAT, 0644)) == -1) {
+ log_warn("failed to open %s", path);
+ return (-1);
+ }
+
+ proc_compose_imsg(env->sc_ps, PROC_LOGGER, -1, IMSG_LOG_OPEN, fd,
+ &id, sizeof(id));
+
+ DPRINTF("%s: opened log file %s, fd %d", __func__, path, fd);
+
+ return (0);
+}
+
+int
+logger_open(struct server *srv, struct server_config *srv_conf, void *arg)
+{
+ struct log_file *log, *logfile = NULL, *errfile = NULL;
+
+ /* disassociate */
+ srv_conf->logaccess = srv_conf->logerror = NULL;
+
+ TAILQ_FOREACH(log, &log_files, log_entry) {
+ if (strcmp(log->log_name, srv_conf->accesslog) == 0)
+ logfile = log;
+ if (strcmp(log->log_name, srv_conf->errorlog) == 0)
+ errfile = log;
+ }
+
+ if (logfile == NULL) {
+ if ((srv_conf->logaccess =
+ logger_open_file(srv_conf->accesslog)) == NULL)
+ return (-1);
+ } else
+ srv_conf->logaccess = logfile;
+
+ if (errfile == NULL) {
+ if ((srv_conf->logerror =
+ logger_open_file(srv_conf->errorlog)) == NULL)
+ return (-1);
+ } else
+ srv_conf->logerror = errfile;
+
+ return (0);
+}
+
+int
+logger_start(void)
+{
+ logger_close();
+ if (server_foreach(logger_open, NULL) == -1)
+ fatalx("failed to open log files");
+ return (0);
+}
+
+int
+logger_log(struct imsg *imsg)
+{
+ char *logline;
+ u_int32_t id;
+ struct server_config *srv_conf;
+ struct log_file *log;
+
+ IMSG_SIZE_CHECK(imsg, &id);
+ memcpy(&id, imsg->data, sizeof(id));
+
+ if ((srv_conf = serverconfig_byid(id)) == NULL)
+ fatalx("invalid logging requestr");
+
+ if (imsg->hdr.type == IMSG_LOG_ACCESS)
+ log = srv_conf->logaccess;
+ else
+ log = srv_conf->logerror;
+
+ if (log == NULL || log->log_fd == -1) {
+ log_warnx("log file %s not opened", log ? log->log_name : "");
+ return (0);
+ }
+
+ /* XXX get_string() would sanitize the string, but add a malloc */
+ logline = (char *)imsg->data + sizeof(id);
+
+ /* For debug output */
+ log_debug("%s", logline);
+
+ if (dprintf(log->log_fd, "%s\n", logline) == -1) {
+ if (logger_start() == -1)
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+logger_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
+{
+ switch (imsg->hdr.type) {
+ case IMSG_CFG_SERVER:
+ config_getserver(env, imsg);
+ break;
+ case IMSG_CFG_DONE:
+ config_getcfg(env, imsg);
+ break;
+ case IMSG_CTL_START:
+ case IMSG_CTL_REOPEN:
+ logger_start();
+ break;
+ case IMSG_CTL_RESET:
+ config_getreset(env, imsg);
+ break;
+ case IMSG_LOG_OPEN:
+ return (logger_open_fd(imsg));
+ default:
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+logger_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
+{
+ switch (imsg->hdr.type) {
+ case IMSG_LOG_ACCESS:
+ case IMSG_LOG_ERROR:
+ logger_log(imsg);
+ break;
+ default:
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/parse.y b/parse.y
index 1105e55..d66f207 100644
--- a/parse.y
+++ b/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.7 2014/07/25 17:04:47 reyk Exp $ */
+/* $OpenBSD: parse.y,v 1.35 2014/08/09 07:35:45 reyk Exp $ */
/*
* Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -53,8 +53,6 @@
#include <ifaddrs.h>
#include <syslog.h>
-#include <openssl/ssl.h>
-
#include "httpd.h"
#include "http.h"
@@ -94,7 +92,8 @@ 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;
+static struct server_config *srv_conf = NULL;
struct serverlist servers;
struct media_type media;
@@ -126,12 +125,16 @@ typedef struct {
%}
-%token ALL PORT LISTEN PREFORK ROOT SERVER ERROR LOG VERBOSE ON TYPES
-%token UPDATES INCLUDE
+%token ACCESS AUTO BACKLOG BODY BUFFER CERTIFICATE CHROOT CIPHERS COMMON
+%token COMBINED CONNECTION DIRECTORY ERR FCGI INDEX IP KEY LISTEN LOCATION
+%token LOG MAXIMUM NO NODELAY ON PORT PREFORK REQUEST REQUESTS ROOT SACK
+%token SERVER SOCKET SSL STYLE SYSLOG TCP TIMEOUT TYPES
+%token ERROR INCLUDE
%token <v.string> STRING
%token <v.number> NUMBER
-%type <v.number> loglevel
%type <v.port> port
+%type <v.number> optssl
+%type <v.tv> timeout
%%
@@ -168,12 +171,11 @@ varset : STRING '=' STRING {
}
;
-main : LOG loglevel {
- if (loadcfg)
- break;
- conf->sc_opts |= $2;
- }
- | PREFORK NUMBER {
+optssl : /*empty*/ { $$ = 0; }
+ | SSL { $$ = 1; }
+ ;
+
+main : PREFORK NUMBER {
if (loadcfg)
break;
if ($2 <= 0 || $2 > SERVER_MAXPROC) {
@@ -183,6 +185,9 @@ main : LOG loglevel {
}
conf->sc_prefork_server = $2;
}
+ | CHROOT STRING {
+ conf->sc_chroot = $2;
+ }
;
server : SERVER STRING {
@@ -215,10 +220,28 @@ server : SERVER STRING {
}
free($2);
- strlcpy(s->srv_conf.docroot, HTTPD_DOCROOT,
- sizeof(s->srv_conf.docroot));
+ 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.timeout.tv_sec = SERVER_TIMEOUT;
+ s->srv_conf.maxrequests = SERVER_MAXREQUESTS;
+ s->srv_conf.maxrequestbody = SERVER_MAXREQUESTBODY;
+ s->srv_conf.flags |= SRVFLAG_LOG;
+ s->srv_conf.logformat = LOG_FORMAT_COMMON;
+ if ((s->srv_conf.ssl_cert_file =
+ strdup(HTTPD_SSL_CERT)) == NULL)
+ fatal("out of memory");
+ if ((s->srv_conf.ssl_key_file =
+ strdup(HTTPD_SSL_KEY)) == NULL)
+ fatal("out of memory");
+ strlcpy(s->srv_conf.ssl_ciphers, HTTPD_SSL_CIPHERS,
+ sizeof(s->srv_conf.ssl_ciphers));
if (last_server_id == INT_MAX) {
yyerror("too many servers defined");
@@ -226,9 +249,23 @@ server : SERVER STRING {
YYERROR;
}
srv = s;
- } '{' optnl serveropts_l '}' {
+ srv_conf = &srv->srv_conf;
+
SPLAY_INIT(&srv->srv_clients);
TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry);
+ } '{' optnl serveropts_l '}' {
+ if (srv->srv_conf.ss.ss_family == AF_UNSPEC) {
+ yyerror("listen address not specified");
+ free($2);
+ YYERROR;
+ }
+ if (server_ssl_load_keypair(srv) == -1) {
+ yyerror("failed to load public/private keys "
+ "for server %s", srv->srv_conf.name);
+ YYERROR;
+ }
+ srv = NULL;
+ srv_conf = NULL;
}
;
@@ -236,25 +273,31 @@ serveropts_l : serveropts_l serveroptsl nl
| serveroptsl optnl
;
-serveroptsl : LISTEN ON STRING port {
+serveroptsl : LISTEN ON STRING optssl port {
struct addresslist al;
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);
YYERROR;
} else
s = srv;
- if ($4.op != PF_OP_EQ) {
+ if ($5.op != PF_OP_EQ) {
yyerror("invalid port");
free($3);
YYERROR;
}
TAILQ_INIT(&al);
- if (host($3, &al, 1, &$4, NULL, -1) <= 0) {
+ if (host($3, &al, 1, &$5, NULL, -1) <= 0) {
yyerror("invalid listen ip: %s", $3);
free($3);
YYERROR;
@@ -266,16 +309,350 @@ serveroptsl : LISTEN ON STRING port {
s->srv_conf.port = h->port.val[0];
s->srv_conf.prefixlen = h->prefixlen;
host_free(&al);
+
+ if ($4) {
+ s->srv_conf.flags |= SRVFLAG_SSL;
+ }
}
+ | TCP {
+ if (parentsrv != NULL) {
+ yyerror("tcp flags inside location");
+ YYERROR;
+ }
+ } tcpip
+ | CONNECTION {
+ if (parentsrv != NULL) {
+ yyerror("connection options inside location");
+ YYERROR;
+ }
+ } connection
+ | SSL {
+ if (parentsrv != NULL) {
+ yyerror("ssl configuration inside location");
+ YYERROR;
+ }
+ } ssl
| ROOT STRING {
- if (strlcpy(srv->srv_conf.docroot, $2,
- sizeof(srv->srv_conf.docroot)) >=
- sizeof(srv->srv_conf.docroot)) {
+ if (strlcpy(srv->srv_conf.root, $2,
+ sizeof(srv->srv_conf.root)) >=
+ sizeof(srv->srv_conf.root)) {
yyerror("document root too long");
free($2);
YYERROR;
}
free($2);
+ srv->srv_conf.flags |= SRVFLAG_ROOT;
+ }
+ | DIRECTORY dirflags
+ | DIRECTORY '{' dirflags_l '}'
+ | logformat
+ | fastcgi
+ | LOCATION STRING {
+ struct server *s;
+
+ if (srv->srv_conf.ss.ss_family == AF_UNSPEC) {
+ yyerror("listen address not specified");
+ free($2);
+ YYERROR;
+ }
+
+ 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;
+ }
+
+ /* A location entry uses the parent id */
+ s->srv_conf.id = srv->srv_conf.id;
+ s->srv_conf.flags = SRVFLAG_LOCATION;
+ memcpy(&s->srv_conf.ss, &srv->srv_conf.ss,
+ sizeof(s->srv_conf.ss));
+ s->srv_conf.port = srv->srv_conf.port;
+ s->srv_conf.prefixlen = srv->srv_conf.prefixlen;
+
+ if (last_server_id == INT_MAX) {
+ yyerror("too many servers/locations defined");
+ free(s);
+ YYERROR;
+ }
+ parentsrv = srv;
+ srv = s;
+ srv_conf = &srv->srv_conf;
+ SPLAY_INIT(&srv->srv_clients);
+ TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry);
+ } '{' optnl serveropts_l '}' {
+ srv = parentsrv;
+ srv_conf = &parentsrv->srv_conf;
+ parentsrv = NULL;
+ }
+ ;
+
+fastcgi : NO FCGI {
+ srv_conf->flags &= ~SRVFLAG_FCGI;
+ srv_conf->flags |= SRVFLAG_NO_FCGI;
+ }
+ | FCGI {
+ srv_conf->flags &= ~SRVFLAG_NO_FCGI;
+ srv_conf->flags |= SRVFLAG_FCGI;
+ }
+ | FCGI {
+ srv_conf->flags &= ~SRVFLAG_NO_FCGI;
+ srv_conf->flags |= SRVFLAG_FCGI;
+ } '{' fcgiflags_l '}'
+ | FCGI {
+ srv_conf->flags &= ~SRVFLAG_NO_FCGI;
+ srv_conf->flags |= SRVFLAG_FCGI;
+ } fcgiflags
+ ;
+
+fcgiflags_l : fcgiflags comma fcgiflags_l
+ | fcgiflags
+ ;
+
+fcgiflags : SOCKET STRING {
+ if (strlcpy(srv_conf->socket, $2,
+ sizeof(srv_conf->socket)) >=
+ sizeof(srv_conf->socket)) {
+ yyerror("fastcgi socket too long");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ srv_conf->flags |= SRVFLAG_SOCKET;
+ }
+ ;
+
+connection : '{' conflags_l '}'
+ | conflags
+ ;
+
+conflags_l : conflags comma conflags_l
+ | conflags
+ ;
+
+conflags : TIMEOUT timeout {
+ memcpy(&srv_conf->timeout, &$2,
+ sizeof(struct timeval));
+ }
+ | MAXIMUM REQUESTS NUMBER {
+ srv_conf->maxrequests = $3;
+ }
+ | MAXIMUM REQUEST BODY NUMBER {
+ srv_conf->maxrequestbody = $4;
+ }
+ ;
+
+ssl : '{' sslopts_l '}'
+ | sslopts
+ ;
+
+sslopts_l : sslopts comma sslopts_l
+ | sslopts
+ ;
+
+sslopts : CERTIFICATE STRING {
+ free(srv_conf->ssl_cert_file);
+ if ((srv_conf->ssl_cert_file = strdup($2)) == NULL)
+ fatal("out of memory");
+ free($2);
+ }
+ | KEY STRING {
+ free(srv_conf->ssl_key_file);
+ if ((srv_conf->ssl_key_file = strdup($2)) == NULL)
+ fatal("out of memory");
+ free($2);
+ }
+ | CIPHERS STRING {
+ if (strlcpy(srv_conf->ssl_ciphers, $2,
+ sizeof(srv_conf->ssl_ciphers)) >=
+ sizeof(srv_conf->ssl_ciphers)) {
+ yyerror("ciphers too long");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ }
+ ;
+
+dirflags_l : dirflags comma dirflags_l
+ | dirflags
+ ;
+
+dirflags : INDEX STRING {
+ if (strlcpy(srv_conf->index, $2,
+ sizeof(srv_conf->index)) >=
+ sizeof(srv_conf->index)) {
+ yyerror("index file too long");
+ free($2);
+ YYERROR;
+ }
+ srv_conf->flags &= ~SRVFLAG_NO_INDEX;
+ srv_conf->flags |= SRVFLAG_INDEX;
+ free($2);
+ }
+ | NO INDEX {
+ srv_conf->flags &= ~SRVFLAG_INDEX;
+ srv_conf->flags |= SRVFLAG_NO_INDEX;
+ }
+ | AUTO INDEX {
+ srv_conf->flags &= ~SRVFLAG_NO_AUTO_INDEX;
+ srv_conf->flags |= SRVFLAG_AUTO_INDEX;
+ }
+ | NO AUTO INDEX {
+ srv_conf->flags &= ~SRVFLAG_AUTO_INDEX;
+ srv_conf->flags |= SRVFLAG_NO_AUTO_INDEX;
+ }
+ ;
+
+
+logformat : LOG logflags
+ | LOG '{' logflags_l '}'
+ | NO LOG {
+ srv_conf->flags &= ~SRVFLAG_LOG;
+ srv_conf->flags |= SRVFLAG_NO_LOG;
+ }
+ ;
+
+logflags_l : logflags comma logflags_l
+ | logflags
+ ;
+
+
+logflags : STYLE logstyle
+ | SYSLOG {
+ srv_conf->flags &= ~SRVFLAG_NO_SYSLOG;
+ srv_conf->flags |= SRVFLAG_SYSLOG;
+ }
+ | NO SYSLOG {
+ srv_conf->flags &= ~SRVFLAG_SYSLOG;
+ srv_conf->flags |= SRVFLAG_NO_SYSLOG;
+ }
+ | ACCESS STRING {
+ if (strlcpy(srv_conf->accesslog, $2,
+ sizeof(srv_conf->accesslog)) >=
+ sizeof(srv_conf->accesslog)) {
+ yyerror("access log name too long");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ srv_conf->flags |= SRVFLAG_ACCESS_LOG;
+ }
+ | ERR STRING {
+ if (strlcpy(srv_conf->errorlog, $2,
+ sizeof(srv_conf->errorlog)) >=
+ sizeof(srv_conf->errorlog)) {
+ yyerror("error log name too long");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ srv_conf->flags |= SRVFLAG_ERROR_LOG;
+ }
+ ;
+
+logstyle : COMMON {
+ srv_conf->flags &= ~SRVFLAG_NO_LOG;
+ srv_conf->flags |= SRVFLAG_LOG;
+ srv_conf->logformat = LOG_FORMAT_COMMON;
+ }
+ | COMBINED {
+ srv_conf->flags &= ~SRVFLAG_NO_LOG;
+ srv_conf->flags |= SRVFLAG_LOG;
+ srv_conf->logformat = LOG_FORMAT_COMBINED;
+ }
+ | CONNECTION {
+ srv_conf->flags &= ~SRVFLAG_NO_LOG;
+ srv_conf->flags |= SRVFLAG_LOG;
+ srv_conf->logformat = LOG_FORMAT_CONNECTION;
+ }
+ ;
+
+tcpip : '{' tcpflags_l '}'
+ | tcpflags
+ ;
+
+tcpflags_l : tcpflags comma tcpflags_l
+ | tcpflags
+ ;
+
+tcpflags : SACK { srv_conf->tcpflags |= TCPFLAG_SACK; }
+ | NO SACK { srv_conf->tcpflags |= TCPFLAG_NSACK; }
+ | NODELAY {
+ srv_conf->tcpflags |= TCPFLAG_NODELAY;
+ }
+ | NO NODELAY {
+ srv_conf->tcpflags |= TCPFLAG_NNODELAY;
+ }
+ | BACKLOG NUMBER {
+ if ($2 < 0 || $2 > SERVER_MAX_CLIENTS) {
+ yyerror("invalid backlog: %d", $2);
+ YYERROR;
+ }
+ srv_conf->tcpbacklog = $2;
+ }
+ | SOCKET BUFFER NUMBER {
+ srv_conf->tcpflags |= TCPFLAG_BUFSIZ;
+ if ((srv_conf->tcpbufsiz = $3) < 0) {
+ yyerror("invalid socket buffer size: %d", $3);
+ YYERROR;
+ }
+ }
+ | IP STRING NUMBER {
+ if ($3 < 0) {
+ yyerror("invalid ttl: %d", $3);
+ free($2);
+ YYERROR;
+ }
+ if (strcasecmp("ttl", $2) == 0) {
+ srv_conf->tcpflags |= TCPFLAG_IPTTL;
+ srv_conf->tcpipttl = $3;
+ } else if (strcasecmp("minttl", $2) == 0) {
+ srv_conf->tcpflags |= TCPFLAG_IPMINTTL;
+ srv_conf->tcpipminttl = $3;
+ } else {
+ yyerror("invalid TCP/IP flag: %s", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
}
;
@@ -286,7 +663,7 @@ mediaopts_l : mediaopts_l mediaoptsl nl
| mediaoptsl optnl
;
-mediaoptsl : STRING '/' STRING {
+mediaoptsl : STRING '/' STRING {
if (strlcpy(media.media_type, $1,
sizeof(media.media_type)) >=
sizeof(media.media_type) ||
@@ -317,6 +694,9 @@ medianamesl : STRING {
}
free($1);
+ if (!loadcfg)
+ break;
+
if (media_add(conf->sc_mediatypes, &media) == NULL) {
yyerror("failed to add media type");
YYERROR;
@@ -360,8 +740,20 @@ port : PORT STRING {
}
;
-loglevel : UPDATES { $$ = HTTPD_OPT_LOGUPDATE; }
- | ALL { $$ = HTTPD_OPT_LOGALL; }
+timeout : NUMBER
+ {
+ if ($1 < 0) {
+ yyerror("invalid timeout: %d\n", $1);
+ YYERROR;
+ }
+ $$.tv_sec = $1;
+ $$.tv_usec = 0;
+ }
+ ;
+
+comma : ','
+ | nl
+ | /* empty */
;
optnl : '\n' optnl
@@ -405,17 +797,45 @@ lookup(char *s)
{
/* this has to be sorted always */
static const struct keywords keywords[] = {
- { "all", ALL },
+ { "access", ACCESS },
+ { "auto", AUTO },
+ { "backlog", BACKLOG },
+ { "body", BODY },
+ { "buffer", BUFFER },
+ { "certificate", CERTIFICATE },
+ { "chroot", CHROOT },
+ { "ciphers", CIPHERS },
+ { "combined", COMBINED },
+ { "common", COMMON },
+ { "connection", CONNECTION },
+ { "directory", DIRECTORY },
+ { "error", ERR },
+ { "fastcgi", FCGI },
{ "include", INCLUDE },
+ { "index", INDEX },
+ { "ip", IP },
+ { "key", KEY },
{ "listen", LISTEN },
+ { "location", LOCATION },
{ "log", LOG },
+ { "max", MAXIMUM },
+ { "no", NO },
+ { "nodelay", NODELAY },
{ "on", ON },
{ "port", PORT },
{ "prefork", PREFORK },
+ { "request", REQUEST },
+ { "requests", REQUESTS },
{ "root", ROOT },
+ { "sack", SACK },
{ "server", SERVER },
- { "types", TYPES },
- { "updates", UPDATES }
+ { "socket", SOCKET },
+ { "ssl", SSL },
+ { "style", STYLE },
+ { "syslog", SYSLOG },
+ { "tcp", TCP },
+ { "timeout", TIMEOUT },
+ { "types", TYPES }
};
const struct keywords *p;
@@ -841,7 +1261,7 @@ load_config(const char *filename, struct httpd *x_conf)
m.media_name);
errors++;
}
- }
+ }
}
return (errors ? -1 : 0);
diff --git a/proc.c b/proc.c
index 7a6d571..25feff1 100644
--- a/proc.c
+++ b/proc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: proc.c,v 1.1 2014/07/12 23:34:54 reyk Exp $ */
+/* $OpenBSD: proc.c,v 1.4 2014/08/04 15:49:28 reyk Exp $ */
/*
* Copyright (c) 2010 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -38,8 +38,6 @@
#include <pwd.h>
#include <event.h>
-#include <openssl/ssl.h>
-
#include "httpd.h"
void proc_open(struct privsep *, struct privsep_proc *,
@@ -163,7 +161,7 @@ proc_open(struct privsep *ps, struct privsep_proc *p,
/*
* Open socket pairs for our peers
- */
+ */
for (proc = 0; proc < nproc; proc++) {
procs[proc].p_ps = ps;
procs[proc].p_env = ps->ps_env;
@@ -323,6 +321,7 @@ proc_sig_handler(int sig, short event, void *arg)
case SIGCHLD:
case SIGHUP:
case SIGPIPE:
+ case SIGUSR1:
/* ignore */
break;
default:
@@ -409,12 +408,14 @@ proc_run(struct privsep *ps, struct privsep_proc *p,
signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p);
signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p);
signal_set(&ps->ps_evsigpipe, SIGPIPE, proc_sig_handler, p);
+ signal_set(&ps->ps_evsigusr1, SIGUSR1, proc_sig_handler, p);
signal_add(&ps->ps_evsigint, NULL);
signal_add(&ps->ps_evsigterm, NULL);
signal_add(&ps->ps_evsigchld, NULL);
signal_add(&ps->ps_evsighup, NULL);
signal_add(&ps->ps_evsigpipe, NULL);
+ signal_add(&ps->ps_evsigusr1, NULL);
proc_listen(ps, procs, nproc);
diff --git a/server.c b/server.c
index 3ef1401..56f363f 100644
--- a/server.c
+++ b/server.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: server.c,v 1.13 2014/07/25 23:30:58 reyk Exp $ */
+/* $OpenBSD: server.c,v 1.39 2014/08/06 18:38:11 reyk Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -22,6 +22,7 @@
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <sys/uio.h>
#include <sys/tree.h>
#include <sys/hash.h>
@@ -36,30 +37,36 @@
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
+#include <syslog.h>
#include <unistd.h>
#include <stdio.h>
#include <err.h>
#include <pwd.h>
#include <event.h>
#include <fnmatch.h>
-
-#include <openssl/dh.h>
-#include <openssl/ssl.h>
+#include <ressl.h>
#include "httpd.h"
int server_dispatch_parent(int, struct privsep_proc *,
struct imsg *);
+int server_dispatch_logger(int, struct privsep_proc *,
+ struct imsg *);
void server_shutdown(void);
void server_init(struct privsep *, struct privsep_proc *p, void *);
void server_launch(void);
int server_socket(struct sockaddr_storage *, in_port_t,
- struct server *, int, int);
+ struct server_config *, int, int);
int server_socket_listen(struct sockaddr_storage *, in_port_t,
- struct server *);
+ struct server_config *);
+
+int server_ssl_init(struct server *);
+void server_ssl_readcb(int, short, void *);
+void server_ssl_writecb(int, short, void *);
void server_accept(int, short, void *);
+void server_accept_ssl(int, short, void *);
void server_input(struct client *);
extern void bufferevent_read_pressure_cb(struct evbuffer *, size_t,
@@ -73,7 +80,8 @@ static struct httpd *env = NULL;
int proc_id;
static struct privsep_proc procs[] = {
- { "parent", PROC_PARENT, server_dispatch_parent }
+ { "parent", PROC_PARENT, server_dispatch_parent },
+ { "logger", PROC_LOGGER, server_dispatch_logger }
};
pid_t
@@ -96,11 +104,112 @@ 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,
- srv->srv_conf.port, srv)) == -1)
+ srv->srv_conf.port, &srv->srv_conf)) == -1)
+ return (-1);
+
+ return (0);
+}
+
+static char *
+server_load_file(const char *filename, off_t *len)
+{
+ struct stat st;
+ off_t size;
+ char *buf = NULL;
+ int fd;
+
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ return (NULL);
+ if (fstat(fd, &st) != 0)
+ goto fail;
+ size = st.st_size;
+ if ((buf = calloc(1, size + 1)) == NULL)
+ goto fail;
+ if (read(fd, buf, size) != size)
+ goto fail;
+
+ close(fd);
+
+ *len = size;
+ return (buf);
+
+ fail:
+ free(buf);
+ close(fd);
+
+ return (NULL);
+}
+
+int
+server_ssl_load_keypair(struct server *srv)
+{
+ if ((srv->srv_conf.flags & SRVFLAG_SSL) == 0)
+ return (0);
+
+ if ((srv->srv_conf.ssl_cert = server_load_file(
+ srv->srv_conf.ssl_cert_file, &srv->srv_conf.ssl_cert_len)) == NULL)
+ return (-1);
+ log_debug("%s: using certificate %s", __func__,
+ srv->srv_conf.ssl_cert_file);
+
+ if ((srv->srv_conf.ssl_key = server_load_file(
+ srv->srv_conf.ssl_key_file, &srv->srv_conf.ssl_key_len)) == NULL)
return (-1);
+ log_debug("%s: using private key %s", __func__,
+ srv->srv_conf.ssl_key_file);
+
+ return (0);
+}
+
+int
+server_ssl_init(struct server *srv)
+{
+ if ((srv->srv_conf.flags & SRVFLAG_SSL) == 0)
+ return (0);
+
+ log_debug("%s: setting up SSL for %s", __func__, srv->srv_conf.name);
+
+ if (ressl_init() != 0) {
+ log_warn("%s: failed to initialise ressl", __func__);
+ return (-1);
+ }
+ if ((srv->srv_ressl_config = ressl_config_new()) == NULL) {
+ log_warn("%s: failed to get ressl config", __func__);
+ return (-1);
+ }
+ if ((srv->srv_ressl_ctx = ressl_server()) == NULL) {
+ log_warn("%s: failed to get ressl server", __func__);
+ return (-1);
+ }
+
+ ressl_config_set_ciphers(srv->srv_ressl_config,
+ srv->srv_conf.ssl_ciphers);
+ ressl_config_set_cert_mem(srv->srv_ressl_config,
+ srv->srv_conf.ssl_cert, srv->srv_conf.ssl_cert_len);
+ ressl_config_set_key_mem(srv->srv_ressl_config,
+ srv->srv_conf.ssl_key, srv->srv_conf.ssl_key_len);
+
+ if (ressl_configure(srv->srv_ressl_ctx, srv->srv_ressl_config) != 0) {
+ log_warn("%s: failed to configure SSL - %s", __func__,
+ ressl_error(srv->srv_ressl_ctx));
+ return (-1);
+ }
+
+ /* We're now done with the public/private key... */
+ explicit_bzero(srv->srv_conf.ssl_cert, srv->srv_conf.ssl_cert_len);
+ explicit_bzero(srv->srv_conf.ssl_key, srv->srv_conf.ssl_key_len);
+ free(srv->srv_conf.ssl_cert);
+ free(srv->srv_conf.ssl_key);
+ srv->srv_conf.ssl_cert = NULL;
+ srv->srv_conf.ssl_key = NULL;
+ srv->srv_conf.ssl_cert_len = 0;
+ srv->srv_conf.ssl_key_len = 0;
return (0);
}
@@ -136,6 +245,7 @@ server_launch(void)
struct server *srv;
TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
+ server_ssl_init(srv);
server_http_init(srv);
log_debug("%s: running server %s", __func__,
@@ -174,10 +284,16 @@ server_purge(struct server *srv)
TAILQ_REMOVE(&srv->srv_hosts, srv_conf, entry);
/* It might point to our own "default" entry */
- if (srv_conf != &srv->srv_conf)
+ if (srv_conf != &srv->srv_conf) {
+ free(srv_conf->ssl_cert);
+ free(srv_conf->ssl_key);
free(srv_conf);
+ }
}
+ ressl_config_free(srv->srv_ressl_config);
+ ressl_free(srv->srv_ressl_ctx);
+
free(srv);
}
@@ -196,6 +312,43 @@ server_byaddr(struct sockaddr *addr, in_port_t port)
return (NULL);
}
+struct server_config *
+serverconfig_byid(u_int32_t id)
+{
+ struct server *srv;
+ struct server_config *srv_conf;
+
+ TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
+ if (srv->srv_conf.id == id)
+ return (&srv->srv_conf);
+ TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) {
+ if (srv_conf->id == id)
+ return (srv_conf);
+ }
+ }
+
+ return (NULL);
+}
+
+int
+server_foreach(int (*srv_cb)(struct server *,
+ struct server_config *, void *), void *arg)
+{
+ struct server *srv;
+ struct server_config *srv_conf;
+
+ TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
+ if ((srv_cb)(srv, &srv->srv_conf, arg) == -1)
+ return (-1);
+ TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) {
+ if ((srv_cb)(srv, srv_conf, arg) == -1)
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
int
server_socket_af(struct sockaddr_storage *ss, in_port_t port)
{
@@ -235,7 +388,7 @@ server_socket_getport(struct sockaddr_storage *ss)
int
server_socket(struct sockaddr_storage *ss, in_port_t port,
- struct server *srv, int fd, int reuseport)
+ struct server_config *srv_conf, int fd, int reuseport)
{
struct linger lng;
int s = -1, val;
@@ -256,17 +409,17 @@ server_socket(struct sockaddr_storage *ss, in_port_t port,
if (reuseport) {
val = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &val,
- sizeof(int)) == -1)
+ sizeof(int)) == -1)
goto bad;
}
if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
goto bad;
- if (srv->srv_tcpflags & TCPFLAG_BUFSIZ) {
- val = srv->srv_tcpbufsiz;
+ if (srv_conf->tcpflags & TCPFLAG_BUFSIZ) {
+ val = srv_conf->tcpbufsiz;
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
&val, sizeof(val)) == -1)
goto bad;
- val = srv->srv_tcpbufsiz;
+ val = srv_conf->tcpbufsiz;
if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
&val, sizeof(val)) == -1)
goto bad;
@@ -275,14 +428,14 @@ server_socket(struct sockaddr_storage *ss, in_port_t port,
/*
* IP options
*/
- if (srv->srv_tcpflags & TCPFLAG_IPTTL) {
- val = (int)srv->srv_tcpipttl;
+ if (srv_conf->tcpflags & TCPFLAG_IPTTL) {
+ val = (int)srv_conf->tcpipttl;
if (setsockopt(s, IPPROTO_IP, IP_TTL,
&val, sizeof(val)) == -1)
goto bad;
}
- if (srv->srv_tcpflags & TCPFLAG_IPMINTTL) {
- val = (int)srv->srv_tcpipminttl;
+ if (srv_conf->tcpflags & TCPFLAG_IPMINTTL) {
+ val = (int)srv_conf->tcpipminttl;
if (setsockopt(s, IPPROTO_IP, IP_MINTTL,
&val, sizeof(val)) == -1)
goto bad;
@@ -291,8 +444,8 @@ server_socket(struct sockaddr_storage *ss, in_port_t port,
/*
* TCP options
*/
- if (srv->srv_tcpflags & (TCPFLAG_NODELAY|TCPFLAG_NNODELAY)) {
- if (srv->srv_tcpflags & TCPFLAG_NNODELAY)
+ if (srv_conf->tcpflags & (TCPFLAG_NODELAY|TCPFLAG_NNODELAY)) {
+ if (srv_conf->tcpflags & TCPFLAG_NNODELAY)
val = 0;
else
val = 1;
@@ -300,8 +453,8 @@ server_socket(struct sockaddr_storage *ss, in_port_t port,
&val, sizeof(val)) == -1)
goto bad;
}
- if (srv->srv_tcpflags & (TCPFLAG_SACK|TCPFLAG_NSACK)) {
- if (srv->srv_tcpflags & TCPFLAG_NSACK)
+ if (srv_conf->tcpflags & (TCPFLAG_SACK|TCPFLAG_NSACK)) {
+ if (srv_conf->tcpflags & TCPFLAG_NSACK)
val = 0;
else
val = 1;
@@ -320,16 +473,16 @@ server_socket(struct sockaddr_storage *ss, in_port_t port,
int
server_socket_listen(struct sockaddr_storage *ss, in_port_t port,
- struct server *srv)
+ struct server_config *srv_conf)
{
int s;
- if ((s = server_socket(ss, port, srv, -1, 1)) == -1)
+ if ((s = server_socket(ss, port, srv_conf, -1, 1)) == -1)
return (-1);
if (bind(s, (struct sockaddr *)ss, ss->ss_len) == -1)
goto bad;
- if (listen(s, srv->srv_tcpbacklog) == -1)
+ if (listen(s, srv_conf->tcpbacklog) == -1)
goto bad;
return (s);
@@ -339,22 +492,168 @@ server_socket_listen(struct sockaddr_storage *ss, in_port_t port,
return (-1);
}
+int
+server_socket_connect(struct sockaddr_storage *ss, in_port_t port,
+ struct server_config *srv_conf)
+{
+ int s;
+
+ if ((s = server_socket(ss, port, srv_conf, -1, 0)) == -1)
+ return (-1);
+
+ if (connect(s, (struct sockaddr *)ss, ss->ss_len) == -1) {
+ if (errno != EINPROGRESS)
+ goto bad;
+ }
+
+ return (s);
+
+ bad:
+ close(s);
+ return (-1);
+}
+
+void
+server_ssl_readcb(int fd, short event, void *arg)
+{
+ struct bufferevent *bufev = arg;
+ struct client *clt = bufev->cbarg;
+ char rbuf[IBUF_READ_SIZE];
+ int what = EVBUFFER_READ;
+ int howmuch = IBUF_READ_SIZE;
+ int ret;
+ size_t len;
+
+ if (event == EV_TIMEOUT) {
+ what |= EVBUFFER_TIMEOUT;
+ goto err;
+ }
+
+ if (bufev->wm_read.high != 0)
+ howmuch = MIN(sizeof(rbuf), bufev->wm_read.high);
+
+ ret = ressl_read(clt->clt_ressl_ctx, rbuf, howmuch, &len);
+ if (ret == RESSL_READ_AGAIN || ret == RESSL_WRITE_AGAIN) {
+ goto retry;
+ } else if (ret != 0) {
+ what |= EVBUFFER_ERROR;
+ goto err;
+ }
+
+ if (evbuffer_add(bufev->input, rbuf, len) == -1) {
+ what |= EVBUFFER_ERROR;
+ goto err;
+ }
+
+ server_bufferevent_add(&bufev->ev_read, bufev->timeout_read);
+
+ len = EVBUFFER_LENGTH(bufev->input);
+ if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
+ return;
+ if (bufev->wm_read.high != 0 && len > bufev->wm_read.high) {
+ struct evbuffer *buf = bufev->input;
+ event_del(&bufev->ev_read);
+ evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev);
+ return;
+ }
+
+ if (bufev->readcb != NULL)
+ (*bufev->readcb)(bufev, bufev->cbarg);
+ return;
+
+ retry:
+ server_bufferevent_add(&bufev->ev_read, bufev->timeout_read);
+ return;
+
+ err:
+ (*bufev->errorcb)(bufev, what, bufev->cbarg);
+}
+
+void
+server_ssl_writecb(int fd, short event, void *arg)
+{
+ struct bufferevent *bufev = arg;
+ struct client *clt = bufev->cbarg;
+ int ret;
+ short what = EVBUFFER_WRITE;
+ size_t len;
+
+ if (event == EV_TIMEOUT) {
+ what |= EVBUFFER_TIMEOUT;
+ goto err;
+ }
+
+ if (EVBUFFER_LENGTH(bufev->output)) {
+ if (clt->clt_buf == NULL) {
+ clt->clt_buflen = EVBUFFER_LENGTH(bufev->output);
+ if ((clt->clt_buf = malloc(clt->clt_buflen)) == NULL) {
+ what |= EVBUFFER_ERROR;
+ goto err;
+ }
+ bcopy(EVBUFFER_DATA(bufev->output),
+ clt->clt_buf, clt->clt_buflen);
+ }
+ ret = ressl_write(clt->clt_ressl_ctx, clt->clt_buf,
+ clt->clt_buflen, &len);
+ if (ret == RESSL_READ_AGAIN || ret == RESSL_WRITE_AGAIN) {
+ goto retry;
+ } else if (ret != 0) {
+ what |= EVBUFFER_ERROR;
+ goto err;
+ }
+ evbuffer_drain(bufev->output, len);
+ }
+ if (clt->clt_buf != NULL) {
+ free(clt->clt_buf);
+ clt->clt_buf = NULL;
+ clt->clt_buflen = 0;
+ }
+
+ if (EVBUFFER_LENGTH(bufev->output) != 0)
+ server_bufferevent_add(&bufev->ev_write, bufev->timeout_write);
+
+ if (bufev->writecb != NULL &&
+ EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
+ (*bufev->writecb)(bufev, bufev->cbarg);
+ return;
+
+ retry:
+ if (clt->clt_buflen != 0)
+ server_bufferevent_add(&bufev->ev_write, bufev->timeout_write);
+ return;
+
+ err:
+ if (clt->clt_buf != NULL) {
+ free(clt->clt_buf);
+ clt->clt_buf = NULL;
+ clt->clt_buflen = 0;
+ }
+ (*bufev->errorcb)(bufev, what, bufev->cbarg);
+}
+
void
server_input(struct client *clt)
{
struct server_config *srv_conf = clt->clt_srv_conf;
evbuffercb inrd = server_read;
evbuffercb inwr = server_write;
+ socklen_t slen;
if (server_httpdesc_init(clt) == -1) {
- server_close(clt,
- "failed to allocate http descriptor");
+ server_close(clt, "failed to allocate http descriptor");
return;
}
clt->clt_toread = TOREAD_HTTP_HEADER;
inrd = server_read_http;
+ slen = sizeof(clt->clt_sndbufsiz);
+ if (getsockopt(clt->clt_s, SOL_SOCKET, SO_SNDBUF,
+ &clt->clt_sndbufsiz, &slen) == -1) {
+ server_close(clt, "failed to get send buffer size");
+ return;
+ }
+
/*
* Client <-> Server
*/
@@ -365,6 +664,19 @@ server_input(struct client *clt)
return;
}
+ if (srv_conf->flags & SRVFLAG_SSL) {
+ event_set(&clt->clt_bev->ev_read, clt->clt_s, EV_READ,
+ server_ssl_readcb, clt->clt_bev);
+ event_set(&clt->clt_bev->ev_write, clt->clt_s, EV_WRITE,
+ server_ssl_writecb, clt->clt_bev);
+ }
+
+ /* Adjust write watermark to the socket buffer output size */
+ bufferevent_setwatermark(clt->clt_bev, EV_WRITE,
+ clt->clt_sndbufsiz, 0);
+ /* Read at most amount of data that fits in one fcgi record. */
+ bufferevent_setwatermark(clt->clt_bev, EV_READ, 0, FCGI_CONTENT_SIZE);
+
bufferevent_settimeout(clt->clt_bev,
srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec);
bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
@@ -384,15 +696,19 @@ server_write(struct bufferevent *bev, void *arg)
if (clt->clt_done)
goto done;
+
+ bufferevent_enable(bev, EV_READ);
return;
done:
- server_close(clt, "done");
+ (*bev->errorcb)(bev, EVBUFFER_WRITE|EVBUFFER_EOF, bev->cbarg);
return;
}
void
server_dump(struct client *clt, const void *buf, size_t len)
{
+ size_t outlen;
+
if (!len)
return;
@@ -402,11 +718,9 @@ server_dump(struct client *clt, const void *buf, size_t len)
* of non-blocking events etc. This is useful to print an
* error message before gracefully closing the client.
*/
-#if 0
- if (cre->ssl != NULL)
- (void)SSL_write(cre->ssl, buf, len);
+ if (clt->clt_ressl_ctx != NULL)
+ (void)ressl_write(clt->clt_ressl_ctx, buf, len, &outlen);
else
-#endif
(void)write(clt->clt_s, buf, len);
}
@@ -424,10 +738,9 @@ server_read(struct bufferevent *bev, void *arg)
goto fail;
if (clt->clt_done)
goto done;
- bufferevent_enable(bev, EV_READ);
return;
done:
- server_close(clt, "done");
+ (*bev->errorcb)(bev, EVBUFFER_READ|EVBUFFER_EOF, bev->cbarg);
return;
fail:
server_close(clt, strerror(errno));
@@ -549,6 +862,13 @@ server_accept(int fd, short event, void *arg)
return;
}
+ if (srv->srv_conf.flags & SRVFLAG_SSL) {
+ event_again(&clt->clt_ev, clt->clt_s, EV_TIMEOUT|EV_READ,
+ server_accept_ssl, &clt->clt_tv_start,
+ &srv->srv_conf.timeout, clt);
+ return;
+ }
+
server_input(clt);
return;
@@ -566,6 +886,41 @@ server_accept(int fd, short event, void *arg)
}
void
+server_accept_ssl(int fd, short event, void *arg)
+{
+ struct client *clt = (struct client *)arg;
+ struct server *srv = (struct server *)clt->clt_srv;
+ int ret;
+
+ if (event == EV_TIMEOUT) {
+ server_close(clt, "SSL accept timeout");
+ return;
+ }
+
+ if (srv->srv_ressl_ctx == NULL)
+ fatalx("NULL ressl context");
+
+ ret = ressl_accept_socket(srv->srv_ressl_ctx, &clt->clt_ressl_ctx,
+ clt->clt_s);
+ if (ret == RESSL_READ_AGAIN) {
+ event_again(&clt->clt_ev, clt->clt_s, EV_TIMEOUT|EV_READ,
+ server_accept_ssl, &clt->clt_tv_start,
+ &srv->srv_conf.timeout, clt);
+ } else if (ret == RESSL_WRITE_AGAIN) {
+ event_again(&clt->clt_ev, clt->clt_s, EV_TIMEOUT|EV_WRITE,
+ server_accept_ssl, &clt->clt_tv_start,
+ &srv->srv_conf.timeout, clt);
+ } else if (ret != 0) {
+ log_warnx("%s: SSL accept failed - %s", __func__,
+ ressl_error(srv->srv_ressl_ctx));
+ return;
+ }
+
+ server_input(clt);
+ return;
+}
+
+void
server_inflight_dec(struct client *clt, const char *why)
{
if (clt != NULL) {
@@ -577,38 +932,80 @@ server_inflight_dec(struct client *clt, const char *why)
/* the file was never opened, thus this was an inflight client. */
server_inflight--;
- log_debug("%s: inflight decremented, now %d, %s",
+ DPRINTF("%s: inflight decremented, now %d, %s",
__func__, server_inflight, why);
}
void
-server_close(struct client *clt, const char *msg)
+server_sendlog(struct server_config *srv_conf, int cmd, const char *emsg, ...)
{
- char ibuf[MAXHOSTNAMELEN], obuf[MAXHOSTNAMELEN];
- char *ptr = NULL;
- struct server *srv = clt->clt_srv;
- struct server_config *srv_conf = clt->clt_srv_conf;
+ va_list ap;
+ char *msg;
+ int ret;
+ struct iovec iov[2];
+
+ if (srv_conf->flags & SRVFLAG_SYSLOG) {
+ va_start(ap, emsg);
+ if (cmd == IMSG_LOG_ACCESS)
+ vlog(LOG_INFO, emsg, ap);
+ else
+ vlog(LOG_DEBUG, emsg, ap);
+ va_end(ap);
+ return;
+ }
- SPLAY_REMOVE(client_tree, &srv->srv_clients, clt);
+ va_start(ap, emsg);
+ ret = vasprintf(&msg, emsg, ap);
+ va_end(ap);
+ if (ret == -1) {
+ log_warn("%s: vasprintf", __func__);
+ return;
+ }
- /* free the HTTP descriptors incl. headers */
- server_close_http(clt);
+ iov[0].iov_base = &srv_conf->id;
+ iov[0].iov_len = sizeof(srv_conf->id);
+ iov[1].iov_base = msg;
+ iov[1].iov_len = strlen(msg) + 1;
- event_del(&clt->clt_ev);
- if (clt->clt_bev != NULL)
- bufferevent_disable(clt->clt_bev, EV_READ|EV_WRITE);
- if (clt->clt_file != NULL)
- bufferevent_disable(clt->clt_file, EV_READ|EV_WRITE);
+ proc_composev_imsg(env->sc_ps, PROC_LOGGER, -1, cmd, -1, iov, 2);
+}
+
+void
+server_log(struct client *clt, const char *msg)
+{
+ char ibuf[MAXHOSTNAMELEN], obuf[MAXHOSTNAMELEN];
+ struct server_config *srv_conf = clt->clt_srv_conf;
+ char *ptr = NULL;
+ int debug_cmd = -1;
+ extern int verbose;
+
+ switch (srv_conf->logformat) {
+ case LOG_FORMAT_CONNECTION:
+ debug_cmd = IMSG_LOG_ACCESS;
+ break;
+ default:
+ if (verbose > 1)
+ debug_cmd = IMSG_LOG_ERROR;
+ if (EVBUFFER_LENGTH(clt->clt_log)) {
+ while ((ptr =
+ evbuffer_readline(clt->clt_log)) != NULL) {
+ server_sendlog(srv_conf,
+ IMSG_LOG_ACCESS, "%s", ptr);
+ free(ptr);
+ }
+ }
+ break;
+ }
- if ((env->sc_opts & HTTPD_OPT_LOGUPDATE) && msg != NULL) {
- memset(&ibuf, 0, sizeof(ibuf));
- memset(&obuf, 0, sizeof(obuf));
+ if (debug_cmd != -1 && msg != NULL) {
+ memset(ibuf, 0, sizeof(ibuf));
+ memset(obuf, 0, sizeof(obuf));
(void)print_host(&clt->clt_ss, ibuf, sizeof(ibuf));
(void)server_http_host(&clt->clt_srv_ss, obuf, sizeof(obuf));
if (EVBUFFER_LENGTH(clt->clt_log) &&
- evbuffer_add_printf(clt->clt_log, "\r\n") != -1)
+ evbuffer_add_printf(clt->clt_log, "\n") != -1)
ptr = evbuffer_readline(clt->clt_log);
- log_info("server %s, "
+ server_sendlog(srv_conf, debug_cmd, "server %s, "
"client %d (%d active), %s:%u -> %s, "
"%s%s%s", srv_conf->name, clt->clt_id, server_clients,
ibuf, ntohs(clt->clt_port), obuf, msg,
@@ -616,19 +1013,44 @@ server_close(struct client *clt, const char *msg)
if (ptr != NULL)
free(ptr);
}
+}
+
+void
+server_close(struct client *clt, const char *msg)
+{
+ struct server *srv = clt->clt_srv;
+
+ 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);
+ if (clt->clt_srvbev != NULL)
+ bufferevent_disable(clt->clt_srvbev, EV_READ|EV_WRITE);
+
+ server_log(clt, msg);
if (clt->clt_bev != NULL)
bufferevent_free(clt->clt_bev);
if (clt->clt_output != NULL)
evbuffer_free(clt->clt_output);
+ if (clt->clt_srvevb != NULL)
+ evbuffer_free(clt->clt_srvevb);
- if (clt->clt_file != NULL)
- bufferevent_free(clt->clt_file);
+ if (clt->clt_srvbev != NULL)
+ bufferevent_free(clt->clt_srvbev);
if (clt->clt_fd != -1)
close(clt->clt_fd);
if (clt->clt_s != -1)
close(clt->clt_s);
+ if (clt->clt_ressl_ctx != NULL)
+ ressl_close(clt->clt_ressl_ctx);
+ ressl_free(clt->clt_ressl_ctx);
+
server_inflight_dec(clt, __func__);
if (clt->clt_log != NULL)
@@ -665,6 +1087,17 @@ server_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
}
int
+server_dispatch_logger(int fd, struct privsep_proc *p, struct imsg *imsg)
+{
+ switch (imsg->hdr.type) {
+ default:
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
server_bufferevent_add(struct event *ev, int timeout)
{
struct timeval tv, *ptv = NULL;
diff --git a/server_fcgi.c b/server_fcgi.c
new file mode 100644
index 0000000..848bae4
--- /dev/null
+++ b/server_fcgi.c
@@ -0,0 +1,643 @@
+/* $OpenBSD: server_fcgi.c,v 1.33 2014/08/13 18:00:54 chrisz Exp $ */
+
+/*
+ * Copyright (c) 2014 Florian Obser <florian@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/tree.h>
+#include <sys/hash.h>
+
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <err.h>
+#include <event.h>
+
+#include "httpd.h"
+#include "http.h"
+
+#define FCGI_PADDING_SIZE 255
+#define FCGI_RECORD_SIZE \
+ (sizeof(struct fcgi_record_header) + FCGI_CONTENT_SIZE + FCGI_PADDING_SIZE)
+
+#define FCGI_BEGIN_REQUEST 1
+#define FCGI_ABORT_REQUEST 2
+#define FCGI_END_REQUEST 3
+#define FCGI_PARAMS 4
+#define FCGI_STDIN 5
+#define FCGI_STDOUT 6
+#define FCGI_STDERR 7
+#define FCGI_DATA 8
+#define FCGI_GET_VALUES 9
+#define FCGI_GET_VALUES_RESULT 10
+#define FCGI_UNKNOWN_TYPE 11
+#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
+
+#define FCGI_RESPONDER 1
+
+struct fcgi_record_header {
+ uint8_t version;
+ uint8_t type;
+ uint16_t id;
+ uint16_t content_len;
+ uint8_t padding_len;
+ uint8_t reserved;
+} __packed;
+
+struct fcgi_begin_request_body {
+ uint16_t role;
+ uint8_t flags;
+ uint8_t reserved[5];
+} __packed;
+
+struct server_fcgi_param {
+ int total_len;
+ uint8_t buf[FCGI_RECORD_SIZE];
+};
+
+int server_fcgi_header(struct client *, u_int);
+void server_fcgi_read(struct bufferevent *, void *);
+int server_fcgi_writeheader(struct client *, struct kv *, void *);
+int fcgi_add_param(struct server_fcgi_param *, const char *, const char *,
+ struct client *);
+int get_status(struct evbuffer *);
+
+int
+server_fcgi(struct httpd *env, struct client *clt)
+{
+ struct server_fcgi_param param;
+ struct server_config *srv_conf = clt->clt_srv_conf;
+ struct http_descriptor *desc = clt->clt_desc;
+ struct fcgi_record_header *h;
+ struct fcgi_begin_request_body *begin;
+ char hbuf[MAXHOSTNAMELEN];
+ size_t scriptlen;
+ int pathlen;
+ int fd = -1, ret;
+ const char *errstr = NULL;
+ char *str, *p, *script = NULL;
+
+ if (srv_conf->socket[0] == ':') {
+ struct sockaddr_storage ss;
+ in_port_t port;
+
+ p = srv_conf->socket + 1;
+
+ port = strtonum(p, 0, 0xffff, &errstr);
+ if (errstr != NULL) {
+ log_warn("%s: strtonum %s, %s", __func__, p, errstr);
+ goto fail;
+ }
+ memset(&ss, 0, sizeof(ss));
+ ss.ss_family = AF_INET;
+ ((struct sockaddr_in *)
+ &ss)->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ port = htons(port);
+
+ if ((fd = server_socket_connect(&ss, port, srv_conf)) == -1)
+ goto fail;
+ } else {
+ struct sockaddr_un sun;
+ size_t len;
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ goto fail;
+
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ len = strlcpy(sun.sun_path,
+ srv_conf->socket, sizeof(sun.sun_path));
+ if (len >= sizeof(sun.sun_path)) {
+ errstr = "socket path to long";
+ goto fail;
+ }
+ sun.sun_len = len;
+
+ if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
+ goto fail;
+ }
+
+ socket_set_blockmode(fd, BM_NONBLOCK);
+
+ memset(hbuf, 0, sizeof(hbuf));
+ clt->clt_fcgi_state = FCGI_READ_HEADER;
+ clt->clt_fcgi_toread = sizeof(struct fcgi_record_header);
+
+ if (clt->clt_srvevb != NULL)
+ evbuffer_free(clt->clt_srvevb);
+
+ clt->clt_srvevb = evbuffer_new();
+ if (clt->clt_srvevb == NULL) {
+ errstr = "failed to allocate evbuffer";
+ goto fail;
+ }
+
+ clt->clt_fd = fd;
+ if (clt->clt_srvbev != NULL)
+ bufferevent_free(clt->clt_srvbev);
+
+ clt->clt_srvbev = bufferevent_new(fd, server_fcgi_read,
+ NULL, server_file_error, clt);
+ if (clt->clt_srvbev == NULL) {
+ errstr = "failed to allocate fcgi buffer event";
+ goto fail;
+ }
+
+ memset(&param, 0, sizeof(param));
+
+ h = (struct fcgi_record_header *)&param.buf;
+ h->version = 1;
+ h->type = FCGI_BEGIN_REQUEST;
+ h->id = htons(1);
+ h->content_len = htons(sizeof(struct fcgi_begin_request_body));
+ h->padding_len = 0;
+
+ begin = (struct fcgi_begin_request_body *)&param.buf[sizeof(struct
+ fcgi_record_header)];
+ begin->role = htons(FCGI_RESPONDER);
+
+ bufferevent_write(clt->clt_srvbev, &param.buf,
+ sizeof(struct fcgi_record_header) +
+ sizeof(struct fcgi_begin_request_body));
+
+ h->type = FCGI_PARAMS;
+ h->content_len = param.total_len = 0;
+
+ if ((pathlen = asprintf(&script, "%s%s", srv_conf->root,
+ desc->http_path_alias != NULL ?
+ desc->http_path_alias : desc->http_path)) == -1) {
+ errstr = "failed to get script name";
+ goto fail;
+ }
+
+ scriptlen = path_info(script);
+ /*
+ * no part of root should show up in PATH_INFO.
+ * therefore scriptlen should be >= strlen(root)
+ */
+ if (scriptlen < strlen(srv_conf->root))
+ scriptlen = strlen(srv_conf->root);
+ if ((int)scriptlen < pathlen) {
+ if (fcgi_add_param(&param, "PATH_INFO",
+ script + scriptlen, clt) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+ script[scriptlen] = '\0';
+ }
+
+ if (fcgi_add_param(&param, "SCRIPT_NAME",
+ script + strlen(srv_conf->root), clt) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+ if (fcgi_add_param(&param, "SCRIPT_FILENAME", script, clt) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+
+ if (desc->http_query)
+ if (fcgi_add_param(&param, "QUERY_STRING", desc->http_query,
+ clt) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+
+ if (fcgi_add_param(&param, "DOCUMENT_ROOT", srv_conf->root,
+ clt) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+ if (fcgi_add_param(&param, "DOCUMENT_URI", desc->http_path,
+ clt) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+ if (fcgi_add_param(&param, "GATEWAY_INTERFACE", "CGI/1.1",
+ clt) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+
+ /* Add HTTP_* headers */
+ if (server_headers(clt, server_fcgi_writeheader, &param) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+
+ if (srv_conf->flags & SRVFLAG_SSL)
+ if (fcgi_add_param(&param, "HTTPS", "on", clt) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+
+ (void)print_host(&clt->clt_ss, hbuf, sizeof(hbuf));
+ if (fcgi_add_param(&param, "REMOTE_ADDR", hbuf, clt) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+
+ (void)snprintf(hbuf, sizeof(hbuf), "%d", ntohs(clt->clt_port));
+ if (fcgi_add_param(&param, "REMOTE_PORT", hbuf, clt) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+
+ if (fcgi_add_param(&param, "REQUEST_METHOD",
+ server_httpmethod_byid(desc->http_method), clt) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+
+ if (!desc->http_query) {
+ if (fcgi_add_param(&param, "REQUEST_URI", desc->http_path,
+ clt) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+ } else if (asprintf(&str, "%s?%s", desc->http_path,
+ desc->http_query) != -1) {
+ ret = fcgi_add_param(&param, "REQUEST_URI", str, clt);
+ free(str);
+ if (ret == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+ }
+
+ (void)print_host(&clt->clt_srv_ss, hbuf, sizeof(hbuf));
+ if (fcgi_add_param(&param, "SERVER_ADDR", hbuf, clt) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+
+ (void)snprintf(hbuf, sizeof(hbuf), "%d",
+ ntohs(server_socket_getport(&clt->clt_srv_ss)));
+ if (fcgi_add_param(&param, "SERVER_PORT", hbuf, clt) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+
+ if (fcgi_add_param(&param, "SERVER_NAME", srv_conf->name,
+ clt) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+
+ if (fcgi_add_param(&param, "SERVER_PROTOCOL", desc->http_version,
+ clt) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+
+ if (fcgi_add_param(&param, "SERVER_SOFTWARE", HTTPD_SERVERNAME,
+ clt) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+
+ if (param.total_len != 0) { /* send last params record */
+ bufferevent_write(clt->clt_srvbev, &param.buf,
+ sizeof(struct fcgi_record_header) +
+ ntohs(h->content_len));
+ }
+
+ /* send "no more params" message */
+ h->content_len = 0;
+ bufferevent_write(clt->clt_srvbev, &param.buf,
+ sizeof(struct fcgi_record_header));
+
+ bufferevent_settimeout(clt->clt_srvbev,
+ srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec);
+ bufferevent_enable(clt->clt_srvbev, EV_READ|EV_WRITE);
+ if (clt->clt_toread != 0) {
+ server_read_httpcontent(clt->clt_bev, clt);
+ bufferevent_enable(clt->clt_bev, EV_READ);
+ } else {
+ bufferevent_disable(clt->clt_bev, EV_READ);
+ fcgi_add_stdin(clt, NULL);
+ }
+
+ /*
+ * persist is not supported yet because we don't get the
+ * Content-Length from slowcgi and don't support chunked encoding.
+ */
+ clt->clt_persist = 0;
+ clt->clt_done = 0;
+
+ free(script);
+ return (0);
+ fail:
+ free(script);
+ if (errstr == NULL)
+ errstr = strerror(errno);
+ server_abort_http(clt, 500, errstr);
+ return (-1);
+}
+
+int
+fcgi_add_stdin(struct client *clt, struct evbuffer *evbuf)
+{
+ struct fcgi_record_header h;
+
+ memset(&h, 0, sizeof(h));
+ h.version = 1;
+ h.type = FCGI_STDIN;
+ h.id = htons(1);
+ h.padding_len = 0;
+
+ if (evbuf == NULL) {
+ h.content_len = 0;
+ return bufferevent_write(clt->clt_srvbev, &h,
+ sizeof(struct fcgi_record_header));
+ } else {
+ h.content_len = htons(EVBUFFER_LENGTH(evbuf));
+ if (bufferevent_write(clt->clt_srvbev, &h,
+ sizeof(struct fcgi_record_header)) == -1)
+ return -1;
+ return bufferevent_write_buffer(clt->clt_srvbev, evbuf);
+ }
+ return (0);
+}
+
+int
+fcgi_add_param(struct server_fcgi_param *p, const char *key,
+ const char *val, struct client *clt)
+{
+ struct fcgi_record_header *h;
+ int len = 0;
+ int key_len = strlen(key);
+ int val_len = strlen(val);
+ uint8_t *param;
+
+ len += key_len + val_len;
+ len += key_len > 127 ? 4 : 1;
+ len += val_len > 127 ? 4 : 1;
+
+ DPRINTF("%s: %s[%d] => %s[%d], total_len: %d", __func__, key, key_len,
+ val, val_len, p->total_len);
+
+ if (len > FCGI_CONTENT_SIZE)
+ return (-1);
+
+ if (p->total_len + len > FCGI_CONTENT_SIZE) {
+ bufferevent_write(clt->clt_srvbev, p->buf,
+ sizeof(struct fcgi_record_header) + p->total_len);
+ p->total_len = 0;
+ }
+
+ h = (struct fcgi_record_header *)p->buf;
+ param = p->buf + sizeof(*h) + p->total_len;
+
+ if (key_len > 127) {
+ *param++ = ((key_len >> 24) & 0xff) | 0x80;
+ *param++ = ((key_len >> 16) & 0xff);
+ *param++ = ((key_len >> 8) & 0xff);
+ *param++ = (key_len & 0xff);
+ } else
+ *param++ = key_len;
+
+ if (val_len > 127) {
+ *param++ = ((val_len >> 24) & 0xff) | 0x80;
+ *param++ = ((val_len >> 16) & 0xff);
+ *param++ = ((val_len >> 8) & 0xff);
+ *param++ = (val_len & 0xff);
+ } else
+ *param++ = val_len;
+
+ memcpy(param, key, key_len);
+ param += key_len;
+ memcpy(param, val, val_len);
+
+ p->total_len += len;
+
+ h->content_len = htons(p->total_len);
+ return (0);
+}
+
+void
+server_fcgi_read(struct bufferevent *bev, void *arg)
+{
+ uint8_t buf[FCGI_RECORD_SIZE];
+ struct client *clt = (struct client *) arg;
+ struct fcgi_record_header *h;
+ size_t len;
+ char *ptr;
+
+ do {
+ len = bufferevent_read(bev, &buf, clt->clt_fcgi_toread);
+ /* XXX error handling */
+ evbuffer_add(clt->clt_srvevb, &buf, len);
+ clt->clt_fcgi_toread -= len;
+ DPRINTF("%s: len: %lu toread: %d state: %d", __func__, len,
+ clt->clt_fcgi_toread, clt->clt_fcgi_state);
+
+ if (clt->clt_fcgi_toread != 0)
+ return;
+
+ switch (clt->clt_fcgi_state) {
+ case FCGI_READ_HEADER:
+ clt->clt_fcgi_state = FCGI_READ_CONTENT;
+ h = (struct fcgi_record_header *)
+ EVBUFFER_DATA(clt->clt_srvevb);
+ DPRINTF("%s: record header: version %d type %d id %d "
+ "content len %d padding %d", __func__,
+ h->version, h->type, ntohs(h->id),
+ ntohs(h->content_len), h->padding_len);
+ clt->clt_fcgi_type = h->type;
+ clt->clt_fcgi_toread = ntohs(h->content_len);
+ clt->clt_fcgi_padding_len = h->padding_len;
+ evbuffer_drain(clt->clt_srvevb,
+ EVBUFFER_LENGTH(clt->clt_srvevb));
+ if (clt->clt_fcgi_toread != 0)
+ break;
+ else if (clt->clt_fcgi_type == FCGI_STDOUT &&
+ !clt->clt_chunk) {
+ server_abort_http(clt, 500, "empty stdout");
+ return;
+ }
+
+ /* fallthrough if content_len == 0 */
+ case FCGI_READ_CONTENT:
+ if (clt->clt_fcgi_type == FCGI_STDERR &&
+ EVBUFFER_LENGTH(clt->clt_srvevb) > 0) {
+ if ((ptr = get_string(
+ EVBUFFER_DATA(clt->clt_srvevb),
+ EVBUFFER_LENGTH(clt->clt_srvevb)))
+ != NULL) {
+ server_sendlog(clt->clt_srv_conf,
+ IMSG_LOG_ERROR, "%s", ptr);
+ free(ptr);
+ }
+ }
+ if (clt->clt_fcgi_type == FCGI_STDOUT &&
+ EVBUFFER_LENGTH(clt->clt_srvevb) > 0) {
+ if (++clt->clt_chunk == 1)
+ server_fcgi_header(clt,
+ get_status(clt->clt_srvevb));
+ server_bufferevent_write_buffer(clt,
+ clt->clt_srvevb);
+ }
+ evbuffer_drain(clt->clt_srvevb,
+ EVBUFFER_LENGTH(clt->clt_srvevb));
+ if (!clt->clt_fcgi_padding_len) {
+ clt->clt_fcgi_state = FCGI_READ_HEADER;
+ clt->clt_fcgi_toread =
+ sizeof(struct fcgi_record_header);
+ } else {
+ clt->clt_fcgi_state = FCGI_READ_PADDING;
+ clt->clt_fcgi_toread =
+ clt->clt_fcgi_padding_len;
+ }
+ break;
+ case FCGI_READ_PADDING:
+ evbuffer_drain(clt->clt_srvevb,
+ EVBUFFER_LENGTH(clt->clt_srvevb));
+ clt->clt_fcgi_state = FCGI_READ_HEADER;
+ clt->clt_fcgi_toread =
+ sizeof(struct fcgi_record_header);
+ break;
+ }
+ } while (len > 0);
+}
+
+int
+server_fcgi_header(struct client *clt, u_int code)
+{
+ struct http_descriptor *desc = clt->clt_desc;
+ const char *error;
+ char tmbuf[32];
+
+ if (desc == NULL || (error = server_httperror_byid(code)) == NULL)
+ return (-1);
+
+ if (server_log_http(clt, code, 0) == -1)
+ return (-1);
+
+ kv_purge(&desc->http_headers);
+
+ /* Add error codes */
+ if (kv_setkey(&desc->http_pathquery, "%lu", code) == -1 ||
+ kv_set(&desc->http_pathquery, "%s", error) == -1)
+ return (-1);
+
+ /* Add headers */
+ if (kv_add(&desc->http_headers, "Server", HTTPD_SERVERNAME) == NULL)
+ return (-1);
+
+ /* Is it a persistent connection? */
+ if (clt->clt_persist) {
+ if (kv_add(&desc->http_headers,
+ "Connection", "keep-alive") == NULL)
+ return (-1);
+ } else if (kv_add(&desc->http_headers, "Connection", "close") == NULL)
+ return (-1);
+
+ /* Date header is mandatory and should be added last */
+ server_http_date(tmbuf, sizeof(tmbuf));
+ if (kv_add(&desc->http_headers, "Date", tmbuf) == NULL)
+ return (-1);
+
+ /* Write initial header (fcgi might append more) */
+ if (server_writeresponse_http(clt) == -1 ||
+ server_bufferevent_print(clt, "\r\n") == -1 ||
+ server_headers(clt, server_writeheader_http, NULL) == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+server_fcgi_writeheader(struct client *clt, struct kv *hdr, void *arg)
+{
+ struct server_fcgi_param *param = arg;
+ char *val, *name, *p;
+ const char *key;
+ int ret;
+
+ if (hdr->kv_flags & KV_FLAG_INVALID)
+ return (0);
+
+ /* The key might have been updated in the parent */
+ if (hdr->kv_parent != NULL && hdr->kv_parent->kv_key != NULL)
+ key = hdr->kv_parent->kv_key;
+ else
+ key = hdr->kv_key;
+
+ val = hdr->kv_value;
+
+ if (strcasecmp(key, "Content-Length") == 0 ||
+ strcasecmp(key, "Content-Type") == 0) {
+ if ((name = strdup(key)) == NULL)
+ return (-1);
+ } else {
+ if (asprintf(&name, "HTTP_%s", key) == -1)
+ return (-1);
+ }
+
+ for (p = name; *p != '\0'; p++) {
+ if (isalpha((unsigned char)*p))
+ *p = toupper((unsigned char)*p);
+ else
+ *p = '_';
+ }
+
+ ret = fcgi_add_param(param, name, val, clt);
+ free(name);
+
+ return (ret);
+}
+
+int
+get_status(struct evbuffer *bev)
+{
+ int code;
+ char *statusline, *tok;
+ const char *errstr;
+
+ /* XXX This is a hack. We need to parse the response header. */
+ code = 200;
+ if (strncmp(EVBUFFER_DATA(bev), "Status: ", strlen("Status: ")) == 0) {
+ statusline = get_string(EVBUFFER_DATA(bev),
+ EVBUFFER_LENGTH(bev));
+ if (strtok(statusline, " ") != NULL) {
+ if ((tok = strtok(NULL, " ")) != NULL) {
+ code = (int) strtonum(tok, 100, 600, &errstr);
+ if (errstr != NULL || server_httperror_byid(
+ code) == NULL)
+ code = 200;
+ }
+ }
+ free(statusline);
+ }
+ return code;
+}
diff --git a/server_file.c b/server_file.c
index 0fc3d69..e87bcbd 100644
--- a/server_file.c
+++ b/server_file.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: server_file.c,v 1.17 2014/07/26 22:38:38 reyk Exp $ */
+/* $OpenBSD: server_file.c,v 1.33 2014/08/14 07:50:35 chrisz Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -39,34 +39,43 @@
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
+#include <time.h>
#include <err.h>
#include <event.h>
-#include <openssl/ssl.h>
-
#include "httpd.h"
#include "http.h"
-int server_file_access(struct http_descriptor *, char *, size_t,
+int server_file_access(struct httpd *, struct client *, char *, size_t);
+int server_file_request(struct httpd *, struct client *, char *,
struct stat *);
int server_file_index(struct httpd *, struct client *);
-void server_file_error(struct bufferevent *, short, void *);
+int server_file_method(struct client *);
int
-server_file_access(struct http_descriptor *desc, char *path, size_t len,
- struct stat *st)
+server_file_access(struct httpd *env, struct client *clt,
+ char *path, size_t len)
{
- struct stat stb;
- char *newpath;
+ struct http_descriptor *desc = clt->clt_desc;
+ struct server_config *srv_conf = clt->clt_srv_conf;
+ struct stat st;
+ char *newpath;
+ int ret;
errno = 0;
if (access(path, R_OK) == -1) {
goto fail;
- } else if (stat(path, st) == -1) {
+ } else if (stat(path, &st) == -1) {
goto fail;
- } else if (S_ISDIR(st->st_mode)) {
- if (!len) {
+ } else if (S_ISDIR(st.st_mode)) {
+ /* Deny access if directory indexing is disabled */
+ if (srv_conf->flags & SRVFLAG_NO_INDEX) {
+ errno = EACCES;
+ goto fail;
+ }
+
+ if (desc->http_path_alias != NULL) {
/* Recursion - the index "file" is a directory? */
errno = EINVAL;
goto fail;
@@ -74,39 +83,56 @@ server_file_access(struct http_descriptor *desc, char *path, size_t len,
/* Redirect to path with trailing "/" */
if (path[strlen(path) - 1] != '/') {
- if (asprintf(&newpath, "http://%s%s/",
+ if (asprintf(&newpath, "http%s://%s%s/",
+ srv_conf->flags & SRVFLAG_SSL ? "s" : "",
desc->http_host, desc->http_path) == -1)
return (500);
- free(desc->http_path);
- desc->http_path = newpath;
+ /* Path alias will be used for the redirection */
+ desc->http_path_alias = newpath;
/* Indicate that the file has been moved */
return (301);
}
- /* Otherwise append the default index file */
- if (strlcat(path, HTTPD_INDEX, len) >= len) {
+ /* Append the default index file to the location */
+ if (asprintf(&newpath, "%s%s", desc->http_path,
+ srv_conf->index) == -1)
+ return (500);
+ desc->http_path_alias = newpath;
+ if (server_getlocation(clt, newpath) != srv_conf) {
+ /* The location has changed */
+ return (server_file(env, clt));
+ }
+
+ /* Otherwise append the default index file to the path */
+ if (strlcat(path, srv_conf->index, len) >= len) {
errno = EACCES;
goto fail;
}
- /* Check again but set len to 0 to avoid recursion */
- if (server_file_access(desc, path, 0, &stb) == 404) {
+ ret = server_file_access(env, clt, path, len);
+ if (ret == 404) {
/*
- * Index file not found, return success but
- * indicate directory with S_ISDIR.
+ * Index file not found; fail if auto-indexing is
+ * not enabled, otherwise return success but
+ * indicate directory with S_ISDIR of the previous
+ * stat.
*/
- } else {
- /* return updated stat from index file */
- memcpy(st, &stb, sizeof(*st));
+ if ((srv_conf->flags & SRVFLAG_AUTO_INDEX) == 0) {
+ errno = EACCES;
+ goto fail;
+ }
+
+ return (server_file_index(env, clt));
}
- } else if (!S_ISREG(st->st_mode)) {
+ return (ret);
+ } else if (!S_ISREG(st.st_mode)) {
/* Don't follow symlinks and ignore special files */
errno = EACCES;
goto fail;
}
- return (0);
+ return (server_file_request(env, clt, path, &st));
fail:
switch (errno) {
@@ -126,72 +152,117 @@ server_file(struct httpd *env, struct client *clt)
{
struct http_descriptor *desc = clt->clt_desc;
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;
+ const char *errstr = NULL;
+ int ret = 500;
+
+ if (srv_conf->flags & SRVFLAG_FCGI)
+ return (server_fcgi(env, clt));
/* Request path is already canonicalized */
if ((size_t)snprintf(path, sizeof(path), "%s%s",
- srv_conf->docroot, desc->http_path) >= sizeof(path)) {
- /* Do not echo the uncanonicalized path */
- server_abort_http(clt, 500, desc->http_path);
- return (-1);
+ srv_conf->root,
+ desc->http_path_alias != NULL ?
+ desc->http_path_alias : desc->http_path) >= sizeof(path)) {
+ errstr = desc->http_path;
+ goto abort;
}
/* Returns HTTP status code on error */
- if ((ret = server_file_access(desc, path, sizeof(path), &st)) != 0) {
- server_abort_http(clt, ret, desc->http_path);
- return (-1);
+ if ((ret = server_file_access(env, clt, path, sizeof(path))) > 0) {
+ errstr = desc->http_path_alias != NULL ?
+ desc->http_path_alias : desc->http_path;
+ goto abort;
}
- if (S_ISDIR(st.st_mode)) {
- /* list directory index */
- return (server_file_index(env, clt));
+ return (ret);
+
+ abort:
+ if (errstr == NULL)
+ errstr = strerror(errno);
+ server_abort_http(clt, ret, errstr);
+ return (-1);
+}
+
+int
+server_file_method(struct client *clt)
+{
+ struct http_descriptor *desc = clt->clt_desc;
+
+ switch (desc->http_method) {
+ case HTTP_METHOD_GET:
+ case HTTP_METHOD_HEAD:
+ return (0);
+ default:
+ /* Other methods are not allowed */
+ errno = EACCES;
+ return (405);
+ }
+ /* NOTREACHED */
+}
+
+int
+server_file_request(struct httpd *env, struct client *clt, char *path,
+ struct stat *st)
+{
+ struct server_config *srv_conf = clt->clt_srv_conf;
+ struct media_type *media;
+ const char *errstr = NULL;
+ int fd = -1, ret, code = 500;
+
+ if ((ret = server_file_method(clt)) != 0) {
+ code = ret;
+ goto abort;
}
/* Now open the file, should be readable or we have another problem */
if ((fd = open(path, O_RDONLY)) == -1)
- goto fail;
-
- /* File descriptor is opened, decrement inflight counter */
- server_inflight_dec(clt, __func__);
+ goto abort;
media = media_find(env->sc_mediatypes, path);
- ret = server_response_http(clt, 200, media, st.st_size);
+ ret = server_response_http(clt, 200, media, st->st_size);
switch (ret) {
case -1:
goto fail;
case 0:
/* Connection is already finished */
close(fd);
- return (0);
+ goto done;
default:
break;
}
clt->clt_fd = fd;
- if (clt->clt_file != NULL)
- bufferevent_free(clt->clt_file);
+ if (clt->clt_srvbev != NULL)
+ bufferevent_free(clt->clt_srvbev);
- clt->clt_file = bufferevent_new(clt->clt_fd, server_read,
+ clt->clt_srvbev = bufferevent_new(clt->clt_fd, server_read,
server_write, server_file_error, clt);
- if (clt->clt_file == NULL) {
+ if (clt->clt_srvbev == NULL) {
errstr = "failed to allocate file buffer event";
goto fail;
}
- bufferevent_settimeout(clt->clt_file,
+ /* Adjust read watermark to the socket output buffer size */
+ bufferevent_setwatermark(clt->clt_srvbev, EV_READ, 0,
+ clt->clt_sndbufsiz);
+
+ bufferevent_settimeout(clt->clt_srvbev,
srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec);
- bufferevent_enable(clt->clt_file, EV_READ);
+ bufferevent_enable(clt->clt_srvbev, EV_READ);
bufferevent_disable(clt->clt_bev, EV_READ);
+ done:
+ server_reset_http(clt);
return (0);
fail:
+ bufferevent_disable(clt->clt_bev, EV_READ|EV_WRITE);
+ bufferevent_free(clt->clt_bev);
+ clt->clt_bev = NULL;
+ abort:
if (errstr == NULL)
errstr = strerror(errno);
- server_abort_http(clt, 500, errstr);
+ server_abort_http(clt, code, errstr);
return (-1);
}
@@ -199,38 +270,47 @@ int
server_file_index(struct httpd *env, struct client *clt)
{
char path[MAXPATHLEN];
+ char tmstr[21];
struct http_descriptor *desc = clt->clt_desc;
struct server_config *srv_conf = clt->clt_srv_conf;
struct dirent **namelist, *dp;
- int namesize, i, ret, fd = -1, width;
+ int namesize, i, ret, fd = -1, namewidth, skip;
+ int code = 500;
struct evbuffer *evb = NULL;
struct media_type *media;
const char *style;
struct stat st;
+ struct tm tm;
+ time_t t;
+
+ if ((ret = server_file_method(clt)) != 0) {
+ code = ret;
+ goto abort;
+ }
/* Request path is already canonicalized */
if ((size_t)snprintf(path, sizeof(path), "%s%s",
- srv_conf->docroot, desc->http_path) >= sizeof(path))
- goto fail;
+ srv_conf->root, desc->http_path) >= sizeof(path))
+ goto abort;
/* Now open the file, should be readable or we have another problem */
if ((fd = open(path, O_RDONLY)) == -1)
- goto fail;
-
- /* File descriptor is opened, decrement inflight counter */
- server_inflight_dec(clt, __func__);
+ goto abort;
if ((evb = evbuffer_new()) == NULL)
- goto fail;
+ goto abort;
if ((namesize = scandir(path, &namelist, NULL, alphasort)) == -1)
- goto fail;
+ goto abort;
+
+ /* Indicate failure but continue going through the list */
+ skip = 0;
/* A CSS stylesheet allows minimal customization by the user */
style = "body { background-color: white; color: black; font-family: "
"sans-serif; }";
/* Generate simple HTML index document */
- evbuffer_add_printf(evb,
+ if (evbuffer_add_printf(evb,
"<!DOCTYPE HTML PUBLIC "
"\"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
"<html>\n"
@@ -241,35 +321,51 @@ server_file_index(struct httpd *env, struct client *clt)
"<body>\n"
"<h1>Index of %s</h1>\n"
"<hr>\n<pre>\n",
- desc->http_path, style, desc->http_path);
+ desc->http_path, style, desc->http_path) == -1)
+ skip = 1;
for (i = 0; i < namesize; i++) {
dp = namelist[i];
- if (dp->d_name[0] == '.' && dp->d_name[1] == '\0') {
- /* ignore */
- } else if (dp->d_type == DT_DIR) {
- evbuffer_add_printf(evb,
- "<a href=\"%s/\">%s/</a>\n",
- dp->d_name, dp->d_name);
- } else if (dp->d_type == DT_REG) {
- if (fstatat(fd, dp->d_name, &st, 0) == -1) {
- free(dp);
- continue;
- }
- width = 50 - strlen(dp->d_name);
- evbuffer_add_printf(evb,
- "<a href=\"%s\">%s</a>%*s%llu bytes\n",
+
+ if (skip ||
+ fstatat(fd, dp->d_name, &st, 0) == -1) {
+ free(dp);
+ continue;
+ }
+
+ t = st.st_mtime;
+ localtime_r(&t, &tm);
+ strftime(tmstr, sizeof(tmstr), "%d-%h-%Y %R", &tm);
+ namewidth = 51 - strlen(dp->d_name);
+
+ if (dp->d_name[0] == '.' &&
+ !(dp->d_name[1] == '.' && dp->d_name[2] == '\0')) {
+ /* ignore hidden files starting with a dot */
+ } else if (S_ISDIR(st.st_mode)) {
+ namewidth -= 1; /* trailing slash */
+ if (evbuffer_add_printf(evb,
+ "<a href=\"%s\">%s/</a>%*s%s%20s\n",
+ dp->d_name, dp->d_name,
+ MAX(namewidth, 0), " ", tmstr, "-") == -1)
+ skip = 1;
+ } else if (S_ISREG(st.st_mode)) {
+ if (evbuffer_add_printf(evb,
+ "<a href=\"%s\">%s</a>%*s%s%20llu\n",
dp->d_name, dp->d_name,
- width < 0 ? 50 : width, "", st.st_size);
+ MAX(namewidth, 0), " ", tmstr, st.st_size) == -1)
+ skip = 1;
}
free(dp);
}
free(namelist);
- evbuffer_add_printf(evb,
- "</pre>\n<hr>\n</body>\n</html>\n");
+ if (skip ||
+ evbuffer_add_printf(evb,
+ "</pre>\n<hr>\n</body>\n</html>\n") == -1)
+ goto abort;
close(fd);
+ fd = -1;
media = media_find(env->sc_mediatypes, "index.html");
ret = server_response_http(clt, 200, media, EVBUFFER_LENGTH(evb));
@@ -279,7 +375,7 @@ server_file_index(struct httpd *env, struct client *clt)
case 0:
/* Connection is already finished */
evbuffer_free(evb);
- return (0);
+ goto done;
default:
break;
}
@@ -287,6 +383,7 @@ server_file_index(struct httpd *env, struct client *clt)
if (server_bufferevent_write_buffer(clt, evb) == -1)
goto fail;
evbuffer_free(evb);
+ evb = NULL;
bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
if (clt->clt_persist)
@@ -295,14 +392,19 @@ server_file_index(struct httpd *env, struct client *clt)
clt->clt_toread = TOREAD_HTTP_NONE;
clt->clt_done = 0;
+ done:
+ server_reset_http(clt);
return (0);
-
fail:
+ bufferevent_disable(clt->clt_bev, EV_READ|EV_WRITE);
+ bufferevent_free(clt->clt_bev);
+ clt->clt_bev = NULL;
+ abort:
if (fd != -1)
close(fd);
if (evb != NULL)
evbuffer_free(evb);
- server_abort_http(clt, 500, desc->http_path);
+ server_abort_http(clt, code, desc->http_path);
return (-1);
}
@@ -321,13 +423,6 @@ server_file_error(struct bufferevent *bev, short error, void *arg)
clt->clt_done = 1;
- dst = EVBUFFER_OUTPUT(clt->clt_bev);
- if (EVBUFFER_LENGTH(dst)) {
- /* Finish writing all data first */
- bufferevent_enable(clt->clt_bev, EV_WRITE);
- return;
- }
-
if (clt->clt_persist) {
/* Close input file and wait for next HTTP request */
if (clt->clt_fd != -1)
@@ -338,6 +433,14 @@ server_file_error(struct bufferevent *bev, short error, void *arg)
bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
return;
}
+
+ dst = EVBUFFER_OUTPUT(clt->clt_bev);
+ if (EVBUFFER_LENGTH(dst)) {
+ /* Finish writing all data first */
+ bufferevent_enable(clt->clt_bev, EV_WRITE);
+ return;
+ }
+
server_close(clt, "done");
return;
}
diff --git a/server_http.c b/server_http.c
index bfc5272..9e09428 100644
--- a/server_http.c
+++ b/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.44 2014/08/08 18:29:42 reyk Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -40,15 +40,13 @@
#include <stdio.h>
#include <err.h>
#include <pwd.h>
+#include <syslog.h>
#include <event.h>
#include <fnmatch.h>
-#include <openssl/ssl.h>
-
#include "httpd.h"
#include "http.h"
-static void server_http_date(char *, size_t);
static int server_httpmethod_cmp(const void *, const void *);
static int server_httperror_cmp(const void *, const void *);
void server_httpdesc_free(struct http_descriptor *);
@@ -78,7 +76,7 @@ server_http(struct httpd *x_env)
void
server_http_init(struct server *srv)
{
- /* nothing */
+ /* nothing */
}
int
@@ -102,6 +100,10 @@ server_httpdesc_free(struct http_descriptor *desc)
free(desc->http_path);
desc->http_path = NULL;
}
+ if (desc->http_path_alias != NULL) {
+ free(desc->http_path_alias);
+ desc->http_path_alias = NULL;
+ }
if (desc->http_query != NULL) {
free(desc->http_query);
desc->http_query = NULL;
@@ -110,6 +112,10 @@ server_httpdesc_free(struct http_descriptor *desc)
free(desc->http_version);
desc->http_version = NULL;
}
+ if (desc->http_host != NULL) {
+ free(desc->http_host);
+ desc->http_host = NULL;
+ }
kv_purge(&desc->http_headers);
desc->http_lastheader = NULL;
}
@@ -118,6 +124,7 @@ void
server_read_http(struct bufferevent *bev, void *arg)
{
struct client *clt = arg;
+ struct server_config *srv_conf = clt->clt_srv_conf;
struct http_descriptor *desc = clt->clt_desc;
struct evbuffer *src = EVBUFFER_INPUT(bev);
char *line = NULL, *key, *value;
@@ -259,6 +266,11 @@ server_read_http(struct bufferevent *bev, void *arg)
server_abort_http(clt, 500, errstr);
goto abort;
}
+ if ((size_t)clt->clt_toread >
+ srv_conf->maxrequestbody) {
+ server_abort_http(clt, 413, NULL);
+ goto abort;
+ }
}
if (strcasecmp("Transfer-Encoding", key) == 0 &&
@@ -320,10 +332,10 @@ server_read_http(struct bufferevent *bev, void *arg)
}
done:
- if (clt->clt_toread <= 0) {
- server_response(env, clt);
- return;
- }
+ if (clt->clt_toread != 0)
+ bufferevent_disable(bev, EV_READ);
+ server_response(env, clt);
+ return;
}
if (clt->clt_done) {
server_close(clt, "done");
@@ -359,12 +371,11 @@ server_read_httpcontent(struct bufferevent *bev, void *arg)
/* Read content data */
if ((off_t)size > clt->clt_toread) {
size = clt->clt_toread;
- if (server_bufferevent_write_chunk(clt,
- src, size) == -1)
+ if (fcgi_add_stdin(clt, src) == -1)
goto fail;
clt->clt_toread = 0;
} else {
- if (server_bufferevent_write_buffer(clt, src) == -1)
+ if (fcgi_add_stdin(clt, src) == -1)
goto fail;
clt->clt_toread -= size;
}
@@ -372,17 +383,19 @@ server_read_httpcontent(struct bufferevent *bev, void *arg)
size, clt->clt_toread);
}
if (clt->clt_toread == 0) {
+ fcgi_add_stdin(clt, NULL);
clt->clt_toread = TOREAD_HTTP_HEADER;
+ bufferevent_disable(bev, EV_READ);
bev->readcb = server_read_http;
+ return;
}
if (clt->clt_done)
goto done;
if (bev->readcb != server_read_httpcontent)
bev->readcb(bev, arg);
- bufferevent_enable(bev, EV_READ);
+
return;
done:
- server_close(clt, "last http content read");
return;
fail:
server_close(clt, strerror(errno));
@@ -514,11 +527,14 @@ server_reset_http(struct client *clt)
clt->clt_headerlen = 0;
clt->clt_line = 0;
clt->clt_done = 0;
+ clt->clt_chunk = 0;
clt->clt_bev->readcb = server_read_http;
clt->clt_srv_conf = &srv->srv_conf;
+
+ server_log(clt, NULL);
}
-static void
+void
server_http_date(char *tmbuf, size_t len)
{
time_t t;
@@ -565,7 +581,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;
@@ -578,6 +595,9 @@ server_abort_http(struct client *clt, u_int code, const char *msg)
if (bev == NULL)
goto done;
+ if (server_log_http(clt, code, 0) == -1)
+ goto done;
+
/* Some system information */
if (print_host(&srv_conf->ss, hbuf, sizeof(hbuf)) == NULL)
goto done;
@@ -603,14 +623,7 @@ server_abort_http(struct client *clt, u_int code, const char *msg)
/* A CSS stylesheet allows minimal customization by the user */
style = "body { background-color: white; color: black; font-family: "
- "'Comic Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif; }"
- "blink { animation:blink 1s; animation-iteration-count: infinite;"
- "-webkit-animation:blink 1s;"
- "-webkit-animation-iteration-count: infinite;}"
- "@keyframes blink { 0%{opacity:0.0;} 50%{opacity:0.0;}"
- "50.01%{opacity:1.0;} 100%{opacity:1.0;} }"
- "@-webkit-keyframes blink { 0%{opacity:0.0;} 50%{opacity:0.0;}"
- "50.01%{opacity:1.0;} 100%{opacity:1.0;} }";
+ "'Comic Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif; }";
/* Generate simple HTTP+HTML error document */
if (asprintf(&httpmsg,
"HTTP/1.0 %03d %s\r\n"
@@ -628,7 +641,7 @@ server_abort_http(struct client *clt, u_int code, const char *msg)
"<style type=\"text/css\"><!--\n%s\n--></style>\n"
"</head>\n"
"<body>\n"
- "<h1><blink>%s</blink></h1>\n"
+ "<h1>%s</h1>\n"
"<div id='m'>%s</div>\n"
"<hr><address>%s at %s port %d</address>\n"
"</body>\n"
@@ -645,9 +658,9 @@ server_abort_http(struct client *clt, u_int code, const char *msg)
done:
free(extraheader);
- if (asprintf(&httpmsg, "%s (%03d %s)", msg, code, httperr) == -1)
+ if (asprintf(&httpmsg, "%s (%03d %s)", msg, code, httperr) == -1) {
server_close(clt, msg);
- else {
+ } else {
server_close(clt, httpmsg);
free(httpmsg);
}
@@ -668,11 +681,11 @@ int
server_response(struct httpd *httpd, struct client *clt)
{
char path[MAXPATHLEN];
+ char hostname[MAXHOSTNAMELEN];
struct http_descriptor *desc = clt->clt_desc;
struct server *srv = clt->clt_srv;
struct server_config *srv_conf = &srv->srv_conf;
struct kv *kv, key, *host;
- int ret;
/* Canonicalize the request path */
if (desc->http_path == NULL ||
@@ -709,6 +722,9 @@ server_response(struct httpd *httpd, struct client *clt)
clt->clt_persist = 0;
}
+ if (clt->clt_persist >= srv_conf->maxrequests)
+ clt->clt_persist = 0;
+
/*
* Do we have a Host header and matching configuration?
* XXX the Host can also appear in the URL path.
@@ -716,7 +732,8 @@ 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,
+ if ((srv_conf->flags & SRVFLAG_LOCATION) == 0 &&
+ fnmatch(srv_conf->name, host->kv_value,
FNM_CASEFOLD) == 0) {
/* Replace host configuration */
clt->clt_srv_conf = srv_conf;
@@ -728,27 +745,49 @@ server_response(struct httpd *httpd, struct client *clt)
if (srv_conf != NULL) {
/* Use the actual server IP address */
- if (server_http_host(&clt->clt_srv_ss, desc->http_host,
- sizeof(desc->http_host)) == NULL)
+ if (server_http_host(&clt->clt_srv_ss, hostname,
+ sizeof(hostname)) == NULL)
goto fail;
} else {
/* Host header was valid and found */
- if (strlcpy(desc->http_host, host->kv_value,
- sizeof(desc->http_host)) >= sizeof(desc->http_host))
+ if (strlcpy(hostname, host->kv_value, sizeof(hostname)) >=
+ sizeof(hostname))
goto fail;
+ srv_conf = clt->clt_srv_conf;
}
- if ((ret = server_file(httpd, clt)) == -1)
- return (-1);
+ if ((desc->http_host = strdup(hostname)) == NULL)
+ goto fail;
- server_reset_http(clt);
+ /* Now search for the location */
+ srv_conf = server_getlocation(clt, desc->http_path);
- return (0);
+ return (server_file(httpd, clt));
fail:
server_abort_http(clt, 400, "bad request");
return (-1);
}
+struct server_config *
+server_getlocation(struct client *clt, const char *path)
+{
+ struct server *srv = clt->clt_srv;
+ struct server_config *srv_conf = clt->clt_srv_conf, *location;
+
+ /* Now search for the location */
+ TAILQ_FOREACH(location, &srv->srv_hosts, entry) {
+ if ((location->flags & SRVFLAG_LOCATION) &&
+ location->id == srv_conf->id &&
+ fnmatch(location->location, path, FNM_CASEFOLD) == 0) {
+ /* Replace host configuration */
+ clt->clt_srv_conf = srv_conf = location;
+ break;
+ }
+ }
+
+ return (srv_conf);
+}
+
int
server_response_http(struct client *clt, u_int code,
struct media_type *media, size_t size)
@@ -761,6 +800,9 @@ server_response_http(struct client *clt, u_int code,
if (desc == NULL || (error = server_httperror_byid(code)) == NULL)
return (-1);
+ if (server_log_http(clt, code, size) == -1)
+ return (-1);
+
kv_purge(&desc->http_headers);
/* Add error codes */
@@ -788,9 +830,9 @@ server_response_http(struct client *clt, u_int code,
return (-1);
/* Set content length, if specified */
- if (size && ((cl =
+ if ((cl =
kv_add(&desc->http_headers, "Content-Length", NULL)) == NULL ||
- kv_set(cl, "%ld", size) == -1))
+ kv_set(cl, "%ld", size) == -1)
return (-1);
/* Date header is mandatory and should be added last */
@@ -801,11 +843,11 @@ server_response_http(struct client *clt, u_int code,
/* Write completed header */
if (server_writeresponse_http(clt) == -1 ||
server_bufferevent_print(clt, "\r\n") == -1 ||
- server_writeheader_http(clt) == -1 ||
+ server_headers(clt, server_writeheader_http, NULL) == -1 ||
server_bufferevent_print(clt, "\r\n") == -1)
return (-1);
- if (desc->http_method == HTTP_METHOD_HEAD) {
+ if (size == 0 || desc->http_method == HTTP_METHOD_HEAD) {
bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
if (clt->clt_persist)
clt->clt_toread = TOREAD_HTTP_HEADER;
@@ -837,7 +879,7 @@ server_writeresponse_http(struct client *clt)
}
int
-server_writeheader_kv(struct client *clt, struct kv *hdr)
+server_writeheader_http(struct client *clt, struct kv *hdr, void *arg)
{
char *ptr;
const char *key;
@@ -865,16 +907,17 @@ server_writeheader_kv(struct client *clt, struct kv *hdr)
}
int
-server_writeheader_http(struct client *clt)
+server_headers(struct client *clt,
+ int (*hdr_cb)(struct client *, struct kv *, void *), void *arg)
{
struct kv *hdr, *kv;
struct http_descriptor *desc = (struct http_descriptor *)clt->clt_desc;
RB_FOREACH(hdr, kvtree, &desc->http_headers) {
- if (server_writeheader_kv(clt, hdr) == -1)
+ if ((hdr_cb)(clt, hdr, arg) == -1)
return (-1);
TAILQ_FOREACH(kv, &hdr->kv_children, kv_entry) {
- if (server_writeheader_kv(clt, kv) == -1)
+ if ((hdr_cb)(clt, kv, arg) == -1)
return (-1);
}
}
@@ -950,3 +993,90 @@ server_httperror_cmp(const void *a, const void *b)
const struct http_error *eb = b;
return (ea->error_code - eb->error_code);
}
+
+int
+server_log_http(struct client *clt, u_int code, size_t len)
+{
+ static char tstamp[64];
+ static char ip[INET6_ADDRSTRLEN];
+ time_t t;
+ struct kv key, *agent, *referrer;
+ struct tm *tm;
+ struct server_config *srv_conf;
+ struct http_descriptor *desc;
+
+ if ((srv_conf = clt->clt_srv_conf) == NULL)
+ return (-1);
+ if ((srv_conf->flags & SRVFLAG_LOG) == 0)
+ return (0);
+ if ((desc = clt->clt_desc) == NULL)
+ return (-1);
+
+ if ((t = time(NULL)) == -1)
+ return (-1);
+ if ((tm = localtime(&t)) == NULL)
+ return (-1);
+ if (strftime(tstamp, sizeof(tstamp), "%d/%b/%Y:%H:%M:%S %z", tm) == 0)
+ return (-1);
+
+ if (print_host(&clt->clt_ss, ip, sizeof(ip)) == NULL)
+ return (-1);
+
+ /*
+ * For details on common log format, see:
+ * https://httpd.apache.org/docs/current/mod/mod_log_config.html
+ *
+ * httpd's format is similar to these Apache LogFormats:
+ * "%v %h %l %u %t \"%r\" %>s %B"
+ * "%v %h %l %u %t \"%r\" %>s %B \"%{Referer}i\" \"%{User-agent}i\""
+ */
+ switch (srv_conf->logformat) {
+ case LOG_FORMAT_COMMON:
+ if (evbuffer_add_printf(clt->clt_log,
+ "%s %s - - [%s] \"%s %s%s%s%s%s\" %03d %zu\n",
+ srv_conf->name, ip, tstamp,
+ server_httpmethod_byid(desc->http_method),
+ desc->http_path == NULL ? "" : desc->http_path,
+ desc->http_query == NULL ? "" : "?",
+ desc->http_query == NULL ? "" : desc->http_query,
+ desc->http_version == NULL ? "" : " ",
+ desc->http_version == NULL ? "" : desc->http_version,
+ code, len) == -1)
+ return (-1);
+ break;
+
+ case LOG_FORMAT_COMBINED:
+ key.kv_key = "Referer"; /* sic */
+ if ((referrer = kv_find(&desc->http_headers, &key)) != NULL &&
+ referrer->kv_value == NULL)
+ referrer = NULL;
+
+ key.kv_key = "User-Agent";
+ if ((agent = kv_find(&desc->http_headers, &key)) != NULL &&
+ agent->kv_value == NULL)
+ agent = NULL;
+
+ if (evbuffer_add_printf(clt->clt_log,
+ "%s %s - - [%s] \"%s %s%s%s%s%s\" %03d %zu \"%s\" \"%s\"\n",
+ srv_conf->name, ip, tstamp,
+ server_httpmethod_byid(desc->http_method),
+ desc->http_path == NULL ? "" : desc->http_path,
+ desc->http_query == NULL ? "" : "?",
+ desc->http_query == NULL ? "" : desc->http_query,
+ desc->http_version == NULL ? "" : " ",
+ desc->http_version == NULL ? "" : desc->http_version,
+ code, len,
+ referrer == NULL ? "" : referrer->kv_value,
+ agent == NULL ? "" : agent->kv_value) == -1)
+ return (-1);
+ break;
+
+ case LOG_FORMAT_CONNECTION:
+ if (evbuffer_add_printf(clt->clt_log, " [%s]",
+ desc->http_path == NULL ? "" : desc->http_path) == -1)
+ return (-1);
+ break;
+ }
+
+ return (0);
+}