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 c7dcdbcd82438a3d520f8e3ba0fb2a4b3dc71ca8
parent b4c54f9dc09de4994f0ff856d81425c9daf5ce74
Author: Joris Vink <joris@coders.se>
Date:   Thu, 12 Dec 2013 00:58:32 +0100

Rework the way validation and param extraction works.

- Parameter validation is now done only when http_process_*()
  is called and upon http_argument_add().
- You MUST have defined your params in a param block or they will
  be filtered out.
- http_argument_lookup() is dead, welcome http_argument_get() and
  its brothers and sisters:
	http_argument_get_string()
	http_argument_get_uint16()
	http_argument_get_int16()
	http_argument_get_uint32()
	http_argument_get_int32()

  They will automatically do bounds checking on integers for you
  and return proper integers or a NUL-terminated string.

- The http_argument_get* functions no longer create an additional
  copy of the string which you need to free. Easier going.
- http_multiple_args() is dead, byebye
- Make some stuff we don't want to share with the modules static.

Diffstat:
includes/http.h | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
includes/kore.h | 2+-
modules/example/src/example.c | 32+++++++++++++++-----------------
src/http.c | 273++++++++++++++++++++++++++++++++++++-------------------------------------------
src/utils.c | 6+++---
5 files changed, 234 insertions(+), 195 deletions(-)

