kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

commit 88c3a3eb986ca5df86ae7c149de274d2b4c81345
parent ef463b49eba049fe6180f3a6979b4cd289a02ee0
Author: Joris Vink <joris@coders.se>
Date:   Sun, 22 Sep 2013 20:05:24 +0200

Add http_header_max and http_postbody_max configuration variables.

- http_header_max:
	Maximum size of HTTP headers (in non SPDY connections).

- http_postbody_max:
	Maximum size of an HTTP POST body (both in SPDY and HTTP mode).

Right now Kore will simply DC the client, ideally we want to send
a 413 (entity too large) to the client however.

See modules/examples/module.conf for more.

Diffstat:
includes/http.h | 5++++-
includes/kore.h | 6+++---
includes/spdy.h | 1+
modules/example/module.conf | 6++++++
src/config.c | 49+++++++++++++++++++++++++++++++++++++++++++++++++
src/connection.c | 4++--
src/http.c | 17++++++++++++++---
src/spdy.c | 40++++++++++++++++++++++++++++++++++++++--
src/utils.c | 6+++---
9 files changed, 120 insertions(+), 14 deletions(-)

diff --git a/includes/http.h b/includes/http.h @@ -18,6 +18,7 @@ #define __H_HTTP_H #define HTTP_HEADER_MAX_LEN 4096 +#define HTTP_POSTBODY_MAX_LEN 10240000 #define HTTP_URI_LEN 2000 #define HTTP_USERAGENT_LEN 256 #define HTTP_REQ_HEADER_MAX 25 @@ -76,7 +77,9 @@ struct http_request { TAILQ_ENTRY(http_request) olist; }; -extern int http_request_count; +extern int http_request_count; +extern u_int16_t http_header_max; +extern u_int64_t http_postbody_max; void http_init(void); void http_process(void); diff --git a/includes/kore.h b/includes/kore.h @@ -194,8 +194,8 @@ TAILQ_HEAD(kore_domain_h, kore_domain); struct kore_buf { u_int8_t *data; - u_int32_t length; - u_int32_t offset; + u_int64_t length; + u_int64_t offset; }; struct buf_vec { @@ -316,7 +316,7 @@ void kore_strlcpy(char *, const char *, size_t); void kore_server_disconnect(struct connection *); int kore_split_string(char *, char *, char **, size_t); void kore_strip_chars(char *, char, char **); -long long kore_strtonum(const char *, int, long long, long long, int *); +u_int64_t kore_strtonum(const char *, int, u_int64_t, u_int64_t, int *); int kore_base64_encode(u_int8_t *, u_int32_t, char **); int kore_base64_decode(char *, u_int8_t **, u_int32_t *); void *kore_mem_find(void *, size_t, void *, u_int32_t); diff --git a/includes/spdy.h b/includes/spdy.h @@ -52,6 +52,7 @@ struct spdy_stream { u_int32_t stream_id; u_int8_t flags; u_int8_t prio; + u_int64_t post_size; int32_t recv_wsize; int32_t send_wsize; diff --git a/modules/example/module.conf b/modules/example/module.conf @@ -33,6 +33,12 @@ workers 4 #kore_cb_interval 1000 #kore_cb_worker 3 +# HTTP specific settings. +# http_header_max Maximum size of HTTP headers (in bytes). +# http_postbody_max Maximum size of an HTTP POST body (in bytes). +#http_header_max 4096 +#http_postbody_max 10240000 + # Specifies what module to be loaded. load modules/example/example.module diff --git a/src/config.c b/src/config.c @@ -20,6 +20,7 @@ #include <pwd.h> #include "kore.h" +#include "http.h" static int configure_bind(char **); static int configure_load(char **); @@ -41,6 +42,8 @@ static int configure_spdy_idle_time(char **); static int configure_kore_cb(char **); static int configure_kore_cb_interval(char **); static int configure_kore_cb_worker(char **); +static int configure_http_header_max(char **); +static int configure_http_postbody_max(char **); static void domain_sslstart(void); static struct { @@ -68,6 +71,8 @@ static struct { { "kore_cb", configure_kore_cb }, { "kore_cb_worker", configure_kore_cb_worker }, { "kore_cb_interval", configure_kore_cb_interval }, + { "http_header_max", configure_http_header_max }, + { "http_postbody_max", configure_http_postbody_max }, { NULL, NULL }, }; @@ -500,6 +505,50 @@ configure_kore_cb_worker(char **argv) return (KORE_RESULT_OK); } +static int +configure_http_header_max(char **argv) +{ + int err; + + if (argv[1] == NULL) + return (KORE_RESULT_ERROR); + + if (http_header_max != HTTP_HEADER_MAX_LEN) { + kore_debug("http_header_max already set"); + return (KORE_RESULT_ERROR); + } + + http_header_max = kore_strtonum(argv[1], 10, 1, 65535, &err); + if (err != KORE_RESULT_OK) { + printf("bad http_header_max value: %s\n", argv[1]); + return (KORE_RESULT_ERROR); + } + + return (KORE_RESULT_OK); +} + +static int +configure_http_postbody_max(char **argv) +{ + int err; + + if (argv[1] == NULL) + return (KORE_RESULT_ERROR); + + if (http_postbody_max != HTTP_POSTBODY_MAX_LEN) { + kore_debug("http_postbody_max already set"); + return (KORE_RESULT_ERROR); + } + + http_postbody_max = kore_strtonum(argv[1], 10, 1, ULONG_MAX, &err); + if (err != KORE_RESULT_OK) { + printf("bad http_postbody_max value: %s\n", argv[1]); + return (KORE_RESULT_ERROR); + } + + return (KORE_RESULT_OK); +} + static void domain_sslstart(void) { diff --git a/src/connection.c b/src/connection.c @@ -152,7 +152,7 @@ kore_connection_handle(struct connection *c) NULL, spdy_frame_recv); } else if (!memcmp(data, "http/1.1", MIN(8, len))) { c->proto = CONN_PROTO_HTTP; - net_recv_queue(c, HTTP_HEADER_MAX_LEN, + net_recv_queue(c, http_header_max, NETBUF_CALL_CB_ALWAYS, NULL, http_header_recv); } else { @@ -160,7 +160,7 @@ kore_connection_handle(struct connection *c) } } else { c->proto = CONN_PROTO_HTTP; - net_recv_queue(c, HTTP_HEADER_MAX_LEN, + net_recv_queue(c, http_header_max, NETBUF_CALL_CB_ALWAYS, NULL, http_header_recv); } diff --git a/src/http.c b/src/http.c @@ -31,6 +31,8 @@ static struct kore_pool http_request_pool; static struct kore_pool http_header_pool; int http_request_count; +u_int16_t http_header_max = HTTP_HEADER_MAX_LEN; +u_int64_t http_postbody_max = HTTP_POSTBODY_MAX_LEN; void http_init(void) @@ -334,10 +336,11 @@ http_request_header_get(struct http_request *req, char *header, char **out) int http_header_recv(struct netbuf *nb) { + size_t len; + u_int64_t clen; struct http_header *hdr; struct http_request *req; struct netbuf *nnb; - size_t clen, len; u_int8_t *end_headers; int h, i, v, skip, bytes_left; char *request[4], *host[3], *hbuf; @@ -426,7 +429,7 @@ http_header_recv(struct netbuf *nb) return (KORE_RESULT_ERROR); } - clen = kore_strtonum(p, 10, 0, UINT_MAX, &v); + clen = kore_strtonum(p, 10, 0, ULONG_MAX, &v); if (v == KORE_RESULT_ERROR) { kore_mem_free(p); kore_debug("content-length invalid: %s", p); @@ -435,6 +438,14 @@ http_header_recv(struct netbuf *nb) } kore_mem_free(p); + + if (clen > http_postbody_max) { + kore_log(LOG_NOTICE, "POST data too large (%ld > %ld)", + clen, http_postbody_max); + req->flags |= HTTP_REQUEST_DELETE; + return (KORE_RESULT_ERROR); + } + req->post_data = kore_buf_create(clen); kore_buf_append(req->post_data, end_headers, (nb->offset - len)); @@ -840,7 +851,7 @@ http_send_done(struct netbuf *nb) { struct connection *c = (struct connection *)nb->owner; - net_recv_queue(c, HTTP_HEADER_MAX_LEN, + net_recv_queue(c, http_header_max, NETBUF_CALL_CB_ALWAYS, NULL, http_header_recv); return (KORE_RESULT_OK); diff --git a/src/spdy.c b/src/spdy.c @@ -595,8 +595,10 @@ static int spdy_data_frame_recv(struct netbuf *nb) { struct spdy_stream *s; + int err; struct http_request *req; struct spdy_data_frame data; + char *content; struct connection *c = (struct connection *)nb->owner; data.stream_id = net_read32(nb->buf) & ~(1 << 31); @@ -618,12 +620,46 @@ spdy_data_frame_recv(struct netbuf *nb) return (KORE_RESULT_ERROR); } - if (req->post_data == NULL) - req->post_data = kore_buf_create(data.length); + if (req->post_data == NULL) { + if (!spdy_stream_get_header(s->hblock, + "content-length", &content)) { + kore_debug("no content-length found for post"); + return (KORE_RESULT_ERROR); + } + + s->post_size = kore_strtonum(content, 10, 0, ULONG_MAX, &err); + if (err == KORE_RESULT_ERROR) { + kore_debug("bad content-length: %s", content); + kore_mem_free(content); + return (KORE_RESULT_ERROR); + } + + kore_mem_free(content); + + if (s->post_size > http_postbody_max) { + kore_log(LOG_NOTICE, "POST data too large (%ld > %ld)", + s->post_size, http_postbody_max); + return (KORE_RESULT_ERROR); + } + + req->post_data = kore_buf_create(s->post_size); + } + + if ((req->post_data->offset + data.length) > s->post_size) { + kore_debug("POST would grow too large"); + return (KORE_RESULT_ERROR); + } + kore_buf_append(req->post_data, (nb->buf + SPDY_FRAME_SIZE), data.length); if (data.flags & FLAG_FIN) { + if (req->post_data->offset != s->post_size) { + kore_debug("FLAG_FIN before all POST data received"); + return (KORE_RESULT_ERROR); + } + + s->post_size = 0; s->flags |= FLAG_FIN; req->flags |= HTTP_REQUEST_COMPLETE; } diff --git a/src/utils.c b/src/utils.c @@ -86,10 +86,10 @@ kore_strlcpy(char *dst, const char *src, size_t len) } } -long long -kore_strtonum(const char *str, int base, long long min, long long max, int *err) +u_int64_t +kore_strtonum(const char *str, int base, u_int64_t min, u_int64_t max, int *err) { - long long l; + u_int64_t l; char *ep; if (min > max) {