Commit b9b20c64 authored by Dmitry Frank's avatar Dmitry Frank Committed by Cesanta Bot

Implement mg_http_parse_header2()

Which is a replacement of (deprecated) `mg_http_parse_header`, but,
similarly to `asprintf`, allocates a new buffer if the client-provided
one is not large enough.

Also use it throughout mongoose code, and thus some header-related
limitations are removed; in particular,
https://github.com/cesanta/mongoose/issues/813 is fixed.

CL: Mongoose Web Server: Deprecate `mg_http_parse_header()` and implement `mg_http_parse_header2()` instead, which allocates a new buffer if the client-provided one is not large enough (similarly to `asprintf`).
CL: Mongoose Web Server: Fix limitations of header value lengths, e.g. when parsing authentication headers such as nonce, etc.

PUBLISHED_FROM=c75b1bbbbdb294ea85075ce69b1368f115fdd1ef
parent a8a7d2cf
...@@ -10,6 +10,7 @@ items: ...@@ -10,6 +10,7 @@ items:
- { name: mg_get_http_var.md } - { name: mg_get_http_var.md }
- { name: mg_http_check_digest_auth.md } - { name: mg_http_check_digest_auth.md }
- { name: mg_http_parse_header.md } - { name: mg_http_parse_header.md }
- { name: mg_http_parse_header2.md }
- { name: mg_http_reverse_proxy.md } - { name: mg_http_reverse_proxy.md }
- { name: mg_http_send_error.md } - { name: mg_http_send_error.md }
- { name: mg_http_send_redirect.md } - { name: mg_http_send_redirect.md }
......
...@@ -7,17 +7,9 @@ signature: | ...@@ -7,17 +7,9 @@ signature: |
size_t buf_size); size_t buf_size);
--- ---
Parses the HTTP header `hdr`. Finds variable `var_name` and stores its value DEPRECATED: use mg_http_parse_header2() instead.
in the buffer `buf`, `buf_size`. Returns 0 if variable not found, non-zero
otherwise.
This function is supposed to parse cookies, authentication headers, etc. Same as mg_http_parse_header2(), but takes buffer as a `char *` (instead of
Example (error handling omitted): `char **`), and thus it cannot allocate a new buffer if the provided one
is not enough, and just returns 0 in that case.
char user[20];
struct mg_str *hdr = mg_get_http_header(hm, "Authorization");
mg_http_parse_header(hdr, "username", user, sizeof(user));
Returns the length of the variable's value. If buffer is not large enough,
or variable not found, 0 is returned.
---
title: "mg_http_parse_header2()"
decl_name: "mg_http_parse_header2"
symbol_kind: "func"
signature: |
int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf,
size_t buf_size);
---
Parses the HTTP header `hdr`. Finds variable `var_name` and stores its value
in the buffer `*buf`, `buf_size`. If the buffer size is not enough,
allocates a buffer of required size and writes it to `*buf`, similar to
asprintf(). The caller should always check whether the buffer was updated,
and free it if so.
This function is supposed to parse cookies, authentication headers, etc.
Example (error handling omitted):
char user_buf[20];
char *user = user_buf;
struct mg_str *hdr = mg_get_http_header(hm, "Authorization");
mg_http_parse_header2(hdr, "username", &user, sizeof(user_buf));
// ... do something useful with user
if (user != user_buf) {
free(user);
}
Returns the length of the variable's value. If variable is not found, 0 is
returned.
...@@ -59,20 +59,29 @@ static int check_pass(const char *user, const char *pass) { ...@@ -59,20 +59,29 @@ static int check_pass(const char *user, const char *pass) {
*/ */
static struct session *get_session(struct http_message *hm) { static struct session *get_session(struct http_message *hm) {
struct mg_str *cookie_header = mg_get_http_header(hm, "cookie"); struct mg_str *cookie_header = mg_get_http_header(hm, "cookie");
if (cookie_header == NULL) return NULL; if (cookie_header == NULL) goto clean;
char ssid[21]; char ssid_buf[21];
if (!mg_http_parse_header(cookie_header, SESSION_COOKIE_NAME, ssid, char *ssid = ssid_buf;
sizeof(ssid))) { struct session *ret = NULL;
return NULL; if (!mg_http_parse_header2(cookie_header, SESSION_COOKIE_NAME, &ssid,
sizeof(ssid_buf))) {
goto clean;
} }
uint64_t sid = strtoull(ssid, NULL, 16); uint64_t sid = strtoull(ssid, NULL, 16);
for (int i = 0; i < NUM_SESSIONS; i++) { int i;
for (i = 0; i < NUM_SESSIONS; i++) {
if (s_sessions[i].id == sid) { if (s_sessions[i].id == sid) {
s_sessions[i].last_used = mg_time(); s_sessions[i].last_used = mg_time();
return &s_sessions[i]; ret = &s_sessions[i];
goto clean;
} }
} }
return NULL;
clean:
if (ssid != ssid_buf) {
free(ssid);
}
return ret;
} }
/* /*
...@@ -91,7 +100,8 @@ static struct session *create_session(const char *user, ...@@ -91,7 +100,8 @@ static struct session *create_session(const char *user,
/* Find first available slot or use the oldest one. */ /* Find first available slot or use the oldest one. */
struct session *s = NULL; struct session *s = NULL;
struct session *oldest_s = s_sessions; struct session *oldest_s = s_sessions;
for (int i = 0; i < NUM_SESSIONS; i++) { int i;
for (i = 0; i < NUM_SESSIONS; i++) {
if (s_sessions[i].id == 0) { if (s_sessions[i].id == 0) {
s = &s_sessions[i]; s = &s_sessions[i];
break; break;
...@@ -176,7 +186,8 @@ static void logout_handler(struct mg_connection *nc, int ev, void *p) { ...@@ -176,7 +186,8 @@ static void logout_handler(struct mg_connection *nc, int ev, void *p) {
/* Cleans up sessions that have been idle for too long. */ /* Cleans up sessions that have been idle for too long. */
void check_sessions(void) { void check_sessions(void) {
double threshold = mg_time() - SESSION_TTL; double threshold = mg_time() - SESSION_TTL;
for (int i = 0; i < NUM_SESSIONS; i++) { int i;
for (i = 0; i < NUM_SESSIONS; i++) {
struct session *s = &s_sessions[i]; struct session *s = &s_sessions[i];
if (s->id != 0 && s->last_used < threshold) { if (s->id != 0 && s->last_used < threshold) {
fprintf(stderr, "Session %" INT64_X_FMT " (%s) closed due to idleness.\n", fprintf(stderr, "Session %" INT64_X_FMT " (%s) closed due to idleness.\n",
......
This diff is collapsed.
...@@ -4647,22 +4647,43 @@ struct mg_str *mg_get_http_header(struct http_message *hm, const char *name); ...@@ -4647,22 +4647,43 @@ struct mg_str *mg_get_http_header(struct http_message *hm, const char *name);
/* /*
* Parses the HTTP header `hdr`. Finds variable `var_name` and stores its value * Parses the HTTP header `hdr`. Finds variable `var_name` and stores its value
* in the buffer `buf`, `buf_size`. Returns 0 if variable not found, non-zero * in the buffer `*buf`, `buf_size`. If the buffer size is not enough,
* otherwise. * allocates a buffer of required size and writes it to `*buf`, similar to
* asprintf(). The caller should always check whether the buffer was updated,
* and free it if so.
* *
* This function is supposed to parse cookies, authentication headers, etc. * This function is supposed to parse cookies, authentication headers, etc.
* Example (error handling omitted): * Example (error handling omitted):
* *
* char user[20]; * char user_buf[20];
* char *user = user_buf;
* struct mg_str *hdr = mg_get_http_header(hm, "Authorization"); * struct mg_str *hdr = mg_get_http_header(hm, "Authorization");
* mg_http_parse_header(hdr, "username", user, sizeof(user)); * mg_http_parse_header2(hdr, "username", &user, sizeof(user_buf));
* // ... do something useful with user
* if (user != user_buf) {
* free(user);
* }
* *
* Returns the length of the variable's value. If buffer is not large enough, * Returns the length of the variable's value. If variable is not found, 0 is
* or variable not found, 0 is returned. * returned.
*/ */
int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf, int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf,
size_t buf_size); size_t buf_size);
/*
* DEPRECATED: use mg_http_parse_header2() instead.
*
* Same as mg_http_parse_header2(), but takes buffer as a `char *` (instead of
* `char **`), and thus it cannot allocate a new buffer if the provided one
* is not enough, and just returns 0 in that case.
*/
int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
size_t buf_size)
#ifdef __GNUC__
__attribute__((deprecated));
#endif
;
/* /*
* Gets and parses the Authorization: Basic header * Gets and parses the Authorization: Basic header
* Returns -1 if no Authorization header is found, or if * Returns -1 if no Authorization header is found, or if
......
This diff is collapsed.
...@@ -31,22 +31,43 @@ struct mg_str *mg_get_http_header(struct http_message *hm, const char *name); ...@@ -31,22 +31,43 @@ struct mg_str *mg_get_http_header(struct http_message *hm, const char *name);
/* /*
* Parses the HTTP header `hdr`. Finds variable `var_name` and stores its value * Parses the HTTP header `hdr`. Finds variable `var_name` and stores its value
* in the buffer `buf`, `buf_size`. Returns 0 if variable not found, non-zero * in the buffer `*buf`, `buf_size`. If the buffer size is not enough,
* otherwise. * allocates a buffer of required size and writes it to `*buf`, similar to
* asprintf(). The caller should always check whether the buffer was updated,
* and free it if so.
* *
* This function is supposed to parse cookies, authentication headers, etc. * This function is supposed to parse cookies, authentication headers, etc.
* Example (error handling omitted): * Example (error handling omitted):
* *
* char user[20]; * char user_buf[20];
* char *user = user_buf;
* struct mg_str *hdr = mg_get_http_header(hm, "Authorization"); * struct mg_str *hdr = mg_get_http_header(hm, "Authorization");
* mg_http_parse_header(hdr, "username", user, sizeof(user)); * mg_http_parse_header2(hdr, "username", &user, sizeof(user_buf));
* // ... do something useful with user
* if (user != user_buf) {
* free(user);
* }
* *
* Returns the length of the variable's value. If buffer is not large enough, * Returns the length of the variable's value. If variable is not found, 0 is
* or variable not found, 0 is returned. * returned.
*/ */
int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf, int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf,
size_t buf_size); size_t buf_size);
/*
* DEPRECATED: use mg_http_parse_header2() instead.
*
* Same as mg_http_parse_header2(), but takes buffer as a `char *` (instead of
* `char **`), and thus it cannot allocate a new buffer if the provided one
* is not enough, and just returns 0 in that case.
*/
int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
size_t buf_size)
#ifdef __GNUC__
__attribute__((deprecated));
#endif
;
/* /*
* Gets and parses the Authorization: Basic header * Gets and parses the Authorization: Basic header
* Returns -1 if no Authorization header is found, or if * Returns -1 if no Authorization header is found, or if
......
...@@ -45,7 +45,9 @@ COMMON_FEATURE_FLAGS = \ ...@@ -45,7 +45,9 @@ COMMON_FEATURE_FLAGS = \
-DMG_ENABLE_SNTP -DMG_SNTP_NO_DELAY_CORRECTION \ -DMG_ENABLE_SNTP -DMG_SNTP_NO_DELAY_CORRECTION \
-DMG_ENABLE_HTTP_STREAMING_MULTIPART -DMG_ENABLE_HTTP_STREAMING_MULTIPART
UNIX_FEATURE_FLAGS=-DMG_ENABLE_IPV6 -DMG_ENABLE_SSL UNIX_FEATURE_FLAGS=-DMG_ENABLE_IPV6 -DMG_ENABLE_SSL
CFLAGS = -W -Wall -Wundef -Werror -g -O0 -Wno-multichar -D__USE_MISC \ # TODO: remove -Wno-deprecated-declarations once deprecated
# `mg_http_parse_header()` is removed from mongoose.
CFLAGS = -W -Wall -Wundef -Werror -Wno-deprecated-declarations -g -O0 -Wno-multichar -D__USE_MISC \
$(COMMON_FEATURE_FLAGS) $(UNIX_FEATURE_FLAGS) \ $(COMMON_FEATURE_FLAGS) $(UNIX_FEATURE_FLAGS) \
$(patsubst %,-I%,$(subst :, ,$(VPATH))) \ $(patsubst %,-I%,$(subst :, ,$(VPATH))) \
-include unit_test.h -pthread $(CFLAGS_EXTRA) -include unit_test.h -pthread $(CFLAGS_EXTRA)
......
...@@ -4941,12 +4941,34 @@ static const char *test_http_parse_header(void) { ...@@ -4941,12 +4941,34 @@ static const char *test_http_parse_header(void) {
"xx=1 kl yy, ert=234 kl=123, " "xx=1 kl yy, ert=234 kl=123, "
"uri=\"/?naii=x,y\";ii=\"12\\\"34\" zz='aa bb',tt=2,gf=\"xx d=1234"); "uri=\"/?naii=x,y\";ii=\"12\\\"34\" zz='aa bb',tt=2,gf=\"xx d=1234");
char buf[20]; char buf[20];
char *buf2;
ASSERT_EQ(mg_http_parse_header(&h, "ert", buf, sizeof(buf)), 3); ASSERT_EQ(mg_http_parse_header(&h, "ert", buf, sizeof(buf)), 3);
ASSERT_STREQ(buf, "234"); ASSERT_STREQ(buf, "234");
ASSERT_EQ(mg_http_parse_header(&h, "ert", buf, 2), 0); ASSERT_EQ(mg_http_parse_header(&h, "ert", buf, 2), 0);
ASSERT_EQ(mg_http_parse_header(&h, "ert", buf, 3), 0); ASSERT_EQ(mg_http_parse_header(&h, "ert", buf, 3), 0);
ASSERT_EQ(mg_http_parse_header(&h, "ert", buf, 4), 3); ASSERT_EQ(mg_http_parse_header(&h, "ert", buf, 4), 3);
buf2 = buf;
ASSERT_EQ(mg_http_parse_header2(&h, "ert", &buf2, 2), 3);
ASSERT(buf2 != buf);
free(buf2);
buf2 = buf;
ASSERT_EQ(mg_http_parse_header2(&h, "ert", &buf2, 3), 3);
ASSERT(buf2 != buf);
free(buf2);
buf2 = buf;
ASSERT_EQ(mg_http_parse_header2(&h, "ert", &buf2, 4), 3);
ASSERT(buf2 == buf);
buf2 = NULL;
ASSERT_EQ(mg_http_parse_header2(&h, "ert", &buf2, 0), 3);
ASSERT_STREQ(buf2, "234");
free(buf2);
ASSERT_EQ(mg_http_parse_header(&h, "gf", buf, sizeof(buf)), 0); ASSERT_EQ(mg_http_parse_header(&h, "gf", buf, sizeof(buf)), 0);
ASSERT_EQ(mg_http_parse_header(&h, "zz", buf, sizeof(buf)), 5); ASSERT_EQ(mg_http_parse_header(&h, "zz", buf, sizeof(buf)), 5);
ASSERT_STREQ(buf, "aa bb"); ASSERT_STREQ(buf, "aa bb");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment