From b95b623e72329bd2800654e78eea1e39422dc8ab Mon Sep 17 00:00:00 2001 From: Joris Vink Date: Tue, 16 Jan 2018 18:47:50 +0100 Subject: [PATCH] Allow param blocks to be marked as "querystring" Before params get would mean querystring and anything else would just count toward a www-encoded body. Now you can prefix the params block with "qs" indicating that those configured parameters are allowed to occur in the query string regardless of the method used. This means you can do something like: params qs:post /uri { ... } to specify what the allowed parameters are in the querystring for a POST request towards /uri. inspired by and properly fixes #205. --- conf/kore.conf.example | 10 +++++++ examples/integers/conf/integers.conf | 3 +- examples/ktunnel/conf/ktunnel.conf | 2 +- examples/parameters/conf/parameters.conf | 2 +- examples/python/conf/python.conf | 4 +-- examples/tasks/conf/tasks.conf | 2 +- includes/http.h | 5 +++- includes/kore.h | 3 ++ src/config.c | 35 ++++++++++++++++++------ src/http.c | 19 ++++++++----- 10 files changed, 63 insertions(+), 22 deletions(-) diff --git a/conf/kore.conf.example b/conf/kore.conf.example index a642ddb..8b56b8d 100644 --- a/conf/kore.conf.example +++ b/conf/kore.conf.example @@ -246,6 +246,16 @@ domain localhost { validate arg1 v_example validate id v_number } + + # Configure a params block for allowed parameters in the + # querystring when performing a POST against /params-test. + # You do this by prefixing the method with the qs: marker. + # In the param blocks below we allow the parameter "post_id" + # in the querystring validated by v_number when a POST is + # done against the supplied URL. + params qs:post /params-test { + validate post_id v_number + } } #domain domain.com { diff --git a/examples/integers/conf/integers.conf b/examples/integers/conf/integers.conf index 517512f..2b66b18 100755 --- a/examples/integers/conf/integers.conf +++ b/examples/integers/conf/integers.conf @@ -15,7 +15,8 @@ domain * { certkey cert/key.pem static / page - params get / { + # allowed parameters in the query string for GETs + params qs:get / { validate id v_id } } diff --git a/examples/ktunnel/conf/ktunnel.conf b/examples/ktunnel/conf/ktunnel.conf index 6d117a9..7b79a5a 100755 --- a/examples/ktunnel/conf/ktunnel.conf +++ b/examples/ktunnel/conf/ktunnel.conf @@ -18,7 +18,7 @@ domain * { static /connect open_connection - params get /connect { + params qs:get /connect { validate host v_host validate port v_port } diff --git a/examples/parameters/conf/parameters.conf b/examples/parameters/conf/parameters.conf index 3c313e4..acc630d 100755 --- a/examples/parameters/conf/parameters.conf +++ b/examples/parameters/conf/parameters.conf @@ -21,7 +21,7 @@ domain * { # If you would want to declare parameters available # to the page handler for POST, swap the 'get' setting # to 'post' instead, Kore takes care of the rest. - params get / { + params qs:get / { # Validate the id parameter with the v_id validator. validate id v_id } diff --git a/examples/python/conf/python.conf b/examples/python/conf/python.conf index 1de0c73..76c6ef9 100644 --- a/examples/python/conf/python.conf +++ b/examples/python/conf/python.conf @@ -44,14 +44,14 @@ domain * { # # On the native page handler, use a python validator. # - params get /c { + params qs:get /c { validate id v_p_id } # # On the python page handler, use a native validator. # - params get / { + params qs:get / { validate id v_id } } diff --git a/examples/tasks/conf/tasks.conf b/examples/tasks/conf/tasks.conf index f428733..4d29274 100644 --- a/examples/tasks/conf/tasks.conf +++ b/examples/tasks/conf/tasks.conf @@ -19,7 +19,7 @@ domain * { static / page_handler static /post_back post_back - params get / { + params qs:get / { validate user v_user } diff --git a/includes/http.h b/includes/http.h index 068f7e1..1194339 100644 --- a/includes/http.h +++ b/includes/http.h @@ -26,6 +26,9 @@ extern "C" { #endif +/* Keep the http_populate_get symbol around. */ +#define http_populate_get http_populate_qs + #define HTTP_KEEPALIVE_TIME 20 #define HTTP_HSTS_ENABLE 31536000 #define HTTP_HEADER_MAX_LEN 4096 @@ -282,7 +285,7 @@ void *http_state_create(struct http_request *, size_t); int http_argument_urldecode(char *); int http_header_recv(struct netbuf *); -void http_populate_get(struct http_request *); +void http_populate_qs(struct http_request *); void http_populate_post(struct http_request *); void http_populate_multipart_form(struct http_request *); void http_populate_cookies(struct http_request *); diff --git a/includes/kore.h b/includes/kore.h index a14ce0e..93caf46 100644 --- a/includes/kore.h +++ b/includes/kore.h @@ -255,8 +255,11 @@ LIST_HEAD(listener_head, listener); #if !defined(KORE_NO_HTTP) +#define KORE_PARAMS_QUERY_STRING 0x0001 + struct kore_handler_params { char *name; + int flags; u_int8_t method; struct kore_validator *validator; diff --git a/src/config.c b/src/config.c index 6a6a6eb..51fe81f 100644 --- a/src/config.c +++ b/src/config.c @@ -178,6 +178,7 @@ char *config_file = NULL; #if !defined(KORE_NO_HTTP) static u_int8_t current_method = 0; +static int current_flags = 0; static struct kore_auth *current_auth = NULL; static struct kore_module_handle *current_handler = NULL; #endif @@ -240,6 +241,8 @@ kore_parse_config_file(const char *fpath) #if !defined(KORE_NO_HTTP) if (!strcmp(p, "}") && current_handler != NULL) { lineno++; + current_flags = 0; + current_method = 0; current_handler = NULL; continue; } @@ -734,7 +737,7 @@ static int configure_params(char *options) { struct kore_module_handle *hdlr; - char *argv[3]; + char *method, *argv[3]; if (current_domain == NULL) { printf("params not used in domain context\n"); @@ -750,21 +753,36 @@ configure_params(char *options) if (argv[1] == NULL) return (KORE_RESULT_ERROR); - if (!strcasecmp(argv[0], "post")) { + if ((method = strchr(argv[0], ':')) != NULL) { + *(method)++ = '\0'; + if (!strcasecmp(argv[0], "qs")) { + current_flags = KORE_PARAMS_QUERY_STRING; + } else { + printf("unknown prefix '%s' for '%s'\n", + argv[0], argv[1]); + return (KORE_RESULT_ERROR); + } + } else { + method = argv[0]; + } + + if (!strcasecmp(method, "post")) { current_method = HTTP_METHOD_POST; - } else if (!strcasecmp(argv[0], "get")) { + } else if (!strcasecmp(method, "get")) { current_method = HTTP_METHOD_GET; - } else if (!strcasecmp(argv[0], "put")) { + /* Let params get /foo {} imply qs:get automatically. */ + current_flags |= KORE_PARAMS_QUERY_STRING; + } else if (!strcasecmp(method, "put")) { current_method = HTTP_METHOD_PUT; - } else if (!strcasecmp(argv[0], "delete")) { + } else if (!strcasecmp(method, "delete")) { current_method = HTTP_METHOD_DELETE; - } else if (!strcasecmp(argv[0], "head")) { + } else if (!strcasecmp(method, "head")) { current_method = HTTP_METHOD_HEAD; - } else if (!strcasecmp(argv[0], "patch")) { + } else if (!strcasecmp(method, "patch")) { current_method = HTTP_METHOD_PATCH; } else { printf("unknown method: %s in params block for %s\n", - argv[0], argv[1]); + method, argv[1]); return (KORE_RESULT_ERROR); } @@ -806,6 +824,7 @@ configure_validate(char *options) p = kore_malloc(sizeof(*p)); p->validator = val; + p->flags = current_flags; p->method = current_method; p->name = kore_strdup(argv[0]); diff --git a/src/http.c b/src/http.c index 2805bf5..1908e42 100644 --- a/src/http.c +++ b/src/http.c @@ -44,7 +44,7 @@ static int http_body_recv(struct netbuf *); static void http_error_response(struct connection *, int); static void http_write_response_cookie(struct http_cookie *); -static void http_argument_add(struct http_request *, char *, char *); +static void http_argument_add(struct http_request *, char *, char *, int); static void http_response_normal(struct http_request *, struct connection *, int, const void *, size_t); static void multipart_add_field(struct http_request *, struct kore_buf *, @@ -1124,7 +1124,7 @@ http_populate_post(struct http_request *req) for (i = 0; i < v; i++) { kore_split_string(args[i], "=", val, 3); if (val[0] != NULL && val[1] != NULL) - http_argument_add(req, val[0], val[1]); + http_argument_add(req, val[0], val[1], 0); } out: @@ -1133,12 +1133,12 @@ out: } void -http_populate_get(struct http_request *req) +http_populate_qs(struct http_request *req) { int i, v; char *query, *args[HTTP_MAX_QUERY_ARGS], *val[3]; - if (req->method != HTTP_METHOD_GET || req->query_string == NULL) + if (req->query_string == NULL) return; query = kore_strdup(req->query_string); @@ -1146,7 +1146,7 @@ http_populate_get(struct http_request *req) for (i = 0; i < v; i++) { kore_split_string(args[i], "=", val, 3); if (val[0] != NULL && val[1] != NULL) - http_argument_add(req, val[0], val[1]); + http_argument_add(req, val[0], val[1], 1); } kore_free(query); @@ -1496,7 +1496,7 @@ multipart_add_field(struct http_request *req, struct kore_buf *in, data->offset -= 2; string = kore_buf_stringify(data, NULL); - http_argument_add(req, name, string); + http_argument_add(req, name, string, 0); kore_buf_free(data); } @@ -1527,7 +1527,7 @@ multipart_file_add(struct http_request *req, struct kore_buf *in, } static void -http_argument_add(struct http_request *req, char *name, char *value) +http_argument_add(struct http_request *req, char *name, char *value, int qs) { struct http_arg *q; struct kore_handler_params *p; @@ -1535,6 +1535,11 @@ http_argument_add(struct http_request *req, char *name, char *value) http_argument_urldecode(name); TAILQ_FOREACH(p, &(req->hdlr->params), list) { + if (qs == 1 && !(p->flags & KORE_PARAMS_QUERY_STRING)) + continue; + if (qs == 0 && (p->flags & KORE_PARAMS_QUERY_STRING)) + continue; + if (p->method != req->method) continue;