aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReyk Floeter <reyk@esdenera.com>2014-07-27 00:28:45 +0200
committerReyk Floeter <reyk@esdenera.com>2014-07-27 00:28:45 +0200
commitb9ef26a6926417277e59cfd3c2b58df0596bab3a (patch)
tree2d0134b2b509735d8b5ac9073263b1c327055ce8
parente30ef12693dc3dcdf72573424c839afb10769b70 (diff)
downloadhttpd-b9ef26a6926417277e59cfd3c2b58df0596bab3a.tar.gz
httpd-b9ef26a6926417277e59cfd3c2b58df0596bab3a.zip
Add directory index
-rw-r--r--config.c4
-rw-r--r--http.h10
-rw-r--r--httpd.c4
-rw-r--r--httpd.h7
-rw-r--r--server.c30
-rw-r--r--server_file.c141
-rw-r--r--server_http.c94
7 files changed, 251 insertions, 39 deletions
diff --git a/config.c b/config.c
index eb9e003..762a7a3 100644
--- a/config.c
+++ b/config.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: config.c,v 1.4 2014/07/25 16:23:19 reyk Exp $ */
+/* $OpenBSD: config.c,v 1.5 2014/07/25 23:30:58 reyk Exp $ */
/*
* Copyright (c) 2011 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -249,7 +249,7 @@ config_getserver(struct httpd *env, struct imsg *imsg)
/* Check if server with matching listening socket already exists */
if ((srv = server_byaddr((struct sockaddr *)
- &srv_conf.ss)) != NULL) {
+ &srv_conf.ss, srv_conf.port)) != NULL) {
/* Add "host" to existing listening server */
close(imsg->fd);
return (config_getserver_config(env,
diff --git a/http.h b/http.h
index af2627f..994e98b 100644
--- a/http.h
+++ b/http.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: http.h,v 1.3 2014/07/13 15:11:23 reyk Exp $ */
+/* $OpenBSD: http.h,v 1.4 2014/07/25 23:23:39 reyk Exp $ */
/*
* Copyright (c) 2012 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -19,6 +19,9 @@
#ifndef _HTTP_H
#define _HTTP_H
+#define HTTP_PORT 80
+#define HTTPS_PORT 443
+
enum httpmethod {
HTTP_METHOD_NONE = 0,
@@ -148,13 +151,14 @@ struct http_descriptor {
#define query_key http_matchquery.kv_key
#define query_val http_matchquery.kv_value
- char *http_version;
+ char http_host[MAXHOSTNAMELEN];
enum httpmethod http_method;
int http_chunked;
+ char *http_version;
/* A tree of headers and attached lists for repeated headers. */
- struct kvtree http_headers;
struct kv *http_lastheader;
+ struct kvtree http_headers;
};
#endif /* _HTTP_H */
diff --git a/httpd.c b/httpd.c
index a1a8558..520ae5e 100644
--- a/httpd.c
+++ b/httpd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: httpd.c,v 1.8 2014/07/25 16:23:19 reyk Exp $ */
+/* $OpenBSD: httpd.c,v 1.10 2014/07/26 09:59:14 reyk Exp $ */
/*
* Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
@@ -654,7 +654,7 @@ prefixlen2mask6(u_int8_t prefixlen, u_int32_t *mask)
if (prefixlen > 128)
prefixlen = 128;
- bzero(&s6, sizeof(s6));
+ memset(&s6, 0, sizeof(s6));
for (i = 0; i < prefixlen / 8; i++)
s6.s6_addr[i] = 0xff;
i = prefixlen % 8;
diff --git a/httpd.h b/httpd.h
index f955fe6..df34b47 100644
--- a/httpd.h
+++ b/httpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: httpd.h,v 1.12 2014/07/25 16:23:19 reyk Exp $ */
+/* $OpenBSD: httpd.h,v 1.15 2014/07/25 23:30:58 reyk Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -256,6 +256,7 @@ struct client {
void *clt_srv;
void *clt_srv_conf;
u_int32_t clt_srv_id;
+ struct sockaddr_storage clt_srv_ss;
int clt_s;
in_port_t clt_port;
@@ -387,7 +388,7 @@ int server_bufferevent_add(struct event *, int);
int server_bufferevent_write(struct client *, void *, size_t);
void server_inflight_dec(struct client *, const char *);
struct server *
- server_byaddr(struct sockaddr *);
+ server_byaddr(struct sockaddr *, in_port_t);
SPLAY_PROTOTYPE(client_tree, client, clt_nodes, server_client_cmp);
@@ -412,6 +413,8 @@ int server_response_http(struct client *, u_int, struct media_type *,
void server_reset_http(struct client *);
void server_close_http(struct client *);
int server_response(struct httpd *, struct client *);
+const char *
+ server_http_host(struct sockaddr_storage *, char *, size_t);
/* server_file.c */
int server_file(struct httpd *, struct client *);
diff --git a/server.c b/server.c
index 21ccff2..3ef1401 100644
--- a/server.c
+++ b/server.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: server.c,v 1.11 2014/07/25 16:23:19 reyk Exp $ */
+/* $OpenBSD: server.c,v 1.13 2014/07/25 23:30:58 reyk Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -182,12 +182,13 @@ server_purge(struct server *srv)
}
struct server *
-server_byaddr(struct sockaddr *addr)
+server_byaddr(struct sockaddr *addr, in_port_t port)
{
struct server *srv;
TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
- if (sockaddr_cmp((struct sockaddr *)&srv->srv_conf.ss,
+ if (port == srv->srv_conf.port &&
+ sockaddr_cmp((struct sockaddr *)&srv->srv_conf.ss,
addr, srv->srv_conf.prefixlen) == 0)
return (srv);
}
@@ -503,6 +504,19 @@ server_accept(int fd, short event, void *arg)
clt->clt_srv_id = srv->srv_conf.id;
clt->clt_pid = getpid();
clt->clt_inflight = 1;
+
+ /* get local address */
+ slen = sizeof(clt->clt_srv_ss);
+ if (getsockname(s, (struct sockaddr *)&clt->clt_srv_ss,
+ &slen) == -1) {
+ server_close(clt, "listen address lookup failed");
+ return;
+ }
+
+ /* get client address */
+ memcpy(&clt->clt_ss, &ss, sizeof(clt->clt_ss));
+
+ /* get ports */
switch (ss.ss_family) {
case AF_INET:
clt->clt_port = ((struct sockaddr_in *)&ss)->sin_port;
@@ -511,7 +525,6 @@ server_accept(int fd, short event, void *arg)
clt->clt_port = ((struct sockaddr_in6 *)&ss)->sin6_port;
break;
}
- memcpy(&clt->clt_ss, &ss, sizeof(clt->clt_ss));
getmonotime(&clt->clt_tv_start);
memcpy(&clt->clt_tv_last, &clt->clt_tv_start, sizeof(clt->clt_tv_last));
@@ -571,7 +584,8 @@ server_inflight_dec(struct client *clt, const char *why)
void
server_close(struct client *clt, const char *msg)
{
- char ibuf[128], obuf[128], *ptr = NULL;
+ char ibuf[MAXHOSTNAMELEN], obuf[MAXHOSTNAMELEN];
+ char *ptr = NULL;
struct server *srv = clt->clt_srv;
struct server_config *srv_conf = clt->clt_srv_conf;
@@ -590,14 +604,14 @@ server_close(struct client *clt, const char *msg)
memset(&ibuf, 0, sizeof(ibuf));
memset(&obuf, 0, sizeof(obuf));
(void)print_host(&clt->clt_ss, ibuf, sizeof(ibuf));
- (void)print_host(&srv_conf->ss, obuf, sizeof(obuf));
+ (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)
ptr = evbuffer_readline(clt->clt_log);
log_info("server %s, "
- "client %d (%d active), %s -> %s:%d, "
+ "client %d (%d active), %s:%u -> %s, "
"%s%s%s", srv_conf->name, clt->clt_id, server_clients,
- ibuf, obuf, ntohs(clt->clt_port), msg,
+ ibuf, ntohs(clt->clt_port), obuf, msg,
ptr == NULL ? "" : ",", ptr == NULL ? "" : ptr);
if (ptr != NULL)
free(ptr);
diff --git a/server_file.c b/server_file.c
index a21c48c..e31dc82 100644
--- a/server_file.c
+++ b/server_file.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: server_file.c,v 1.14 2014/07/25 20:13:06 reyk Exp $ */
+/* $OpenBSD: server_file.c,v 1.16 2014/07/25 23:23:39 reyk Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -38,6 +38,7 @@
#include <string.h>
#include <unistd.h>
#include <stdio.h>
+#include <dirent.h>
#include <err.h>
#include <event.h>
@@ -54,7 +55,9 @@ int
server_file_access(struct http_descriptor *desc, char *path, size_t len,
struct stat *st)
{
- char *newpath;
+ struct stat stb;
+ char *newpath;
+
errno = 0;
if (access(path, R_OK) == -1) {
@@ -72,7 +75,8 @@ 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, "%s/", desc->http_path) == -1)
+ if (asprintf(&newpath, "http://%s%s/",
+ desc->http_host, desc->http_path) == -1)
return (500);
free(desc->http_path);
desc->http_path = newpath;
@@ -88,7 +92,15 @@ server_file_access(struct http_descriptor *desc, char *path, size_t len,
}
/* Check again but set len to 0 to avoid recursion */
- return (server_file_access(desc, path, 0, st));
+ if (server_file_access(desc, path, 0, &stb) == 404) {
+ /*
+ * Index file not found, return success but
+ * indicate directory with S_ISDIR.
+ */
+ } else {
+ /* return updated stat from index file */
+ memcpy(st, &stb, sizeof(*st));
+ }
} else if (!S_ISREG(st->st_mode)) {
/* Don't follow symlinks and ignore special files */
errno = EACCES;
@@ -110,19 +122,131 @@ server_file_access(struct http_descriptor *desc, char *path, size_t len,
/* NOTREACHED */
}
+static int
+server_file_index(struct httpd *env, struct client *clt)
+{
+ char path[MAXPATHLEN];
+ 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;
+ struct evbuffer *evb = NULL;
+ struct media_type *media;
+ const char *style;
+ struct stat st;
+
+ /* Request path is already canonicalized */
+ if ((size_t)snprintf(path, sizeof(path), "%s%s",
+ srv_conf->docroot, desc->http_path) >= sizeof(path))
+ goto fail;
+
+ /* 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__);
+
+ if ((evb = evbuffer_new()) == NULL)
+ goto fail;
+
+ if ((namesize = scandir(path, &namelist, NULL, alphasort)) == -1)
+ goto fail;
+
+ /* 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,
+ "<!DOCTYPE HTML PUBLIC "
+ "\"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
+ "<html>\n"
+ "<head>\n"
+ "<title>Index of %s</title>\n"
+ "<style type=\"text/css\"><!--\n%s\n--></style>\n"
+ "</head>\n"
+ "<body>\n"
+ "<h1>Index of %s</h1>\n"
+ "<hr>\n<pre>\n",
+ desc->http_path, style, desc->http_path);
+
+ 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",
+ dp->d_name, dp->d_name,
+ width < 0 ? 50 : width, "", st.st_size);
+ }
+ free(dp);
+ }
+ free(namelist);
+
+ evbuffer_add_printf(evb,
+ "</pre>\n<hr>\n</body>\n</html>\n");
+
+ close(fd);
+
+ media = media_find(env->sc_mediatypes, "index.html");
+ ret = server_response_http(clt, 200, media, EVBUFFER_LENGTH(evb));
+ switch (ret) {
+ case -1:
+ goto fail;
+ case 0:
+ /* Connection is already finished */
+ evbuffer_free(evb);
+ return (0);
+ default:
+ break;
+ }
+
+ if (server_bufferevent_write_buffer(clt, evb) == -1)
+ goto fail;
+ evbuffer_free(evb);
+
+ bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
+ if (clt->clt_persist)
+ clt->clt_toread = TOREAD_HTTP_HEADER;
+ else
+ clt->clt_toread = TOREAD_HTTP_NONE;
+ clt->clt_done = 0;
+
+ return (0);
+
+ fail:
+ if (fd != -1)
+ close(fd);
+ if (evb != NULL)
+ evbuffer_free(evb);
+ server_abort_http(clt, 500, desc->http_path);
+ return (-1);
+}
+
int
server_file(struct httpd *env, struct client *clt)
{
+ char path[MAXPATHLEN];
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;
/* Request path is already canonicalized */
- if ((size_t)snprintf(path, sizeof(path), "%s/%s",
+ 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);
@@ -135,6 +259,11 @@ server_file(struct httpd *env, struct client *clt)
return (-1);
}
+ if (S_ISDIR(st.st_mode)) {
+ /* list directory index */
+ return (server_file_index(env, clt));
+ }
+
/* Now open the file, should be readable or we have another problem */
if ((fd = open(path, O_RDONLY)) == -1)
goto fail;
diff --git a/server_http.c b/server_http.c
index 58080a4..bfc5272 100644
--- a/server_http.c
+++ b/server_http.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: server_http.c,v 1.14 2014/07/25 16:23:19 reyk Exp $ */
+/* $OpenBSD: server_http.c,v 1.19 2014/07/25 23:25:38 reyk Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -48,6 +48,7 @@
#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 *);
@@ -505,6 +506,7 @@ void
server_reset_http(struct client *clt)
{
struct http_descriptor *desc = clt->clt_desc;
+ struct server *srv = clt->clt_srv;
server_httpdesc_free(desc);
desc->http_method = 0;
@@ -513,6 +515,51 @@ server_reset_http(struct client *clt)
clt->clt_line = 0;
clt->clt_done = 0;
clt->clt_bev->readcb = server_read_http;
+ clt->clt_srv_conf = &srv->srv_conf;
+}
+
+static void
+server_http_date(char *tmbuf, size_t len)
+{
+ time_t t;
+ struct tm tm;
+
+ /* New HTTP/1.1 RFC 7231 prefers IMF-fixdate from RFC 5322 */
+ time(&t);
+ gmtime_r(&t, &tm);
+ strftime(tmbuf, len, "%a, %d %h %Y %T %Z", &tm);
+}
+
+const char *
+server_http_host(struct sockaddr_storage *ss, char *buf, size_t len)
+{
+ char hbuf[MAXHOSTNAMELEN];
+ in_port_t port;
+
+ if (print_host(ss, buf, len) == NULL)
+ return (NULL);
+
+ port = ntohs(server_socket_getport(ss));
+ if (port == HTTP_PORT)
+ return (buf);
+
+ switch (ss->ss_family) {
+ case AF_INET:
+ if ((size_t)snprintf(hbuf, sizeof(hbuf),
+ "%s:%u", buf, port) >= sizeof(hbuf))
+ return (NULL);
+ break;
+ case AF_INET6:
+ if ((size_t)snprintf(hbuf, sizeof(hbuf),
+ "[%s]:%u", buf, port) >= sizeof(hbuf))
+ return (NULL);
+ break;
+ }
+
+ if (strlcpy(buf, hbuf, len) >= len)
+ return (NULL);
+
+ return (buf);
}
void
@@ -522,8 +569,6 @@ server_abort_http(struct client *clt, u_int code, const char *msg)
struct bufferevent *bev = clt->clt_bev;
const char *httperr = NULL, *text = "";
char *httpmsg, *extraheader = NULL;
- time_t t;
- struct tm *lt;
char tmbuf[32], hbuf[128];
const char *style;
@@ -537,12 +582,7 @@ server_abort_http(struct client *clt, u_int code, const char *msg)
if (print_host(&srv_conf->ss, hbuf, sizeof(hbuf)) == NULL)
goto done;
- /* RFC 2616 "tolerates" asctime() */
- time(&t);
- lt = localtime(&t);
- tmbuf[0] = '\0';
- if (asctime_r(lt, tmbuf) != NULL)
- tmbuf[strlen(tmbuf) - 1] = '\0'; /* skip final '\n' */
+ server_http_date(tmbuf, sizeof(tmbuf));
/* Do not send details of the Internal Server Error */
switch (code) {
@@ -630,8 +670,8 @@ server_response(struct httpd *httpd, struct client *clt)
char path[MAXPATHLEN];
struct http_descriptor *desc = clt->clt_desc;
struct server *srv = clt->clt_srv;
- struct server_config *srv_conf;
- struct kv *kv, key;
+ struct server_config *srv_conf = &srv->srv_conf;
+ struct kv *kv, key, *host;
int ret;
/* Canonicalize the request path */
@@ -642,10 +682,14 @@ server_response(struct httpd *httpd, struct client *clt)
if ((desc->http_path = strdup(path)) == NULL)
goto fail;
+ key.kv_key = "Host";
+ if ((host = kv_find(&desc->http_headers, &key)) != NULL &&
+ host->kv_value == NULL)
+ host = NULL;
+
if (strcmp(desc->http_version, "HTTP/1.1") == 0) {
/* Host header is mandatory */
- key.kv_key = "Host";
- if ((kv = kv_find(&desc->http_headers, &key)) == NULL)
+ if (host == NULL)
goto fail;
/* Is the connection persistent? */
@@ -669,19 +713,31 @@ server_response(struct httpd *httpd, struct client *clt)
* Do we have a Host header and matching configuration?
* XXX the Host can also appear in the URL path.
*/
- key.kv_key = "Host";
- if ((kv = kv_find(&desc->http_headers, &key)) != NULL) {
+ 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, kv->kv_value,
+ if (fnmatch(srv_conf->name, host->kv_value,
FNM_CASEFOLD) == 0) {
/* Replace host configuration */
clt->clt_srv_conf = srv_conf;
+ srv_conf = NULL;
break;
}
}
}
+ 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)
+ 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))
+ goto fail;
+ }
+
if ((ret = server_file(httpd, clt)) == -1)
return (-1);
@@ -700,6 +756,7 @@ server_response_http(struct client *clt, u_int code,
struct http_descriptor *desc = clt->clt_desc;
const char *error;
struct kv *ct, *cl;
+ char tmbuf[32];
if (desc == NULL || (error = server_httperror_byid(code)) == NULL)
return (-1);
@@ -736,6 +793,11 @@ server_response_http(struct client *clt, u_int code,
kv_set(cl, "%ld", size) == -1))
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 completed header */
if (server_writeresponse_http(clt) == -1 ||
server_bufferevent_print(clt, "\r\n") == -1 ||