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:
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) {