aboutsummaryrefslogtreecommitdiff
path: root/server_file.c
diff options
context:
space:
mode:
Diffstat (limited to 'server_file.c')
-rw-r--r--server_file.c285
1 files changed, 194 insertions, 91 deletions
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;
}