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.
This commit is contained in:
Joris Vink 2013-12-12 00:58:32 +01:00
parent b4c54f9dc0
commit c7dcdbcd82
5 changed files with 242 additions and 203 deletions

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
}
}
@ -612,69 +643,6 @@ http_argument_urldecode(char *arg)
return (KORE_RESULT_OK);
}
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,7 +805,87 @@ http_populate_multipart_form(struct http_request *req, int *v)
return (KORE_RESULT_OK);
}
char *
int
http_generic_404(struct http_request *req)
{
kore_debug("http_generic_404(%s, %d, %s)",
req->host, req->method, req->path);
return (http_response(req, 404, NULL, 0));
}
static void
http_argument_add(struct http_request *req, char *name,
void *value, u_int32_t len, int type)
{
struct http_arg *q;
struct kore_handler_params *p;
if (len == 0 || value == NULL) {
kore_debug("http_argument_add: with NULL value");
return;
}
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);
}
static 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);
}
static int
http_post_data_recv(struct netbuf *nb)
{
struct http_request *req = (struct http_request *)nb->extra;
kore_buf_append(req->post_data, nb->buf, nb->s_off);
req->flags |= HTTP_REQUEST_COMPLETE;
kore_debug("post complete for request %p", req);
return (KORE_RESULT_OK);
}
static char *
http_post_data_text(struct http_request *req)
{
u_int32_t len;
@ -855,7 +903,7 @@ http_post_data_text(struct http_request *req)
return (text);
}
u_int8_t *
static u_int8_t *
http_post_data_bytes(struct http_request *req, u_int32_t *len)
{
u_int8_t *data;
@ -866,77 +914,6 @@ http_post_data_bytes(struct http_request *req, u_int32_t *len)
return (data);
}
int
http_generic_404(struct http_request *req)
{
kore_debug("http_generic_404(%s, %d, %s)",
req->host, req->method, req->path);
return (http_response(req, 404, NULL, 0));
}
static int
http_post_data_recv(struct netbuf *nb)
{
struct http_request *req = (struct http_request *)nb->extra;
kore_buf_append(req->post_data, nb->buf, nb->s_off);
req->flags |= HTTP_REQUEST_COMPLETE;
kore_debug("post complete for request %p", req);
return (KORE_RESULT_OK);
}
static void
http_validate_params(struct http_request *req, struct kore_module_handle *hdlr)
{
int r;
struct kore_handler_params *p;
struct http_arg *q, *next;
if (!http_populate_multipart_form(req, &r))
http_populate_arguments(req);
for (q = TAILQ_FIRST(&(req->arguments)); q != NULL; q = next) {
next = TAILQ_NEXT(q, list);
p = NULL;
TAILQ_FOREACH(p, &(hdlr->params), list) {
if (p->method != req->method)
continue;
if (!strcmp(p->name, q->name))
break;
}
if (q->value != NULL)
http_argument_urldecode(q->value);
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);
}
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;
}
}
}
static char *
http_status_text(int status)
{

View File

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