Add http_send_body() and data_send_error() and refactor
This turns the data-functions into the only functions "allowed" to send body-data (called with http_send_body()). The previous (hacky) approach of doing this in http_send_header() is not only out of place, it's an easy source of bugs given, for instance, the sending of body data is not expected with HEAD-requests. Given html_escape() is now only used in data.c, we move it there from util.c and make it a static method again. Signed-off-by: Laslo Hunhold <dev@frign.de>
This commit is contained in:
parent
db127723c6
commit
a36b901d40
7 changed files with 101 additions and 78 deletions
73
data.c
73
data.c
|
@ -38,10 +38,55 @@ suffix(int t)
|
|||
return "";
|
||||
}
|
||||
|
||||
static void
|
||||
html_escape(const char *src, char *dst, size_t dst_siz)
|
||||
{
|
||||
const struct {
|
||||
char c;
|
||||
char *s;
|
||||
} escape[] = {
|
||||
{ '&', "&" },
|
||||
{ '<', "<" },
|
||||
{ '>', ">" },
|
||||
{ '"', """ },
|
||||
{ '\'', "'" },
|
||||
};
|
||||
size_t i, j, k, esclen;
|
||||
|
||||
for (i = 0, j = 0; src[i] != '\0'; i++) {
|
||||
for (k = 0; k < LEN(escape); k++) {
|
||||
if (src[i] == escape[k].c) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (k == LEN(escape)) {
|
||||
/* no escape char at src[i] */
|
||||
if (j == dst_siz - 1) {
|
||||
/* silent truncation */
|
||||
break;
|
||||
} else {
|
||||
dst[j++] = src[i];
|
||||
}
|
||||
} else {
|
||||
/* escape char at src[i] */
|
||||
esclen = strlen(escape[k].s);
|
||||
|
||||
if (j >= dst_siz - esclen) {
|
||||
/* silent truncation */
|
||||
break;
|
||||
} else {
|
||||
memcpy(&dst[j], escape[k].s, esclen);
|
||||
j += esclen;
|
||||
}
|
||||
}
|
||||
}
|
||||
dst[j] = '\0';
|
||||
}
|
||||
|
||||
enum status
|
||||
data_send_dirlisting(int fd, const struct response *res)
|
||||
{
|
||||
enum status ret;
|
||||
enum status ret = 0;
|
||||
struct dirent **e;
|
||||
size_t i;
|
||||
int dirlen;
|
||||
|
@ -52,6 +97,17 @@ data_send_dirlisting(int fd, const struct response *res)
|
|||
return S_FORBIDDEN;
|
||||
}
|
||||
|
||||
/* listing header (we use esc because sizeof(esc) >= PATH_MAX) */
|
||||
html_escape(res->uri, esc, MIN(PATH_MAX, sizeof(esc)));
|
||||
if (dprintf(fd,
|
||||
"<!DOCTYPE html>\n<html>\n\t<head>"
|
||||
"<title>Index of %s</title></head>\n"
|
||||
"\t<body>\n\t\t<a href=\"..\">..</a>",
|
||||
esc) < 0) {
|
||||
ret = S_REQUEST_TIMEOUT;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* listing */
|
||||
for (i = 0; i < (size_t)dirlen; i++) {
|
||||
/* skip hidden files, "." and ".." */
|
||||
|
@ -86,6 +142,21 @@ cleanup:
|
|||
return ret;
|
||||
}
|
||||
|
||||
enum status
|
||||
data_send_error(int fd, const struct response *res)
|
||||
{
|
||||
if (dprintf(fd,
|
||||
"<!DOCTYPE html>\n<html>\n\t<head>\n"
|
||||
"\t\t<title>%d %s</title>\n\t</head>\n\t<body>\n"
|
||||
"\t\t<h1>%d %s</h1>\n\t</body>\n</html>\n",
|
||||
res->status, status_str[res->status],
|
||||
res->status, status_str[res->status]) < 0) {
|
||||
return S_REQUEST_TIMEOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum status
|
||||
data_send_file(int fd, const struct response *res)
|
||||
{
|
||||
|
|
1
data.h
1
data.h
|
@ -5,6 +5,7 @@
|
|||
#include "http.h"
|
||||
|
||||
enum status data_send_dirlisting(int, const struct response *);
|
||||
enum status data_send_error(int, const struct response *);
|
||||
enum status data_send_file(int, const struct response *);
|
||||
|
||||
#endif /* DATA_H */
|
||||
|
|
43
http.c
43
http.c
|
@ -17,6 +17,7 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "data.h"
|
||||
#include "http.h"
|
||||
#include "util.h"
|
||||
|
||||
|
@ -57,10 +58,16 @@ const char *res_field_str[] = {
|
|||
[RES_CONTENT_TYPE] = "Content-Type",
|
||||
};
|
||||
|
||||
enum status (* const body_fct[])(int, const struct response *) = {
|
||||
[RESTYPE_ERROR] = data_send_error,
|
||||
[RESTYPE_FILE] = data_send_file,
|
||||
[RESTYPE_DIRLISTING] = data_send_dirlisting,
|
||||
};
|
||||
|
||||
enum status
|
||||
http_send_header(int fd, const struct response *res)
|
||||
{
|
||||
char t[FIELD_MAX], esc[PATH_MAX];
|
||||
char t[FIELD_MAX];
|
||||
size_t i;
|
||||
|
||||
if (timestamp(t, sizeof(t), time(NULL))) {
|
||||
|
@ -88,27 +95,6 @@ http_send_header(int fd, const struct response *res)
|
|||
return S_REQUEST_TIMEOUT;
|
||||
}
|
||||
|
||||
/* listing header */
|
||||
if (res->type == RESTYPE_DIRLISTING) {
|
||||
html_escape(res->uri, esc, sizeof(esc));
|
||||
if (dprintf(fd,
|
||||
"<!DOCTYPE html>\n<html>\n\t<head>"
|
||||
"<title>Index of %s</title></head>\n"
|
||||
"\t<body>\n\t\t<a href=\"..\">..</a>",
|
||||
esc) < 0) {
|
||||
return S_REQUEST_TIMEOUT;
|
||||
}
|
||||
} else if (res->type == RESTYPE_ERROR) {
|
||||
if (dprintf(fd,
|
||||
"<!DOCTYPE html>\n<html>\n\t<head>\n"
|
||||
"\t\t<title>%d %s</title>\n\t</head>\n\t<body>\n"
|
||||
"\t\t<h1>%d %s</h1>\n\t</body>\n</html>\n",
|
||||
res->status, status_str[res->status],
|
||||
res->status, status_str[res->status]) < 0) {
|
||||
return S_REQUEST_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -854,3 +840,16 @@ http_prepare_error_response(const struct request *req,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum status
|
||||
http_send_body(int fd, const struct response *res,
|
||||
const struct request *req)
|
||||
{
|
||||
enum status s;
|
||||
|
||||
if (req->method == M_GET && (s = body_fct[res->type](fd, res))) {
|
||||
return s;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
6
http.h
6
http.h
|
@ -82,11 +82,13 @@ struct response {
|
|||
} file;
|
||||
};
|
||||
|
||||
extern enum status (* const body_fct[])(int, const struct response *);
|
||||
|
||||
enum conn_state {
|
||||
C_VACANT,
|
||||
C_RECV_HEADER,
|
||||
C_SEND_HEADER,
|
||||
C_SEND_DATA,
|
||||
C_SEND_BODY,
|
||||
NUM_CONN_STATES,
|
||||
};
|
||||
|
||||
|
@ -107,5 +109,7 @@ void http_prepare_response(const struct request *, struct response *,
|
|||
const struct server *);
|
||||
void http_prepare_error_response(const struct request *,
|
||||
struct response *, enum status);
|
||||
enum status http_send_body(int, const struct response *,
|
||||
const struct request *);
|
||||
|
||||
#endif /* HTTP_H */
|
||||
|
|
10
main.c
10
main.c
|
@ -45,15 +45,9 @@ serve(int infd, const struct sockaddr_storage *in_sa, const struct server *srv)
|
|||
http_prepare_response(&c.req, &c.res, srv);
|
||||
}
|
||||
|
||||
if ((s = http_send_header(c.fd, &c.res))) {
|
||||
if ((s = http_send_header(c.fd, &c.res)) ||
|
||||
(s = http_send_body(c.fd, &c.res, &c.req))) {
|
||||
c.res.status = s;
|
||||
} else {
|
||||
/* send data */
|
||||
if (c.res.type == RESTYPE_FILE) {
|
||||
data_send_file(c.fd, &c.res);
|
||||
} else if (c.res.type == RESTYPE_DIRLISTING) {
|
||||
data_send_dirlisting(c.fd, &c.res);
|
||||
}
|
||||
}
|
||||
|
||||
/* write output to log */
|
||||
|
|
45
util.c
45
util.c
|
@ -123,51 +123,6 @@ prepend(char *str, size_t size, const char *prefix)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
html_escape(const char *src, char *dst, size_t dst_siz)
|
||||
{
|
||||
const struct {
|
||||
char c;
|
||||
char *s;
|
||||
} escape[] = {
|
||||
{ '&', "&" },
|
||||
{ '<', "<" },
|
||||
{ '>', ">" },
|
||||
{ '"', """ },
|
||||
{ '\'', "'" },
|
||||
};
|
||||
size_t i, j, k, esclen;
|
||||
|
||||
for (i = 0, j = 0; src[i] != '\0'; i++) {
|
||||
for (k = 0; k < LEN(escape); k++) {
|
||||
if (src[i] == escape[k].c) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (k == LEN(escape)) {
|
||||
/* no escape char at src[i] */
|
||||
if (j == dst_siz - 1) {
|
||||
/* silent truncation */
|
||||
break;
|
||||
} else {
|
||||
dst[j++] = src[i];
|
||||
}
|
||||
} else {
|
||||
/* escape char at src[i] */
|
||||
esclen = strlen(escape[k].s);
|
||||
|
||||
if (j >= dst_siz - esclen) {
|
||||
/* silent truncation */
|
||||
break;
|
||||
} else {
|
||||
memcpy(&dst[j], escape[k].s, esclen);
|
||||
j += esclen;
|
||||
}
|
||||
}
|
||||
}
|
||||
dst[j] = '\0';
|
||||
}
|
||||
|
||||
#define INVALID 1
|
||||
#define TOOSMALL 2
|
||||
#define TOOLARGE 3
|
||||
|
|
1
util.h
1
util.h
|
@ -52,7 +52,6 @@ void eunveil(const char *, const char *);
|
|||
int timestamp(char *, size_t, time_t);
|
||||
int esnprintf(char *, size_t, const char *, ...);
|
||||
int prepend(char *, size_t, const char *);
|
||||
void html_escape(const char *, char *, size_t);
|
||||
|
||||
void *reallocarray(void *, size_t, size_t);
|
||||
long long strtonum(const char *, long long, long long, const char **);
|
||||
|
|
Loading…
Reference in a new issue