untrusted comment: signature from openbsd 5.6 base private key
RWR0EANmo9nqhn3Gnfk2/2x+xII6do92zreKp/t5zOwfkVgsQAI4ZCPkWAazbbnWNV7Ptkle876f/kb6C2KuvnTqvwUItsyvogA=
OpenBSD 5.6 errata 9, Nov 18, 2014: httpd was developed very rapidly
in the weeks before 5.6 release, and it has a few flaws. It would be
nice to get these flaws fully remediated before the next release, and
that requires the community to want to use it. Therefore here is a
"jumbo" patch that brings in the most important fixes.
Apply patch using:
signify -Vep /etc/signify/openbsd-56-base.pub -x 009_httpd.patch.sig \
-m - | (cd /usr/src && patch -p0)
Then build and install httpd:
cd /usr/src/usr.sbin/httpd
make obj
make
make install
Index: usr.sbin/httpd/config.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/config.c,v
retrieving revision 1.21
diff -u -p -r1.21 config.c
--- usr.sbin/httpd/config.c 6 Aug 2014 18:21:14 -0000 1.21
+++ usr.sbin/httpd/config.c 18 Nov 2014 15:02:54 -0000
@@ -223,7 +223,7 @@ config_getserver_config(struct httpd *en
#ifdef DEBUG
struct privsep *ps = env->sc_ps;
#endif
- struct server_config *srv_conf;
+ struct server_config *srv_conf, *parent;
u_int8_t *p = imsg->data;
u_int f;
@@ -233,18 +233,28 @@ config_getserver_config(struct httpd *en
IMSG_SIZE_CHECK(imsg, srv_conf);
memcpy(srv_conf, p, sizeof(*srv_conf));
+ /* Reset these variables to avoid free'ing invalid pointers */
+ serverconfig_reset(srv_conf);
+
+ TAILQ_FOREACH(parent, &srv->srv_hosts, entry) {
+ if (strcmp(parent->name, srv_conf->name) == 0)
+ break;
+ }
+ if (parent == NULL)
+ parent = &srv->srv_conf;
+
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,
+ srv_conf->flags |= parent->flags & f;
+ (void)strlcpy(srv_conf->index, parent->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;
+ srv_conf->flags |= parent->flags & f;
f = SRVFLAG_SOCKET|SRVFLAG_FCGI;
if ((srv_conf->flags & f) == SRVFLAG_FCGI) {
@@ -255,48 +265,48 @@ config_getserver_config(struct httpd *en
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,
+ srv_conf->flags |= parent->flags & f;
+ (void)strlcpy(srv_conf->root, parent->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;
+ srv_conf->flags |= parent->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;
+ srv_conf->flags |= parent->flags & f;
+ srv_conf->logformat = parent->logformat;
}
f = SRVFLAG_SYSLOG|SRVFLAG_NO_SYSLOG;
if ((srv_conf->flags & f) == 0)
- srv_conf->flags |= srv->srv_conf.flags & f;
+ srv_conf->flags |= parent->flags & f;
f = SRVFLAG_SSL;
- srv_conf->flags |= srv->srv_conf.flags & f;
+ srv_conf->flags |= parent->flags & f;
f = SRVFLAG_ACCESS_LOG;
if ((srv_conf->flags & f) == 0) {
- srv_conf->flags |= srv->srv_conf.flags & f;
+ srv_conf->flags |= parent->flags & f;
(void)strlcpy(srv_conf->accesslog,
- srv->srv_conf.accesslog,
+ parent->accesslog,
sizeof(srv_conf->accesslog));
}
f = SRVFLAG_ERROR_LOG;
if ((srv_conf->flags & f) == 0) {
- srv_conf->flags |= srv->srv_conf.flags & f;
+ srv_conf->flags |= parent->flags & f;
(void)strlcpy(srv_conf->errorlog,
- srv->srv_conf.errorlog,
+ parent->errorlog,
sizeof(srv_conf->errorlog));
}
- memcpy(&srv_conf->timeout, &srv->srv_conf.timeout,
+ memcpy(&srv_conf->timeout, &parent->timeout,
sizeof(srv_conf->timeout));
- srv_conf->maxrequests = srv->srv_conf.maxrequests;
- srv_conf->maxrequestbody = srv->srv_conf.maxrequestbody;
+ srv_conf->maxrequests = parent->maxrequests;
+ srv_conf->maxrequestbody = parent->maxrequestbody;
DPRINTF("%s: %s %d location \"%s\", "
"parent \"%s\", flags: %s",
@@ -330,6 +340,9 @@ config_getserver(struct httpd *env, stru
IMSG_SIZE_CHECK(imsg, &srv_conf);
memcpy(&srv_conf, p, sizeof(srv_conf));
s = sizeof(srv_conf);
+
+ /* Reset these variables to avoid free'ing invalid pointers */
+ serverconfig_reset(&srv_conf);
if ((u_int)(IMSG_DATA_SIZE(imsg) - s) <
(srv_conf.ssl_cert_len + srv_conf.ssl_key_len)) {
Index: usr.sbin/httpd/http.h
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/http.h,v
retrieving revision 1.5
diff -u -p -r1.5 http.h
--- usr.sbin/httpd/http.h 3 Aug 2014 21:33:27 -0000 1.5
+++ usr.sbin/httpd/http.h 18 Nov 2014 15:02:54 -0000
@@ -44,6 +44,32 @@ enum httpmethod {
HTTP_METHOD_LOCK,
HTTP_METHOD_UNLOCK,
+ /* WebDAV Versioning Extension, RFC 3253 */
+ HTTP_METHOD_VERSION_CONTROL,
+ HTTP_METHOD_REPORT,
+ HTTP_METHOD_CHECKOUT,
+ HTTP_METHOD_CHECKIN,
+ HTTP_METHOD_UNCHECKOUT,
+ HTTP_METHOD_MKWORKSPACE,
+ HTTP_METHOD_UPDATE,
+ HTTP_METHOD_LABEL,
+ HTTP_METHOD_MERGE,
+ HTTP_METHOD_BASELINE_CONTROL,
+ HTTP_METHOD_MKACTIVITY,
+
+ /* WebDAV Ordered Collections, RFC 3648 */
+ HTTP_METHOD_ORDERPATCH,
+
+ /* WebDAV Access Control, RFC 3744 */
+ HTTP_METHOD_ACL,
+
+ /* WebDAV Redirect Reference Resources, RFC 4437 */
+ HTTP_METHOD_MKREDIRECTREF,
+ HTTP_METHOD_UPDATEREDIRECTREF,
+
+ /* WebDAV Search, RFC 5323 */
+ HTTP_METHOD_SEARCH,
+
/* PATCH, RFC 5789 */
HTTP_METHOD_PATCH,
@@ -71,6 +97,22 @@ struct http_method {
{ HTTP_METHOD_MOVE, "MOVE" }, \
{ HTTP_METHOD_LOCK, "LOCK" }, \
{ HTTP_METHOD_UNLOCK, "UNLOCK" }, \
+ { HTTP_METHOD_VERSION_CONTROL, "VERSION-CONTROL" }, \
+ { HTTP_METHOD_REPORT, "REPORT" }, \
+ { HTTP_METHOD_CHECKOUT, "CHECKOUT" }, \
+ { HTTP_METHOD_CHECKIN, "CHECKIN" }, \
+ { HTTP_METHOD_UNCHECKOUT, "UNCHECKOUT" }, \
+ { HTTP_METHOD_MKWORKSPACE, "MKWORKSPACE" }, \
+ { HTTP_METHOD_UPDATE, "UPDATE" }, \
+ { HTTP_METHOD_LABEL, "LABEL" }, \
+ { HTTP_METHOD_MERGE, "MERGE" }, \
+ { HTTP_METHOD_BASELINE_CONTROL, "BASELINE-CONTROL" }, \
+ { HTTP_METHOD_MKACTIVITY, "MKACTIVITY" }, \
+ { HTTP_METHOD_ORDERPATCH, "ORDERPATCH" }, \
+ { HTTP_METHOD_ACL, "ACL" }, \
+ { HTTP_METHOD_MKREDIRECTREF, "MKREDIRECTREF" }, \
+ { HTTP_METHOD_UPDATEREDIRECTREF, "UPDATEREDIRECTREF" }, \
+ { HTTP_METHOD_SEARCH, "SEARCH" }, \
{ HTTP_METHOD_PATCH, "PATCH" }, \
{ HTTP_METHOD_NONE, NULL } \
}
@@ -155,6 +197,9 @@ struct http_descriptor {
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;
Index: usr.sbin/httpd/httpd.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.c,v
retrieving revision 1.17
diff -u -p -r1.17 httpd.c
--- usr.sbin/httpd/httpd.c 5 Aug 2014 15:36:59 -0000 1.17
+++ usr.sbin/httpd/httpd.c 18 Nov 2014 15:02:54 -0000
@@ -289,10 +289,20 @@ parent_configure(struct httpd *env)
fatal("send media");
}
+ /* First send the servers... */
TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
+ if (srv->srv_conf.flags & SRVFLAG_LOCATION)
+ continue;
if (config_setserver(env, srv) == -1)
fatal("send server");
}
+ /* ...and now send the locations */
+ TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
+ if ((srv->srv_conf.flags & SRVFLAG_LOCATION) == 0)
+ continue;
+ if (config_setserver(env, srv) == -1)
+ fatal("send location");
+ }
/* The servers need to reload their config. */
env->sc_reload = env->sc_prefork_server + 1;
@@ -526,6 +536,46 @@ canonicalize_host(const char *host, char
}
const char *
+url_decode(char *url)
+{
+ char *p, *q;
+ char hex[3];
+ u_long x;
+
+ hex[2] = '\0';
+ p = q = url;
+
+ while (*p != '\0') {
+ switch (*p) {
+ case '%':
+ /* Encoding character is followed by two hex chars */
+ if (!(isxdigit(p[1]) && isxdigit(p[2])))
+ return (NULL);
+
+ hex[0] = p[1];
+ hex[1] = p[2];
+
+ /*
+ * We don't have to validate "hex" because it is
+ * guaranteed to include two hex chars followed by nul.
+ */
+ x = strtoul(hex, NULL, 16);
+ *q = (char)x;
+ p += 2;
+ break;
+ default:
+ *q = *p;
+ break;
+ }
+ p++;
+ q++;
+ }
+ *q = '\0';
+
+ return(url);
+}
+
+const char *
canonicalize_path(const char *input, char *path, size_t len)
{
const char *i;
@@ -580,28 +630,33 @@ canonicalize_path(const char *input, cha
return (path);
}
-ssize_t
-path_info(char *name)
+size_t
+path_info(char *path)
{
- char *p, *start, *end;
- char path[MAXPATHLEN];
+ char *p, *start, *end, ch;
struct stat st;
-
- if (strlcpy(path, name, sizeof(path)) >= sizeof(path))
- return (-1);
+ int ret;
start = path;
end = start + strlen(path);
for (p = end; p > start; p--) {
- if (*p != '/')
+ /* Scan every path component from the end and at each '/' */
+ if (p < end && *p != '/')
continue;
- if (stat(path, &st) == 0)
- break;
+
+ /* 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 (strlen(path));
+ return (p - start);
}
void
@@ -623,6 +678,40 @@ socket_rlimit(int maxfd)
rl.rlim_cur = MAX(rl.rlim_max, (rlim_t)maxfd);
if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
fatal("socket_rlimit: failed to set resource limit");
+}
+
+char *
+evbuffer_getline(struct evbuffer *evb)
+{
+ u_int8_t *ptr = EVBUFFER_DATA(evb);
+ size_t len = EVBUFFER_LENGTH(evb);
+ char *str;
+ u_int i;
+
+ /* Safe version of evbuffer_readline() */
+ if ((str = get_string(ptr, len)) == NULL)
+ return (NULL);
+
+ for (i = 0; str[i] != '\0'; i++) {
+ if (str[i] == '\r' || str[i] == '\n')
+ break;
+ }
+
+ if (i == len) {
+ free(str);
+ return (NULL);
+ }
+
+ str[i] = '\0';
+
+ if ((i + 1) < len) {
+ if (ptr[i] == '\r' && ptr[i + 1] == '\n')
+ i++;
+ }
+
+ evbuffer_drain(evb, ++i);
+
+ return (str);
}
char *
Index: usr.sbin/httpd/httpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.h,v
retrieving revision 1.51
diff -u -p -r1.51 httpd.h
--- usr.sbin/httpd/httpd.h 6 Aug 2014 18:21:14 -0000 1.51
+++ usr.sbin/httpd/httpd.h 18 Nov 2014 15:02:54 -0000
@@ -276,7 +276,8 @@ struct client {
size_t clt_buflen;
struct evbuffer *clt_output;
struct event clt_ev;
- void *clt_desc;
+ void *clt_descreq;
+ void *clt_descresp;
int clt_sndbufsiz;
int clt_fd;
@@ -294,6 +295,8 @@ struct client {
int clt_fcgi_toread;
int clt_fcgi_padding_len;
int clt_fcgi_type;
+ int clt_fcgi_chunked;
+ int clt_fcgi_end;
struct evbuffer *clt_srvevb;
struct evbuffer *clt_log;
@@ -463,6 +466,8 @@ pid_t server(struct privsep *, struct p
int server_ssl_load_keypair(struct server *);
int server_privinit(struct server *);
void server_purge(struct server *);
+void serverconfig_free(struct server_config *);
+void serverconfig_reset(struct server_config *);
int server_socket_af(struct sockaddr_storage *, in_port_t);
in_port_t
server_socket_getport(struct sockaddr_storage *);
@@ -477,6 +482,8 @@ void server_sendlog(struct server_confi
void server_close(struct client *, const char *);
void server_dump(struct client *, const void *, size_t);
int server_client_cmp(struct client *, struct client *);
+int server_bufferevent_printf(struct client *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
int server_bufferevent_print(struct client *, const char *);
int server_bufferevent_write_buffer(struct client *,
struct evbuffer *);
@@ -508,17 +515,20 @@ const char
void server_read_httpcontent(struct bufferevent *, void *);
void server_read_httpchunks(struct bufferevent *, void *);
int server_writeheader_http(struct client *clt, struct kv *, void *);
-int server_headers(struct client *,
+int server_headers(struct client *, void *,
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);
+ size_t, time_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);
+char *server_http_parsehost(char *, char *, size_t, int *);
+ssize_t server_http_time(time_t, char *, size_t);
int server_log_http(struct client *, u_int, size_t);
/* server_file.c */
@@ -533,13 +543,15 @@ int fcgi_add_stdin(struct client *, str
void event_again(struct event *, int, short,
void (*)(int, short, void *),
struct timeval *, struct timeval *, void *);
+const char *url_decode(char *);
const char *canonicalize_host(const char *, char *, size_t);
const char *canonicalize_path(const char *, char *, size_t);
-ssize_t path_info(char *);
+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);
void socket_rlimit(int);
+char *evbuffer_getline(struct evbuffer *);
char *get_string(u_int8_t *, size_t);
void *get_data(u_int8_t *, size_t);
int sockaddr_cmp(struct sockaddr *, struct sockaddr *, int);
@@ -575,6 +587,7 @@ void log_warn(const char *, ...) __attri
void log_warnx(const char *, ...) __attribute__((__format__ (printf, 1, 2)));
void log_info(const char *, ...) __attribute__((__format__ (printf, 1, 2)));
void log_debug(const char *, ...) __attribute__((__format__ (printf, 1, 2)));
+void logit(int, const char *, ...) __attribute__((__format__ (printf, 2, 3)));
void vlog(int, const char *, va_list) __attribute__((__format__ (printf, 2, 0)));
__dead void fatal(const char *);
__dead void fatalx(const char *);
Index: usr.sbin/httpd/logger.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/logger.c,v
retrieving revision 1.5
diff -u -p -r1.5 logger.c
--- usr.sbin/httpd/logger.c 6 Aug 2014 12:56:58 -0000 1.5
+++ usr.sbin/httpd/logger.c 18 Nov 2014 15:02:55 -0000
@@ -194,6 +194,9 @@ logger_open(struct server *srv, struct s
{
struct log_file *log, *logfile = NULL, *errfile = NULL;
+ if (srv_conf->flags & SRVFLAG_SYSLOG)
+ return(0);
+
/* disassociate */
srv_conf->logaccess = srv_conf->logerror = NULL;
Index: usr.sbin/httpd/parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/parse.y,v
retrieving revision 1.34
diff -u -p -r1.34 parse.y
--- usr.sbin/httpd/parse.y 6 Aug 2014 20:29:54 -0000 1.34
+++ usr.sbin/httpd/parse.y 18 Nov 2014 15:02:55 -0000
@@ -180,7 +180,7 @@ main : PREFORK NUMBER {
break;
if ($2 <= 0 || $2 > SERVER_MAXPROC) {
yyerror("invalid number of preforked "
- "servers: %d", $2);
+ "servers: %lld", $2);
YYERROR;
}
conf->sc_prefork_server = $2;
@@ -198,15 +198,6 @@ server : SERVER STRING {
YYACCEPT;
}
- TAILQ_FOREACH(s, conf->sc_servers, srv_entry)
- if (!strcmp(s->srv_conf.name, $2))
- break;
- if (s != NULL) {
- yyerror("server %s defined twice", $2);
- free($2);
- YYERROR;
- }
-
if ((s = calloc(1, sizeof (*s))) == NULL)
fatal("out of memory");
@@ -252,18 +243,46 @@ server : SERVER STRING {
srv_conf = &srv->srv_conf;
SPLAY_INIT(&srv->srv_clients);
- TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry);
} '{' optnl serveropts_l '}' {
+ struct server *s = NULL;
+
+ TAILQ_FOREACH(s, conf->sc_servers, srv_entry) {
+ if ((s->srv_conf.flags &
+ SRVFLAG_LOCATION) == 0 &&
+ strcmp(s->srv_conf.name,
+ srv->srv_conf.name) == 0 &&
+ s->srv_conf.port == srv->srv_conf.port &&
+ sockaddr_cmp(
+ (struct sockaddr *)&s->srv_conf.ss,
+ (struct sockaddr *)&srv->srv_conf.ss,
+ s->srv_conf.prefixlen) == 0)
+ break;
+ }
+ if (s != NULL) {
+ yyerror("server \"%s\" defined twice",
+ srv->srv_conf.name);
+ serverconfig_free(srv_conf);
+ free(srv);
+ YYABORT;
+ }
+
if (srv->srv_conf.ss.ss_family == AF_UNSPEC) {
yyerror("listen address not specified");
- free($2);
+ serverconfig_free(srv_conf);
+ free(srv);
YYERROR;
}
+
if (server_ssl_load_keypair(srv) == -1) {
yyerror("failed to load public/private keys "
"for server %s", srv->srv_conf.name);
+ serverconfig_free(srv_conf);
+ free(srv);
YYERROR;
}
+
+ TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry);
+
srv = NULL;
srv_conf = NULL;
}
@@ -367,17 +386,6 @@ serveroptsl : LISTEN ON STRING optssl po
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");
@@ -416,12 +424,31 @@ serveroptsl : LISTEN ON STRING optssl po
srv = s;
srv_conf = &srv->srv_conf;
SPLAY_INIT(&srv->srv_clients);
- TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry);
} '{' optnl serveropts_l '}' {
+ struct server *s = NULL;
+
+ TAILQ_FOREACH(s, conf->sc_servers, srv_entry) {
+ if ((s->srv_conf.flags & SRVFLAG_LOCATION) &&
+ s->srv_conf.id == srv_conf->id &&
+ strcmp(s->srv_conf.location,
+ srv_conf->location) == 0)
+ break;
+ }
+ if (s != NULL) {
+ yyerror("location \"%s\" defined twice",
+ srv->srv_conf.location);
+ serverconfig_free(srv_conf);
+ free(srv);
+ YYABORT;
+ }
+
+ TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry);
+
srv = parentsrv;
srv_conf = &parentsrv->srv_conf;
parentsrv = NULL;
}
+ | include
;
fastcgi : NO FCGI {
@@ -623,7 +650,7 @@ tcpflags : SACK { srv_conf->tcpflags |
}
| BACKLOG NUMBER {
if ($2 < 0 || $2 > SERVER_MAX_CLIENTS) {
- yyerror("invalid backlog: %d", $2);
+ yyerror("invalid backlog: %lld", $2);
YYERROR;
}
srv_conf->tcpbacklog = $2;
@@ -631,13 +658,13 @@ tcpflags : SACK { srv_conf->tcpflags |
| SOCKET BUFFER NUMBER {
srv_conf->tcpflags |= TCPFLAG_BUFSIZ;
if ((srv_conf->tcpbufsiz = $3) < 0) {
- yyerror("invalid socket buffer size: %d", $3);
+ yyerror("invalid socket buffer size: %lld", $3);
YYERROR;
}
}
| IP STRING NUMBER {
if ($3 < 0) {
- yyerror("invalid ttl: %d", $3);
+ yyerror("invalid ttl: %lld", $3);
free($2);
YYERROR;
}
@@ -694,6 +721,9 @@ medianamesl : STRING {
}
free($1);
+ if (!loadcfg)
+ break;
+
if (media_add(conf->sc_mediatypes, &media) == NULL) {
yyerror("failed to add media type");
YYERROR;
@@ -729,7 +759,7 @@ port : PORT STRING {
}
| PORT NUMBER {
if ($2 <= 0 || $2 >= (int)USHRT_MAX) {
- yyerror("invalid port: %d", $2);
+ yyerror("invalid port: %lld", $2);
YYERROR;
}
$$.val[0] = htons($2);
@@ -740,7 +770,7 @@ port : PORT STRING {
timeout : NUMBER
{
if ($1 < 0) {
- yyerror("invalid timeout: %d\n", $1);
+ yyerror("invalid timeout: %lld", $1);
YYERROR;
}
$$.tv_sec = $1;
@@ -771,15 +801,15 @@ int
yyerror(const char *fmt, ...)
{
va_list ap;
- char *nfmt;
+ char *msg;
file->errors++;
va_start(ap, fmt);
- if (asprintf(&nfmt, "%s:%d: %s", file->name, yylval.lineno, fmt) == -1)
- fatalx("yyerror asprintf");
- vlog(LOG_CRIT, nfmt, ap);
+ if (vasprintf(&msg, fmt, ap) == -1)
+ fatalx("yyerror vasprintf");
va_end(ap);
- free(nfmt);
+ logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
+ free(msg);
return (0);
}
Index: usr.sbin/httpd/server.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server.c,v
retrieving revision 1.39
diff -u -p -r1.39 server.c
--- usr.sbin/httpd/server.c 6 Aug 2014 18:38:11 -0000 1.39
+++ usr.sbin/httpd/server.c 18 Nov 2014 15:02:55 -0000
@@ -285,8 +285,7 @@ server_purge(struct server *srv)
/* It might point to our own "default" entry */
if (srv_conf != &srv->srv_conf) {
- free(srv_conf->ssl_cert);
- free(srv_conf->ssl_key);
+ serverconfig_free(srv_conf);
free(srv_conf);
}
}
@@ -297,6 +296,22 @@ server_purge(struct server *srv)
free(srv);
}
+void
+serverconfig_free(struct server_config *srv_conf)
+{
+ free(srv_conf->ssl_cert_file);
+ free(srv_conf->ssl_cert);
+ free(srv_conf->ssl_key_file);
+ free(srv_conf->ssl_key);
+}
+
+void
+serverconfig_reset(struct server_config *srv_conf)
+{
+ srv_conf->ssl_cert_file = srv_conf->ssl_cert =
+ srv_conf->ssl_key_file = srv_conf->ssl_key = NULL;
+}
+
struct server *
server_byaddr(struct sockaddr *addr, in_port_t port)
{
@@ -750,23 +765,36 @@ void
server_error(struct bufferevent *bev, short error, void *arg)
{
struct client *clt = arg;
+ struct evbuffer *dst;
if (error & EVBUFFER_TIMEOUT) {
server_close(clt, "buffer event timeout");
return;
}
- if (error & EVBUFFER_ERROR && errno == EFBIG) {
- bufferevent_enable(bev, EV_READ);
+ if (error & EVBUFFER_ERROR) {
+ if (errno == EFBIG) {
+ bufferevent_enable(bev, EV_READ);
+ return;
+ }
+ server_close(clt, "buffer event error");
return;
}
if (error & (EVBUFFER_READ|EVBUFFER_WRITE|EVBUFFER_EOF)) {
bufferevent_disable(bev, EV_READ|EV_WRITE);
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;
+ }
+
server_close(clt, "done");
return;
}
- server_close(clt, "buffer event error");
+ server_close(clt, "unknown event error");
return;
}
@@ -1109,6 +1137,26 @@ server_bufferevent_add(struct event *ev,
}
return (event_add(ev, ptv));
+}
+
+int
+server_bufferevent_printf(struct client *clt, const char *fmt, ...)
+{
+ int ret;
+ va_list ap;
+ char *str;
+
+ va_start(ap, fmt);
+ ret = vasprintf(&str, fmt, ap);
+ va_end(ap);
+
+ if (ret == -1)
+ return (ret);
+
+ ret = server_bufferevent_print(clt, str);
+ free(str);
+
+ return (ret);
}
int
Index: usr.sbin/httpd/server_fcgi.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server_fcgi.c,v
retrieving revision 1.29
diff -u -p -r1.29 server_fcgi.c
--- usr.sbin/httpd/server_fcgi.c 7 Aug 2014 12:43:22 -0000 1.29
+++ usr.sbin/httpd/server_fcgi.c 18 Nov 2014 15:02:55 -0000
@@ -87,22 +87,24 @@ struct server_fcgi_param {
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 server_fcgi_writechunk(struct client *);
+int server_fcgi_getheaders(struct client *);
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;
- char hbuf[MAXHOSTNAMELEN];
struct server_config *srv_conf = clt->clt_srv_conf;
- struct http_descriptor *desc = clt->clt_desc;
+ struct http_descriptor *desc = clt->clt_descreq;
struct sockaddr_un sun;
struct fcgi_record_header *h;
struct fcgi_begin_request_body *begin;
size_t len;
- ssize_t scriptlen;
+ char hbuf[MAXHOSTNAMELEN];
+ size_t scriptlen;
+ int pathlen;
int fd = -1, ret;
const char *errstr = NULL;
char *str, *p, *script = NULL;
@@ -189,14 +191,21 @@ server_fcgi(struct httpd *env, struct cl
h->type = FCGI_PARAMS;
h->content_len = param.total_len = 0;
- if (asprintf(&script, "%s%s", srv_conf->root,
- desc->http_path) == -1 ||
- (scriptlen = path_info(script)) == -1) {
+ 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;
}
- if (scriptlen) {
+ 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(¶m, "PATH_INFO",
script + scriptlen, clt) == -1) {
errstr = "failed to encode param";
@@ -239,7 +248,7 @@ server_fcgi(struct httpd *env, struct cl
}
/* Add HTTP_* headers */
- if (server_headers(clt, server_fcgi_writeheader, ¶m) == -1) {
+ if (server_headers(clt, desc, server_fcgi_writeheader, ¶m) == -1) {
errstr = "failed to encode param";
goto fail;
}
@@ -337,11 +346,14 @@ server_fcgi(struct httpd *env, struct cl
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;
+ if (strcmp(desc->http_version, "HTTP/1.1") == 0) {
+ clt->clt_fcgi_chunked = 1;
+ } else {
+ /* HTTP/1.0 does not support chunked encoding */
+ clt->clt_fcgi_chunked = 0;
+ clt->clt_persist = 0;
+ }
+ clt->clt_fcgi_end = 0;
clt->clt_done = 0;
free(script);
@@ -444,9 +456,9 @@ server_fcgi_read(struct bufferevent *bev
char *ptr;
do {
- len = bufferevent_read(bev, &buf, clt->clt_fcgi_toread);
+ len = bufferevent_read(bev, buf, clt->clt_fcgi_toread);
/* XXX error handling */
- evbuffer_add(clt->clt_srvevb, &buf, len);
+ 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);
@@ -478,9 +490,10 @@ server_fcgi_read(struct bufferevent *bev
/* 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(
+ switch (clt->clt_fcgi_type) {
+ case FCGI_STDERR:
+ if (EVBUFFER_LENGTH(clt->clt_srvevb) > 0 &&
+ (ptr = get_string(
EVBUFFER_DATA(clt->clt_srvevb),
EVBUFFER_LENGTH(clt->clt_srvevb)))
!= NULL) {
@@ -488,14 +501,27 @@ server_fcgi_read(struct bufferevent *bev
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);
+ break;
+ case FCGI_STDOUT:
+ if (++clt->clt_chunk == 1) {
+ if (server_fcgi_header(clt,
+ server_fcgi_getheaders(clt))
+ == -1) {
+ server_abort_http(clt, 500,
+ "malformed fcgi headers");
+ return;
+ }
+ if (!EVBUFFER_LENGTH(clt->clt_srvevb))
+ break;
+ }
+ /* FALLTHROUGH */
+ case FCGI_END_REQUEST:
+ if (server_fcgi_writechunk(clt) == -1) {
+ server_abort_http(clt, 500,
+ "encoding error");
+ return;
+ }
+ break;
}
evbuffer_drain(clt->clt_srvevb,
EVBUFFER_LENGTH(clt->clt_srvevb));
@@ -523,9 +549,11 @@ server_fcgi_read(struct bufferevent *bev
int
server_fcgi_header(struct client *clt, u_int code)
{
- struct http_descriptor *desc = clt->clt_desc;
+ struct http_descriptor *desc = clt->clt_descreq;
+ struct http_descriptor *resp = clt->clt_descresp;
const char *error;
char tmbuf[32];
+ struct kv *kv, key;
if (desc == NULL || (error = server_httperror_byid(code)) == NULL)
return (-1);
@@ -533,34 +561,49 @@ server_fcgi_header(struct client *clt, u
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)
+ if (kv_setkey(&resp->http_pathquery, "%lu", code) == -1 ||
+ kv_set(&resp->http_pathquery, "%s", error) == -1)
return (-1);
/* Add headers */
- if (kv_add(&desc->http_headers, "Server", HTTPD_SERVERNAME) == NULL)
+ if (kv_add(&resp->http_headers, "Server", HTTPD_SERVERNAME) == NULL)
return (-1);
+ /* Set chunked encoding */
+ if (clt->clt_fcgi_chunked) {
+ /* XXX Should we keep and handle Content-Length instead? */
+ key.kv_key = "Content-Length";
+ if ((kv = kv_find(&resp->http_headers, &key)) != NULL)
+ kv_delete(&resp->http_headers, kv);
+
+ /*
+ * XXX What if the FastCGI added some kind of Transfer-Encoding?
+ * XXX like gzip, deflate or even "chunked"?
+ */
+ if (kv_add(&resp->http_headers,
+ "Transfer-Encoding", "chunked") == NULL)
+ return (-1);
+ }
+
/* Is it a persistent connection? */
if (clt->clt_persist) {
- if (kv_add(&desc->http_headers,
+ if (kv_add(&resp->http_headers,
"Connection", "keep-alive") == NULL)
return (-1);
- } else if (kv_add(&desc->http_headers, "Connection", "close") == NULL)
+ } else if (kv_add(&resp->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)
+ /* Date header is mandatory and should be added as late as possible */
+ if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0 ||
+ kv_add(&resp->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)
+ server_headers(clt, resp, server_writeheader_http, NULL) == -1 ||
+ server_bufferevent_print(clt, "\r\n") == -1)
return (-1);
return (0);
@@ -608,26 +651,63 @@ server_fcgi_writeheader(struct client *c
}
int
-get_status(struct evbuffer *bev)
+server_fcgi_writechunk(struct client *clt)
{
- 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;
- }
+ struct evbuffer *evb = clt->clt_srvevb;
+ size_t len;
+
+ if (clt->clt_fcgi_type == FCGI_END_REQUEST) {
+ len = 0;
+ } else
+ len = EVBUFFER_LENGTH(evb);
+
+ /* If len is 0, make sure to write the end marker only once */
+ if (len == 0 && clt->clt_fcgi_end++)
+ return (0);
+
+ if (clt->clt_fcgi_chunked) {
+ if (server_bufferevent_printf(clt, "%zx\r\n", len) == -1 ||
+ server_bufferevent_write_chunk(clt, evb, len) == -1 ||
+ server_bufferevent_print(clt, "\r\n") == -1)
+ return (-1);
+ } else
+ return (server_bufferevent_write_buffer(clt, evb));
+
+ return (0);
+}
+
+int
+server_fcgi_getheaders(struct client *clt)
+{
+ struct http_descriptor *resp = clt->clt_descresp;
+ struct evbuffer *evb = clt->clt_srvevb;
+ int code = 200;
+ char *line, *key, *value;
+ const char *errstr;
+
+ while ((line = evbuffer_getline(evb)) != NULL && *line != '\0') {
+ key = line;
+
+ if ((value = strchr(key, ':')) == NULL)
+ break;
+ if (*value == ':') {
+ *value++ = '\0';
+ value += strspn(value, " \t");
+ } else {
+ *value++ = '\0';
+ }
+
+ if (strcasecmp("Status", key) == 0) {
+ value[strcspn(value, " \t")] = '\0';
+ code = (int)strtonum(value, 100, 600, &errstr);
+ if (errstr != NULL || server_httperror_byid(
+ code) == NULL)
+ code = 200;
+ } else {
+ (void)kv_add(&resp->http_headers, key, value);
}
- free(statusline);
+ free(line);
}
- return code;
+
+ return (code);
}
Index: usr.sbin/httpd/server_file.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server_file.c,v
retrieving revision 1.31
diff -u -p -r1.31 server_file.c
--- usr.sbin/httpd/server_file.c 6 Aug 2014 11:24:12 -0000 1.31
+++ usr.sbin/httpd/server_file.c 18 Nov 2014 15:02:55 -0000
@@ -46,42 +46,36 @@
#include "httpd.h"
#include "http.h"
-int server_file_access(struct client *, 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 *);
+int server_file_index(struct httpd *, struct client *, struct stat *);
+int server_file_method(struct client *);
int
-server_file_access(struct client *clt, char *path, size_t len,
- struct stat *st)
+server_file_access(struct httpd *env, struct client *clt,
+ char *path, size_t len)
{
- struct http_descriptor *desc = clt->clt_desc;
+ struct http_descriptor *desc = clt->clt_descreq;
struct server_config *srv_conf = clt->clt_srv_conf;
- struct stat stb;
+ struct stat st;
char *newpath;
+ int ret;
errno = 0;
- switch (desc->http_method) {
- case HTTP_METHOD_GET:
- case HTTP_METHOD_HEAD:
- break;
- default:
- /* Other methods are not allowed */
- return (405);
- }
-
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)) {
+ } 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 (!len) {
+ if (desc->http_path_alias != NULL) {
/* Recursion - the index "file" is a directory? */
errno = EINVAL;
goto fail;
@@ -93,21 +87,31 @@ server_file_access(struct client *clt, c
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 */
+ /* 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(clt, path, 0, &stb) == 404) {
+ ret = server_file_access(env, clt, path, len);
+ if (ret == 404) {
/*
* Index file not found; fail if auto-indexing is
* not enabled, otherwise return success but
@@ -118,17 +122,17 @@ server_file_access(struct client *clt, c
errno = EACCES;
goto fail;
}
- } else {
- /* return updated stat from index file */
- memcpy(st, &stb, sizeof(*st));
+
+ return (server_file_index(env, clt, &st));
}
- } 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) {
@@ -146,31 +150,69 @@ server_file_access(struct client *clt, c
int
server_file(struct httpd *env, struct client *clt)
{
- struct http_descriptor *desc = clt->clt_desc;
+ struct http_descriptor *desc = clt->clt_descreq;
struct server_config *srv_conf = clt->clt_srv_conf;
- struct media_type *media;
- const char *errstr = NULL;
- int fd = -1, ret, code = 500;
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->root, desc->http_path) >= sizeof(path)) {
+ 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(clt, path, sizeof(path), &st)) != 0) {
- code = ret;
- errstr = desc->http_path;
+ 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_descreq;
+
+ 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 */
@@ -178,7 +220,8 @@ server_file(struct httpd *env, struct cl
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,
+ MIN(time(NULL), st->st_mtim.tv_sec));
switch (ret) {
case -1:
goto fail;
@@ -225,20 +268,25 @@ server_file(struct httpd *env, struct cl
}
int
-server_file_index(struct httpd *env, struct client *clt)
+server_file_index(struct httpd *env, struct client *clt, struct stat *st)
{
char path[MAXPATHLEN];
char tmstr[21];
- struct http_descriptor *desc = clt->clt_desc;
+ struct http_descriptor *desc = clt->clt_descreq;
struct server_config *srv_conf = clt->clt_srv_conf;
struct dirent **namelist, *dp;
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;
+ time_t t, dir_mtime;
+
+ 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",
@@ -249,6 +297,9 @@ server_file_index(struct httpd *env, str
if ((fd = open(path, O_RDONLY)) == -1)
goto abort;
+ /* Save last modification time */
+ dir_mtime = MIN(time(NULL), st->st_mtim.tv_sec);
+
if ((evb = evbuffer_new()) == NULL)
goto abort;
@@ -260,7 +311,7 @@ server_file_index(struct httpd *env, str
/* A CSS stylesheet allows minimal customization by the user */
style = "body { background-color: white; color: black; font-family: "
- "sans-serif; }";
+ "sans-serif; }\nhr { border: 0; border-bottom: 1px dashed; }\n";
/* Generate simple HTML index document */
if (evbuffer_add_printf(evb,
"d_name);
@@ -293,18 +344,18 @@ server_file_index(struct httpd *env, str
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)) {
+ } else if (S_ISDIR(st->st_mode)) {
namewidth -= 1; /* trailing slash */
if (evbuffer_add_printf(evb,
"%s/%*s%s%20s\n",
dp->d_name, dp->d_name,
MAX(namewidth, 0), " ", tmstr, "-") == -1)
skip = 1;
- } else if (S_ISREG(st.st_mode)) {
+ } else if (S_ISREG(st->st_mode)) {
if (evbuffer_add_printf(evb,
"%s%*s%s%20llu\n",
dp->d_name, dp->d_name,
- MAX(namewidth, 0), " ", tmstr, st.st_size) == -1)
+ MAX(namewidth, 0), " ", tmstr, st->st_size) == -1)
skip = 1;
}
free(dp);
@@ -320,7 +371,8 @@ server_file_index(struct httpd *env, str
fd = -1;
media = media_find(env->sc_mediatypes, "index.html");
- ret = server_response_http(clt, 200, media, EVBUFFER_LENGTH(evb));
+ ret = server_response_http(clt, 200, media, EVBUFFER_LENGTH(evb),
+ dir_mtime);
switch (ret) {
case -1:
goto fail;
@@ -356,7 +408,7 @@ server_file_index(struct httpd *env, str
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);
}
@@ -370,8 +422,16 @@ server_file_error(struct bufferevent *be
server_close(clt, "buffer event timeout");
return;
}
+ if (error & EVBUFFER_ERROR) {
+ if (errno == EFBIG) {
+ bufferevent_enable(bev, EV_READ);
+ return;
+ }
+ server_close(clt, "buffer event error");
+ return;
+ }
if (error & (EVBUFFER_READ|EVBUFFER_WRITE|EVBUFFER_EOF)) {
- bufferevent_disable(bev, EV_READ);
+ bufferevent_disable(bev, EV_READ|EV_WRITE);
clt->clt_done = 1;
@@ -396,10 +456,6 @@ server_file_error(struct bufferevent *be
server_close(clt, "done");
return;
}
- if (error & EVBUFFER_ERROR && errno == EFBIG) {
- bufferevent_enable(bev, EV_READ);
- return;
- }
- server_close(clt, "buffer event error");
+ server_close(clt, "unknown event error");
return;
}
Index: usr.sbin/httpd/server_http.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server_http.c,v
retrieving revision 1.42
diff -u -p -r1.42 server_http.c
--- usr.sbin/httpd/server_http.c 6 Aug 2014 18:21:14 -0000 1.42
+++ usr.sbin/httpd/server_http.c 18 Nov 2014 15:02:55 -0000
@@ -86,9 +86,15 @@ server_httpdesc_init(struct client *clt)
if ((desc = calloc(1, sizeof(*desc))) == NULL)
return (-1);
+ RB_INIT(&desc->http_headers);
+ clt->clt_descreq = desc;
+ if ((desc = calloc(1, sizeof(*desc))) == NULL) {
+ /* req will be cleaned up later */
+ return (-1);
+ }
RB_INIT(&desc->http_headers);
- clt->clt_desc = desc;
+ clt->clt_descresp = desc;
return (0);
}
@@ -96,10 +102,16 @@ server_httpdesc_init(struct client *clt)
void
server_httpdesc_free(struct http_descriptor *desc)
{
+ if (desc == NULL)
+ return;
if (desc->http_path != NULL) {
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;
@@ -114,6 +126,8 @@ server_httpdesc_free(struct http_descrip
}
kv_purge(&desc->http_headers);
desc->http_lastheader = NULL;
+ desc->http_method = 0;
+ desc->http_chunked = 0;
}
void
@@ -121,7 +135,7 @@ server_read_http(struct bufferevent *bev
{
struct client *clt = arg;
struct server_config *srv_conf = clt->clt_srv_conf;
- struct http_descriptor *desc = clt->clt_desc;
+ struct http_descriptor *desc = clt->clt_descreq;
struct evbuffer *src = EVBUFFER_INPUT(bev);
char *line = NULL, *key, *value;
const char *errstr;
@@ -215,18 +229,20 @@ server_read_http(struct bufferevent *bev
goto fail;
}
desc->http_version = strchr(desc->http_path, ' ');
- if (desc->http_version != NULL)
- *desc->http_version++ = '\0';
+ if (desc->http_version == NULL) {
+ free(line);
+ goto fail;
+ }
+ *desc->http_version++ = '\0';
desc->http_query = strchr(desc->http_path, '?');
if (desc->http_query != NULL)
*desc->http_query++ = '\0';
/*
* Have to allocate the strings because they could
- * be changed independetly by the filters later.
+ * be changed independently by the filters later.
*/
- if (desc->http_version != NULL &&
- (desc->http_version =
+ if ((desc->http_version =
strdup(desc->http_version)) == NULL) {
free(line);
goto fail;
@@ -300,11 +316,36 @@ server_read_http(struct bufferevent *bev
case HTTP_METHOD_GET:
case HTTP_METHOD_HEAD:
case HTTP_METHOD_OPTIONS:
+ /* WebDAV methods */
+ case HTTP_METHOD_COPY:
clt->clt_toread = 0;
break;
case HTTP_METHOD_POST:
case HTTP_METHOD_PUT:
case HTTP_METHOD_RESPONSE:
+ /* WebDAV methods */
+ case HTTP_METHOD_PROPFIND:
+ case HTTP_METHOD_PROPPATCH:
+ case HTTP_METHOD_MKCOL:
+ case HTTP_METHOD_LOCK:
+ case HTTP_METHOD_UNLOCK:
+ case HTTP_METHOD_VERSION_CONTROL:
+ case HTTP_METHOD_REPORT:
+ case HTTP_METHOD_CHECKOUT:
+ case HTTP_METHOD_CHECKIN:
+ case HTTP_METHOD_UNCHECKOUT:
+ case HTTP_METHOD_MKWORKSPACE:
+ case HTTP_METHOD_UPDATE:
+ case HTTP_METHOD_LABEL:
+ case HTTP_METHOD_MERGE:
+ case HTTP_METHOD_BASELINE_CONTROL:
+ case HTTP_METHOD_MKACTIVITY:
+ case HTTP_METHOD_ORDERPATCH:
+ case HTTP_METHOD_ACL:
+ case HTTP_METHOD_MKREDIRECTREF:
+ case HTTP_METHOD_UPDATEREDIRECTREF:
+ case HTTP_METHOD_SEARCH:
+ case HTTP_METHOD_PATCH:
/* HTTP request payload */
if (clt->clt_toread > 0)
bev->readcb = server_read_httpcontent;
@@ -316,10 +357,8 @@ server_read_http(struct bufferevent *bev
}
break;
default:
- /* HTTP handler */
- clt->clt_toread = TOREAD_HTTP_HEADER;
- bev->readcb = server_read_http;
- break;
+ server_abort_http(clt, 405, "method not allowed");
+ return;
}
if (desc->http_chunked) {
/* Chunked transfer encoding */
@@ -514,12 +553,10 @@ server_read_httpchunks(struct buffereven
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;
- desc->http_chunked = 0;
+ server_httpdesc_free(clt->clt_descreq);
+ server_httpdesc_free(clt->clt_descresp);
clt->clt_headerlen = 0;
clt->clt_line = 0;
clt->clt_done = 0;
@@ -530,16 +567,16 @@ server_reset_http(struct client *clt)
server_log(clt, NULL);
}
-void
-server_http_date(char *tmbuf, size_t len)
+ssize_t
+server_http_time(time_t t, 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);
+ if (t == -1 || gmtime_r(&t, &tm) == NULL)
+ return (-1);
+ else
+ return (strftime(tmbuf, len, "%a, %d %h %Y %T %Z", &tm));
}
const char *
@@ -574,6 +611,55 @@ server_http_host(struct sockaddr_storage
return (buf);
}
+char *
+server_http_parsehost(char *host, char *buf, size_t len, int *portval)
+{
+ char *start, *end, *port;
+ const char *errstr = NULL;
+
+ if (strlcpy(buf, host, len) >= len) {
+ log_debug("%s: host name too long", __func__);
+ return (NULL);
+ }
+
+ start = buf;
+ end = port = NULL;
+
+ if (*start == '[' && (end = strchr(start, ']')) != NULL) {
+ /* Address enclosed in [] with port, eg. [2001:db8::1]:80 */
+ start++;
+ *end++ = '\0';
+ if ((port = strchr(end, ':')) == NULL || *port == '\0')
+ port = NULL;
+ else
+ port++;
+ memmove(buf, start, strlen(start) + 1);
+ } else if ((end = strchr(start, ':')) != NULL) {
+ /* Name or address with port, eg. www.example.com:80 */
+ *end++ = '\0';
+ port = end;
+ } else {
+ /* Name or address with default port, eg. www.example.com */
+ port = NULL;
+ }
+
+ if (port != NULL) {
+ /* Save the requested port */
+ *portval = strtonum(port, 0, 0xffff, &errstr);
+ if (errstr != NULL) {
+ log_debug("%s: invalid port: %s", __func__,
+ strerror(errno));
+ return (NULL);
+ }
+ *portval = htons(*portval);
+ } else {
+ /* Port not given, indicate the default port */
+ *portval = -1;
+ }
+
+ return (start);
+}
+
void
server_abort_http(struct client *clt, u_int code, const char *msg)
{
@@ -598,13 +684,11 @@ server_abort_http(struct client *clt, u_
if (print_host(&srv_conf->ss, hbuf, sizeof(hbuf)) == NULL)
goto done;
- server_http_date(tmbuf, sizeof(tmbuf));
+ if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0)
+ goto done;
/* Do not send details of the Internal Server Error */
switch (code) {
- case 500:
- /* Do not send details of the Internal Server Error */
- break;
case 301:
case 302:
if (asprintf(&extraheader, "Location: %s\r\n", msg) == -1) {
@@ -613,7 +697,6 @@ server_abort_http(struct client *clt, u_
}
break;
default:
- text = msg;
break;
}
@@ -665,12 +748,17 @@ server_abort_http(struct client *clt, u_
void
server_close_http(struct client *clt)
{
- struct http_descriptor *desc = clt->clt_desc;
+ struct http_descriptor *desc;
- if (desc == NULL)
- return;
+ desc = clt->clt_descreq;
+ server_httpdesc_free(desc);
+ free(desc);
+ clt->clt_descreq = NULL;
+
+ desc = clt->clt_descresp;
server_httpdesc_free(desc);
free(desc);
+ clt->clt_descresp = NULL;
}
int
@@ -678,13 +766,17 @@ server_response(struct httpd *httpd, str
{
char path[MAXPATHLEN];
char hostname[MAXHOSTNAMELEN];
- struct http_descriptor *desc = clt->clt_desc;
+ struct http_descriptor *desc = clt->clt_descreq;
+ struct http_descriptor *resp = clt->clt_descresp;
struct server *srv = clt->clt_srv;
- struct server_config *srv_conf = &srv->srv_conf, *location;
+ struct server_config *srv_conf = &srv->srv_conf;
struct kv *kv, key, *host;
+ int portval = -1;
+ char *hostval;
/* Canonicalize the request path */
if (desc->http_path == NULL ||
+ url_decode(desc->http_path) == NULL ||
canonicalize_path(desc->http_path, path, sizeof(path)) == NULL)
goto fail;
free(desc->http_path);
@@ -726,11 +818,16 @@ server_response(struct httpd *httpd, str
* XXX the Host can also appear in the URL path.
*/
if (host != NULL) {
- /* XXX maybe better to turn srv_hosts into a tree */
+ if ((hostval = server_http_parsehost(host->kv_value,
+ hostname, sizeof(hostname), &portval)) == NULL)
+ goto fail;
+
TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) {
if ((srv_conf->flags & SRVFLAG_LOCATION) == 0 &&
- fnmatch(srv_conf->name, host->kv_value,
- FNM_CASEFOLD) == 0) {
+ fnmatch(srv_conf->name, hostname,
+ FNM_CASEFOLD) == 0 &&
+ (portval == -1 ||
+ (portval != -1 && portval == srv_conf->port))) {
/* Replace host configuration */
clt->clt_srv_conf = srv_conf;
srv_conf = NULL;
@@ -755,31 +852,46 @@ server_response(struct httpd *httpd, str
if ((desc->http_host = strdup(hostname)) == NULL)
goto fail;
+ /* Now fill in the mandatory parts of the response descriptor */
+ resp->http_method = desc->http_method;
+ if ((resp->http_version = strdup(desc->http_version)) == NULL)
+ goto fail;
+
+ /* Now search for the location */
+ srv_conf = server_getlocation(clt, desc->http_path);
+
+ 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, desc->http_path,
- FNM_CASEFOLD) == 0) {
+ fnmatch(location->location, path, FNM_CASEFOLD) == 0) {
/* Replace host configuration */
clt->clt_srv_conf = srv_conf = location;
break;
}
}
- if (srv_conf->flags & SRVFLAG_FCGI)
- return (server_fcgi(httpd, clt));
- return (server_file(httpd, clt));
- fail:
- server_abort_http(clt, 400, "bad request");
- return (-1);
+ return (srv_conf);
}
int
server_response_http(struct client *clt, u_int code,
- struct media_type *media, size_t size)
+ struct media_type *media, size_t size, time_t mtime)
{
- struct http_descriptor *desc = clt->clt_desc;
+ struct http_descriptor *desc = clt->clt_descreq;
+ struct http_descriptor *resp = clt->clt_descresp;
const char *error;
struct kv *ct, *cl;
char tmbuf[32];
@@ -790,51 +902,54 @@ server_response_http(struct client *clt,
if (server_log_http(clt, code, size) == -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)
+ if (kv_setkey(&resp->http_pathquery, "%lu", code) == -1 ||
+ kv_set(&resp->http_pathquery, "%s", error) == -1)
return (-1);
/* Add headers */
- if (kv_add(&desc->http_headers, "Server", HTTPD_SERVERNAME) == NULL)
+ if (kv_add(&resp->http_headers, "Server", HTTPD_SERVERNAME) == NULL)
return (-1);
/* Is it a persistent connection? */
if (clt->clt_persist) {
- if (kv_add(&desc->http_headers,
+ if (kv_add(&resp->http_headers,
"Connection", "keep-alive") == NULL)
return (-1);
- } else if (kv_add(&desc->http_headers, "Connection", "close") == NULL)
+ } else if (kv_add(&resp->http_headers, "Connection", "close") == NULL)
return (-1);
/* Set media type */
- if ((ct = kv_add(&desc->http_headers, "Content-Type", NULL)) == NULL ||
+ if ((ct = kv_add(&resp->http_headers, "Content-Type", NULL)) == NULL ||
kv_set(ct, "%s/%s",
media == NULL ? "application" : media->media_type,
media == NULL ? "octet-stream" : media->media_subtype) == -1)
return (-1);
/* Set content length, if specified */
- if (size && ((cl =
- kv_add(&desc->http_headers, "Content-Length", NULL)) == NULL ||
- kv_set(cl, "%ld", size) == -1))
+ if ((cl =
+ kv_add(&resp->http_headers, "Content-Length", NULL)) == NULL ||
+ kv_set(cl, "%ld", size) == -1)
+ return (-1);
+
+ /* Set last modification time */
+ if (server_http_time(mtime, tmbuf, sizeof(tmbuf)) <= 0 ||
+ kv_add(&resp->http_headers, "Last-Modified", tmbuf) == 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)
+ /* Date header is mandatory and should be added as late as possible */
+ if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0 ||
+ kv_add(&resp->http_headers, "Date", tmbuf) == NULL)
return (-1);
/* Write completed header */
if (server_writeresponse_http(clt) == -1 ||
server_bufferevent_print(clt, "\r\n") == -1 ||
- server_headers(clt, server_writeheader_http, NULL) == -1 ||
+ server_headers(clt, resp, server_writeheader_http, NULL) == -1 ||
server_bufferevent_print(clt, "\r\n") == -1)
return (-1);
- if (desc->http_method == HTTP_METHOD_HEAD) {
+ if (size == 0 || resp->http_method == HTTP_METHOD_HEAD) {
bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
if (clt->clt_persist)
clt->clt_toread = TOREAD_HTTP_HEADER;
@@ -850,7 +965,7 @@ server_response_http(struct client *clt,
int
server_writeresponse_http(struct client *clt)
{
- struct http_descriptor *desc = (struct http_descriptor *)clt->clt_desc;
+ struct http_descriptor *desc = clt->clt_descresp;
DPRINTF("version: %s rescode: %s resmsg: %s", desc->http_version,
desc->http_rescode, desc->http_resmesg);
@@ -894,11 +1009,11 @@ server_writeheader_http(struct client *c
}
int
-server_headers(struct client *clt,
+server_headers(struct client *clt, void *descp,
int (*hdr_cb)(struct client *, struct kv *, void *), void *arg)
{
struct kv *hdr, *kv;
- struct http_descriptor *desc = (struct http_descriptor *)clt->clt_desc;
+ struct http_descriptor *desc = descp;
RB_FOREACH(hdr, kvtree, &desc->http_headers) {
if ((hdr_cb)(clt, hdr, arg) == -1)
@@ -932,7 +1047,7 @@ server_httpmethod_byname(const char *nam
const char *
server_httpmethod_byid(u_int id)
{
- const char *name = NULL;
+ const char *name = "";
int i;
for (i = 0; http_methods[i].method_name != NULL; i++) {
@@ -996,7 +1111,7 @@ server_log_http(struct client *clt, u_in
return (-1);
if ((srv_conf->flags & SRVFLAG_LOG) == 0)
return (0);
- if ((desc = clt->clt_desc) == NULL)
+ if ((desc = clt->clt_descreq) == NULL)
return (-1);
if ((t = time(NULL)) == -1)