diff --git a/includes/http.h b/includes/http.h @@ -26,6 +26,14 @@ #define HTTP_REQ_HEADER_MAX 25 #define HTTP_MAX_QUERY_ARGS 10 +#define HTTP_ARG_TYPE_RAW 0 +#define HTTP_ARG_TYPE_BYTE 1 +#define HTTP_ARG_TYPE_INT16 2 +#define HTTP_ARG_TYPE_UINT16 3 +#define HTTP_ARG_TYPE_INT32 4 +#define HTTP_ARG_TYPE_UINT32 5 +#define HTTP_ARG_TYPE_STRING 6 + struct http_header { char *header; char *value; @@ -35,11 +43,65 @@ struct http_header { struct http_arg { char *name; - char *value; + void *value; + u_int32_t len; + + char *s_value; + u_int32_t s_len; TAILQ_ENTRY(http_arg) list; }; +#define COPY_ARG_TYPE(v, l, t, o) \ + do { \ + if (l != NULL) \ + *l = sizeof(t); \ + *(t **)o = *(t **)v; \ + } while (0); + +#define COPY_ARG_INT(min, max, type) \ + do { \ + int err; \ + int64_t nval; \ + nval = kore_strtonum(q->s_value, 10, min, max, &err); \ + if (err != KORE_RESULT_OK) \ + return (KORE_RESULT_ERROR); \ + COPY_ARG_TYPE(&nval, len, type, out); \ + } while (0); + +#define CACHE_STRING() \ + do { \ + if (q->s_value == NULL) { \ + q->s_len = q->len + 1; \ + q->s_value = kore_malloc(q->s_len); \ + kore_strlcpy(q->s_value, q->value, q->s_len); \ + } \ + } while (0); + +#define COPY_AS_INTTYPE(min, max, type) \ + do { \ + CACHE_STRING(); \ + COPY_ARG_INT(min, max, type); \ + } while (0); + +#define http_argument_type(r, n, o, l, t) \ + http_argument_get(r, n, (void **)o, l, t) + +#define http_argument_get_string(n, o, l) \ + http_argument_type(req, n, o, l, HTTP_ARG_TYPE_STRING) + +#define http_argument_get_uint16(n, o) \ + http_argument_type(req, n, o, NULL, HTTP_ARG_TYPE_UINT16) + +#define http_argument_get_int16(n, o) \ + http_argument_type(req, n, o, NULL, HTTP_ARG_TYPE_INT16) + +#define http_argument_get_uint32(n, o) \ + http_argument_type(req, n, o, NULL, HTTP_ARG_TYPE_UINT32) + +#define http_argument_get_int32(n, o) \ + http_argument_type(req, n, o, NULL, HTTP_ARG_TYPE_INT32) + struct http_file { char *name; char *filename; @@ -55,22 +117,22 @@ struct http_file { #define HTTP_REQUEST_COMPLETE 0x01 #define HTTP_REQUEST_DELETE 0x02 -#define HTTP_REQUEST_PARSED_PARAMS 0x04 struct http_request { - u_int8_t method; - u_int8_t flags; - int status; - u_int64_t start; - u_int64_t end; - char host[KORE_DOMAINNAME_LEN]; - char path[HTTP_URI_LEN]; - char *agent; - struct connection *owner; - struct spdy_stream *stream; - struct kore_buf *post_data; - void *hdlr_extra; - u_int8_t *multipart_body; + u_int8_t method; + u_int8_t flags; + int status; + u_int64_t start; + u_int64_t end; + char host[KORE_DOMAINNAME_LEN]; + char path[HTTP_URI_LEN]; + char *agent; + struct connection *owner; + struct spdy_stream *stream; + struct kore_buf *post_data; + void *hdlr_extra; + u_int8_t *multipart_body; + struct kore_module_handle *hdlr; TAILQ_HEAD(, http_header) req_headers; TAILQ_HEAD(, http_header) resp_headers; @@ -100,24 +162,26 @@ int http_request_new(struct connection *, struct spdy_stream *, int http_argument_urldecode(char *); int http_header_recv(struct netbuf *); int http_generic_404(struct http_request *); -char *http_post_data_text(struct http_request *); -u_int8_t *http_post_data_bytes(struct http_request *, u_int32_t *); int http_populate_arguments(struct http_request *); int http_populate_multipart_form(struct http_request *, int *); -void http_argument_multiple_free(struct http_arg *); -void http_file_add(struct http_request *, char *, char *, - u_int8_t *, u_int32_t); -void http_argument_add(struct http_request *, char *, - char *, u_int32_t); -int http_argument_lookup(struct http_request *, - const char *, char **); +int http_argument_get(struct http_request *, + const char *, void **, u_int32_t *, int); int http_file_lookup(struct http_request *, char *, char **, u_int8_t **, u_int32_t *); -int http_argument_multiple_lookup(struct http_request *, - struct http_arg *); void kore_accesslog(struct http_request *); +static inline char * +http_argument_string(struct http_request *r, char *n) +{ + char *str; + + if (http_argument_get(r, n, (void **)&str, NULL, HTTP_ARG_TYPE_STRING)) + return (str); + + return (NULL); +} + enum http_status_code { HTTP_STATUS_CONTINUE = 100, HTTP_STATUS_SWITCHING_PROTOCOLS = 101, diff --git a/includes/kore.h b/includes/kore.h @@ -349,7 +349,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 **); -u_int64_t kore_strtonum(const char *, int, u_int64_t, u_int64_t, int *); +long long kore_strtonum(const char *, int, long long, long long, 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/modules/example/src/example.c b/modules/example/src/example.c @@ -140,10 +140,8 @@ serve_file_upload(struct http_request *req) if (req->method == HTTP_METHOD_POST) { http_populate_multipart_form(req, &r); - if (http_argument_lookup(req, "firstname", &name)) { - kore_buf_replace_string(b, "$firstname$", - name, strlen(name)); - kore_mem_free(name); + if (http_argument_get_string("firstname", &name, &len)) { + kore_buf_replace_string(b, "$firstname$", name, len); } else { kore_buf_replace_string(b, "$firstname$", NULL, 0); } @@ -237,22 +235,22 @@ serve_params_test(struct http_request *req) int r, i; char *test, name[10]; + http_populate_arguments(req); + b = kore_buf_create(static_len_html_params); kore_buf_append(b, static_html_params, static_len_html_params); /* * The GET parameters will be filtered out on POST. */ - if (http_argument_lookup(req, "arg1", &test)) { - kore_buf_replace_string(b, "$arg1$", test, strlen(test)); - kore_mem_free(test); + if (http_argument_get_string("arg1", &test, &len)) { + kore_buf_replace_string(b, "$arg1$", test, len); } else { kore_buf_replace_string(b, "$arg1$", NULL, 0); } - if (http_argument_lookup(req, "arg2", &test)) { - kore_buf_replace_string(b, "$arg2$", test, strlen(test)); - kore_mem_free(test); + if (http_argument_get_string("arg2", &test, &len)) { + kore_buf_replace_string(b, "$arg2$", test, len); } else { kore_buf_replace_string(b, "$arg2$", NULL, 0); } @@ -262,6 +260,11 @@ serve_params_test(struct http_request *req) kore_buf_replace_string(b, "$test2$", NULL, 0); kore_buf_replace_string(b, "$test3$", NULL, 0); + if (http_argument_get_uint16("id", &r)) + kore_log(LOG_NOTICE, "id: %d", r); + else + kore_log(LOG_NOTICE, "No id set"); + http_response_header_add(req, "content-type", "text/html"); d = kore_buf_release(b, &len); r = http_response(req, 200, d, len); @@ -270,16 +273,11 @@ serve_params_test(struct http_request *req) return (r); } - /* - * No need to call http_populate_arguments(), it's been done - * by the parameter validation automatically. - */ for (i = 1; i < 4; i++) { snprintf(name, sizeof(name), "test%d", i); - if (http_argument_lookup(req, name, &test)) { + if (http_argument_get_string(name, &test, &len)) { snprintf(name, sizeof(name), "$test%d$", i); - kore_buf_replace_string(b, name, test, strlen(test)); - kore_mem_free(test); + kore_buf_replace_string(b, name, test, len); } else { snprintf(name, sizeof(name), "$test%d$", i); kore_buf_replace_string(b, name, NULL, 0); diff --git a/src/http.c b/src/http.c @@ -25,8 +25,13 @@ static char *http_status_text(int); static int http_post_data_recv(struct netbuf *); -static void http_validate_params(struct http_request *, - struct kore_module_handle *); +static char *http_post_data_text(struct http_request *); +static u_int8_t *http_post_data_bytes(struct http_request *, + u_int32_t *); +static void http_argument_add(struct http_request *, char *, + void *, u_int32_t, int); +static void http_file_add(struct http_request *, char *, char *, + u_int8_t *, u_int32_t); static TAILQ_HEAD(, http_request) http_requests; static struct kore_pool http_request_pool; @@ -141,12 +146,7 @@ http_process(void) if (hdlr == NULL) { r = http_generic_404(req); } else { - if (!TAILQ_EMPTY(&(hdlr->params)) && - !(req->flags & HTTP_REQUEST_PARSED_PARAMS)) { - http_validate_params(req, hdlr); - req->flags |= HTTP_REQUEST_PARSED_PARAMS; - } - + req->hdlr = hdlr; cb = hdlr->addr; worker->active_hdlr = hdlr; r = cb(req); @@ -225,6 +225,9 @@ http_request_free(struct http_request *req) if (q->value != NULL) kore_mem_free(q->value); + if (q->s_value != NULL) + kore_mem_free(q->s_value); + kore_mem_free(q); } @@ -533,13 +536,12 @@ http_populate_arguments(struct http_request *req) continue; } - if (val[1] == NULL) - len = 0; - else + if (val[1] != NULL) { len = strlen(val[1]); - - http_argument_add(req, val[0], val[1], len); - count++; + http_argument_add(req, val[0], val[1], + len, HTTP_ARG_TYPE_STRING); + count++; + } } kore_mem_free(query); @@ -547,17 +549,46 @@ http_populate_arguments(struct http_request *req) } int -http_argument_lookup(struct http_request *req, const char *name, char **out) +http_argument_get(struct http_request *req, const char *name, + void **out, u_int32_t *len, int type) { struct http_arg *q; + if (len != NULL) + *len = 0; + TAILQ_FOREACH(q, &(req->arguments), list) { if (!strcmp(q->name, name)) { - if (q->value == NULL) + switch (type) { + case HTTP_ARG_TYPE_RAW: + if (len != NULL) + *len = q->len; + *out = q->value; + return (KORE_RESULT_OK); + case HTTP_ARG_TYPE_BYTE: + COPY_ARG_TYPE(q->value, len, u_int8_t, out); + return (KORE_RESULT_OK); + case HTTP_ARG_TYPE_INT16: + COPY_AS_INTTYPE(SHRT_MIN, SHRT_MAX, int16_t); + return (KORE_RESULT_OK); + case HTTP_ARG_TYPE_UINT16: + COPY_AS_INTTYPE(0, USHRT_MAX, u_int16_t); + return (KORE_RESULT_OK); + case HTTP_ARG_TYPE_INT32: + COPY_AS_INTTYPE(INT_MIN, INT_MAX, int32_t); + return (KORE_RESULT_OK); + case HTTP_ARG_TYPE_UINT32: + COPY_AS_INTTYPE(0, UINT_MAX, u_int32_t); + return (KORE_RESULT_OK); + case HTTP_ARG_TYPE_STRING: + CACHE_STRING(); + *out = q->s_value; + if (len != NULL) + *len = q->s_len - 1; + return (KORE_RESULT_OK); + default: return (KORE_RESULT_ERROR); - - *out = kore_strdup(q->value); - return (KORE_RESULT_OK); + } } } @@ -613,69 +644,6 @@ http_argument_urldecode(char *arg) } int -http_argument_multiple_lookup(struct http_request *req, struct http_arg *args) -{ - int i, c; - - c = 0; - for (i = 0; args[i].name != NULL; i++) { - if (!http_argument_lookup(req, args[i].name, - &(args[i].value))) { - args[i].value = NULL; - } else { - c++; - } - } - - return (c); -} - -void -http_argument_multiple_free(struct http_arg *args) -{ - int i; - - for (i = 0; args[i].name != NULL; i++) { - if (args[i].value != NULL) - kore_mem_free(args[i].value); - } -} - -void -http_argument_add(struct http_request *req, char *name, - char *value, u_int32_t len) -{ - struct http_arg *q; - - q = kore_malloc(sizeof(struct http_arg)); - q->name = kore_strdup(name); - - if (len > 0) { - q->value = kore_malloc(len + 1); - kore_strlcpy(q->value, value, len + 1); - } else { - q->value = NULL; - } - - TAILQ_INSERT_TAIL(&(req->arguments), q, list); -} - -void -http_file_add(struct http_request *req, char *name, char *filename, - u_int8_t *data, u_int32_t len) -{ - struct http_file *f; - - f = kore_malloc(sizeof(struct http_file)); - f->len = len; - f->data = data; - f->name = kore_strdup(name); - f->filename = kore_strdup(filename); - - TAILQ_INSERT_TAIL(&(req->files), f, list); -} - -int http_file_lookup(struct http_request *req, char *name, char **fname, u_int8_t **data, u_int32_t *len) { @@ -798,7 +766,7 @@ http_populate_multipart_form(struct http_request *req, int *v) if (opt[2] == NULL) { *v = *v + 1; http_argument_add(req, name, - (char *)data, (e - 2) - data); + data, (e - 2) - data, HTTP_ARG_TYPE_STRING); kore_mem_free(name); continue; } @@ -837,42 +805,71 @@ http_populate_multipart_form(struct http_request *req, int *v) return (KORE_RESULT_OK); } -char * -http_post_data_text(struct http_request *req) +int +http_generic_404(struct http_request *req) { - u_int32_t len; - u_int8_t *data; - char *text; - - data = kore_buf_release(req->post_data, &len); - req->post_data = NULL; - len++; - - text = kore_malloc(len); - kore_strlcpy(text, (char *)data, len); - kore_mem_free(data); + kore_debug("http_generic_404(%s, %d, %s)", + req->host, req->method, req->path); - return (text); + return (http_response(req, 404, NULL, 0)); } -u_int8_t * -http_post_data_bytes(struct http_request *req, u_int32_t *len) +static void +http_argument_add(struct http_request *req, char *name, + void *value, u_int32_t len, int type) { - u_int8_t *data; + struct http_arg *q; + struct kore_handler_params *p; - data = kore_buf_release(req->post_data, len); - req->post_data = NULL; + if (len == 0 || value == NULL) { + kore_debug("http_argument_add: with NULL value"); + return; + } - return (data); + TAILQ_FOREACH(p, &(req->hdlr->params), list) { + if (p->method != req->method) + continue; + + if (!strcmp(p->name, name)) { + if (type == HTTP_ARG_TYPE_STRING) { + http_argument_urldecode(value); + len = strlen(value); + } + + if (!kore_validator_check(p->validator, value)) { + kore_log(LOG_NOTICE, + "validator %s (%s) for %s failed", + p->validator->name, p->name, req->path); + } else { + q = kore_malloc(sizeof(struct http_arg)); + q->len = len; + q->s_value = NULL; + q->name = kore_strdup(name); + q->value = kore_malloc(len); + memcpy(q->value, value, len); + TAILQ_INSERT_TAIL(&(req->arguments), q, list); + } + + return; + } + } + + kore_log(LOG_NOTICE, "unexpected parameter %s for %s", name, req->path); } -int -http_generic_404(struct http_request *req) +static void +http_file_add(struct http_request *req, char *name, char *filename, + u_int8_t *data, u_int32_t len) { - kore_debug("http_generic_404(%s, %d, %s)", - req->host, req->method, req->path); + struct http_file *f; - return (http_response(req, 404, NULL, 0)); + f = kore_malloc(sizeof(struct http_file)); + f->len = len; + f->data = data; + f->name = kore_strdup(name); + f->filename = kore_strdup(filename); + + TAILQ_INSERT_TAIL(&(req->files), f, list); } static int @@ -888,53 +885,33 @@ http_post_data_recv(struct netbuf *nb) return (KORE_RESULT_OK); } -static void -http_validate_params(struct http_request *req, struct kore_module_handle *hdlr) +static char * +http_post_data_text(struct http_request *req) { - int r; - struct kore_handler_params *p; - struct http_arg *q, *next; + u_int32_t len; + u_int8_t *data; + char *text; - if (!http_populate_multipart_form(req, &r)) - http_populate_arguments(req); + data = kore_buf_release(req->post_data, &len); + req->post_data = NULL; + len++; - for (q = TAILQ_FIRST(&(req->arguments)); q != NULL; q = next) { - next = TAILQ_NEXT(q, list); + text = kore_malloc(len); + kore_strlcpy(text, (char *)data, len); + kore_mem_free(data); - p = NULL; - TAILQ_FOREACH(p, &(hdlr->params), list) { - if (p->method != req->method) - continue; - if (!strcmp(p->name, q->name)) - break; - } + return (text); +} - if (q->value != NULL) - http_argument_urldecode(q->value); +static u_int8_t * +http_post_data_bytes(struct http_request *req, u_int32_t *len) +{ + u_int8_t *data; - r = KORE_RESULT_ERROR; - if (p != NULL && q->value != NULL) { - r = kore_validator_check(p->validator, q->value); - if (r != KORE_RESULT_OK) { - kore_log(LOG_NOTICE, - "validator %s(%s) for %s failed", - p->validator->name, p->name, req->path); - } - } else if (p == NULL) { - kore_log(LOG_NOTICE, - "received unexpected parameter %s for %s", - q->name, req->path); - } + data = kore_buf_release(req->post_data, len); + req->post_data = NULL; - if (r == KORE_RESULT_ERROR) { - TAILQ_REMOVE(&(req->arguments), q, list); - kore_mem_free(q->name); - if (q->value != NULL) - kore_mem_free(q->value); - kore_mem_free(q); - continue; - } - } + return (data); } static char * diff --git a/src/utils.c b/src/utils.c @@ -86,10 +86,10 @@ kore_strlcpy(char *dst, const char *src, size_t len) } } -u_int64_t -kore_strtonum(const char *str, int base, u_int64_t min, u_int64_t max, int *err) +long long +kore_strtonum(const char *str, int base, long long min, long long max, int *err) { - u_int64_t l; + long long l; char *ep; if (min > max) {