From 7d1e41f9c51bc1525e5b0c86b9832562d65675c1 Mon Sep 17 00:00:00 2001 From: Xavier Del Campo Romero Date: Sun, 30 Apr 2023 23:43:10 +0200 Subject: [PATCH] http.c: Decode URL resource and parameters separately Given the following contrived example request: /example%FB%DC&arg%DE1=examplevalue%AA slcl must decode each token separately, so that percent-encoded characters '&', '=' or '?' do not get accidently intepreted. --- http.c | 70 ++++++++++++++++++++++++++++++++++++---------------------- http.h | 2 +- main.c | 13 +++-------- 3 files changed, 48 insertions(+), 37 deletions(-) diff --git a/http.c b/http.c index 26c30bf..501499b 100644 --- a/http.c +++ b/http.c @@ -126,18 +126,13 @@ static size_t chrcnt(const char *s, const int c) return ret; } -static void arg_decode(char *s) -{ - while ((s = strchr(s, '+'))) - *s = ' '; -} - static int parse_arg(struct ctx *const c, const char *const arg, const size_t n) { int ret = -1; struct http_arg a = {0}, *args = NULL; const char *sep = memchr(arg, '=', n); + char *enckey = NULL, *encvalue = NULL; if (!sep) { @@ -163,25 +158,41 @@ static int parse_arg(struct ctx *const c, const char *const arg, const size_t keylen = sep - arg, valuelen = n - keylen - 1; - a = (const struct http_arg) - { - .key = strndup(arg, keylen), - .value = strndup(value, valuelen) - }; - - if (!a.key || !a.value) + if (!(enckey = strndup(arg, keylen))) { fprintf(stderr, "%s: strndup(3) key: %s\n", __func__, strerror(errno)); goto end; } + else if (!(encvalue = strndup(value, valuelen))) + { + fprintf(stderr, "%s: strndup(3) value: %s\n", + __func__, strerror(errno)); + goto end; + } + + /* URL parameters use '+' for whitespace, rather than %20. */ + a = (const struct http_arg) + { + .key = http_decode_url(enckey, true), + .value = http_decode_url(encvalue, true) + }; + + if (!a.key) + { + fprintf(stderr, "%s: http_decode_url key failed\n", __func__); + goto end; + } + else if (!a.value) + { + fprintf(stderr, "%s: http_decode_url value failed\n", __func__); + goto end; + } else if (!(args = realloc(c->args, (c->n_args + 1) * sizeof *args))) { fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno)); goto end; } - arg_decode(a.key); - arg_decode(a.value); args[c->n_args++] = a; c->args = args; ret = 0; @@ -190,6 +201,8 @@ end: if (ret) arg_free(&a); + free(enckey); + free(encvalue); return ret; } @@ -294,28 +307,28 @@ static int parse_resource(struct ctx *const c, const char *const enc_res) size_t reslen; char *trimmed_encres = NULL, *resource = NULL; - if (!(resource = http_decode_url(enc_res))) - { - fprintf(stderr, "%s: http_decode_url failed\n", __func__); - goto end; - } - else if ((error = parse_args(c, resource, &reslen))) + if ((error = parse_args(c, enc_res, &reslen))) { fprintf(stderr, "%s: parse_args failed\n", __func__); ret = error; goto end; } - else if (!(trimmed_encres = strndup(resource, reslen))) + else if (!(trimmed_encres = strndup(enc_res, reslen))) { fprintf(stderr, "%s: strndup(3): %s\n", __func__, strerror(errno)); goto end; } + else if (!(resource = http_decode_url(trimmed_encres, false))) + { + fprintf(stderr, "%s: http_decode_url failed\n", __func__); + goto end; + } - c->resource = trimmed_encres; + c->resource = resource; ret = 0; end: - free(resource); + free(trimmed_encres); return ret; } @@ -1874,7 +1887,7 @@ failure: return NULL; } -char *http_decode_url(const char *url) +char *http_decode_url(const char *url, const bool spaces) { char *ret = NULL; size_t n = 0; @@ -1892,7 +1905,12 @@ char *http_decode_url(const char *url) ret = r; - if (*url != '%') + if (spaces && *url == '+') + { + ret[n++] = ' '; + url++; + } + else if (*url != '%') ret[n++] = *url++; else if (*(url + 1) && *(url + 2)) { diff --git a/http.h b/http.h index 8b3a286..ac1f51a 100644 --- a/http.h +++ b/http.h @@ -101,6 +101,6 @@ int http_response_add_header(struct http_response *r, const char *header, const char *value); char *http_cookie_create(const char *key, const char *value); char *http_encode_url(const char *url); -char *http_decode_url(const char *url); +char *http_decode_url(const char *url, bool spaces); #endif /* HTTP_H */ diff --git a/main.c b/main.c index c7481d0..4a9dfa5 100644 --- a/main.c +++ b/main.c @@ -150,10 +150,11 @@ static int append_form(struct form **const forms, const char **const s, *forms = fs; f = &(*forms)[(*n)++]; + /* HTML input forms use '+' for whitespace, rather than %20. */ *f = (const struct form) { - .key = http_decode_url(key), - .value = http_decode_url(value) + .key = http_decode_url(key, true), + .value = http_decode_url(value, true) }; if (!f->key || !f->value) @@ -162,14 +163,6 @@ static int append_form(struct form **const forms, const char **const s, goto end; } - /* HTML input forms use '+' for whitespace, rather than %20. */ - { - char *c = f->value; - - while ((c = strchr(c, '+'))) - *c = ' '; - } - *s = end; ret = 0;