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.
This commit is contained in:
Xavier Del Campo Romero 2023-04-30 23:43:10 +02:00
parent 291d951ee1
commit 7d1e41f9c5
Signed by: xavi
GPG Key ID: 84FF3612A9BF43F2
3 changed files with 48 additions and 37 deletions

70
http.c
View File

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

2
http.h
View File

@ -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 */

13
main.c
View File

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