mirror of
https://github.com/cesanta/mongoose.git
synced 2025-06-07 09:27:05 +08:00
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
This commit is contained in:
parent
a8a7d2cf7e
commit
b9b20c6494
@ -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: |
|
|||||||
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.
|
|
||||||
|
|
||||||
|
30
docs/c-api/mg_http_server.h/mg_http_parse_header2.md
Normal file
30
docs/c-api/mg_http_server.h/mg_http_parse_header2.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
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) {
|
|||||||
*/
|
*/
|
||||||
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,
|
|||||||
/* 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) {
|
|||||||
/* 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",
|
||||||
|
238
mongoose.c
238
mongoose.c
@ -5373,6 +5373,86 @@ out:
|
|||||||
/* Amalgamated: #include "mg_internal.h" */
|
/* Amalgamated: #include "mg_internal.h" */
|
||||||
/* Amalgamated: #include "mg_util.h" */
|
/* Amalgamated: #include "mg_util.h" */
|
||||||
|
|
||||||
|
/* altbuf {{{ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Alternate buffer: fills the client-provided buffer with data; and if it's
|
||||||
|
* not large enough, allocates another buffer (via mbuf), similar to asprintf.
|
||||||
|
*/
|
||||||
|
struct altbuf {
|
||||||
|
struct mbuf m;
|
||||||
|
char *user_buf;
|
||||||
|
size_t len;
|
||||||
|
size_t user_buf_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initializes altbuf; `buf`, `buf_size` is the client-provided buffer.
|
||||||
|
*/
|
||||||
|
MG_INTERNAL void altbuf_init(struct altbuf *ab, char *buf, size_t buf_size) {
|
||||||
|
mbuf_init(&ab->m, 0);
|
||||||
|
ab->user_buf = buf;
|
||||||
|
ab->user_buf_size = buf_size;
|
||||||
|
ab->len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Appends a single char to the altbuf.
|
||||||
|
*/
|
||||||
|
MG_INTERNAL void altbuf_append(struct altbuf *ab, char c) {
|
||||||
|
if (ab->len < ab->user_buf_size) {
|
||||||
|
/* The data fits into the original buffer */
|
||||||
|
ab->user_buf[ab->len++] = c;
|
||||||
|
} else {
|
||||||
|
/* The data can't fit into the original buffer, so write it to mbuf. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First of all, see if that's the first byte which overflows the original
|
||||||
|
* buffer: if so, copy the existing data from there to a newly allocated
|
||||||
|
* mbuf.
|
||||||
|
*/
|
||||||
|
if (ab->len > 0 && ab->m.len == 0) {
|
||||||
|
mbuf_append(&ab->m, ab->user_buf, ab->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
mbuf_append(&ab->m, &c, 1);
|
||||||
|
ab->len = ab->m.len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Resets any data previously appended to altbuf.
|
||||||
|
*/
|
||||||
|
MG_INTERNAL void altbuf_reset(struct altbuf *ab) {
|
||||||
|
mbuf_free(&ab->m);
|
||||||
|
ab->len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns whether the additional buffer was allocated (and thus the data
|
||||||
|
* is in the mbuf, not the client-provided buffer)
|
||||||
|
*/
|
||||||
|
MG_INTERNAL int altbuf_reallocated(struct altbuf *ab) {
|
||||||
|
return ab->len > ab->user_buf_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the actual buffer with data, either the client-provided or a newly
|
||||||
|
* allocated one. If `trim` is non-zero, mbuf-backed buffer is trimmed first.
|
||||||
|
*/
|
||||||
|
MG_INTERNAL char *altbuf_get_buf(struct altbuf *ab, int trim) {
|
||||||
|
if (altbuf_reallocated(ab)) {
|
||||||
|
if (trim) {
|
||||||
|
mbuf_trim(&ab->m);
|
||||||
|
}
|
||||||
|
return ab->m.buf;
|
||||||
|
} else {
|
||||||
|
return ab->user_buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
static const char *mg_version_header = "Mongoose/" MG_VERSION;
|
static const char *mg_version_header = "Mongoose/" MG_VERSION;
|
||||||
|
|
||||||
enum mg_http_proto_data_type { DATA_NONE, DATA_FILE, DATA_PUT };
|
enum mg_http_proto_data_type { DATA_NONE, DATA_FILE, DATA_PUT };
|
||||||
@ -6182,7 +6262,8 @@ static void mg_http_multipart_begin(struct mg_connection *nc,
|
|||||||
struct mg_str *ct;
|
struct mg_str *ct;
|
||||||
struct mbuf *io = &nc->recv_mbuf;
|
struct mbuf *io = &nc->recv_mbuf;
|
||||||
|
|
||||||
char boundary[100];
|
char boundary_buf[100];
|
||||||
|
char *boundary = boundary_buf;
|
||||||
int boundary_len;
|
int boundary_len;
|
||||||
|
|
||||||
ct = mg_get_http_header(hm, "Content-Type");
|
ct = mg_get_http_header(hm, "Content-Type");
|
||||||
@ -6197,7 +6278,7 @@ static void mg_http_multipart_begin(struct mg_connection *nc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
boundary_len =
|
boundary_len =
|
||||||
mg_http_parse_header(ct, "boundary", boundary, sizeof(boundary));
|
mg_http_parse_header2(ct, "boundary", &boundary, sizeof(boundary_buf));
|
||||||
if (boundary_len == 0) {
|
if (boundary_len == 0) {
|
||||||
/*
|
/*
|
||||||
* Content type is multipart, but there is no boundary,
|
* Content type is multipart, but there is no boundary,
|
||||||
@ -6234,7 +6315,7 @@ static void mg_http_multipart_begin(struct mg_connection *nc,
|
|||||||
mbuf_remove(io, req_len);
|
mbuf_remove(io, req_len);
|
||||||
}
|
}
|
||||||
exit_mp:
|
exit_mp:
|
||||||
;
|
if (boundary != boundary_buf) MG_FREE(boundary);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CONTENT_DISPOSITION "Content-Disposition: "
|
#define CONTENT_DISPOSITION "Content-Disposition: "
|
||||||
@ -6316,17 +6397,24 @@ static int mg_http_multipart_wait_for_boundary(struct mg_connection *c) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mg_http_parse_header_internal(struct mg_str *hdr,
|
||||||
|
const char *var_name,
|
||||||
|
struct altbuf *ab);
|
||||||
|
|
||||||
static int mg_http_multipart_process_boundary(struct mg_connection *c) {
|
static int mg_http_multipart_process_boundary(struct mg_connection *c) {
|
||||||
int data_size;
|
int data_size;
|
||||||
const char *boundary, *block_begin;
|
const char *boundary, *block_begin;
|
||||||
struct mbuf *io = &c->recv_mbuf;
|
struct mbuf *io = &c->recv_mbuf;
|
||||||
struct mg_http_proto_data *pd = mg_http_get_proto_data(c);
|
struct mg_http_proto_data *pd = mg_http_get_proto_data(c);
|
||||||
char file_name[100], var_name[100];
|
struct altbuf ab_file_name, ab_var_name;
|
||||||
int line_len;
|
int line_len;
|
||||||
boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len);
|
boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len);
|
||||||
block_begin = boundary + pd->mp_stream.boundary_len + 2;
|
block_begin = boundary + pd->mp_stream.boundary_len + 2;
|
||||||
data_size = io->len - (block_begin - io->buf);
|
data_size = io->len - (block_begin - io->buf);
|
||||||
|
|
||||||
|
altbuf_init(&ab_file_name, NULL, 0);
|
||||||
|
altbuf_init(&ab_var_name, NULL, 0);
|
||||||
|
|
||||||
while (data_size > 0 &&
|
while (data_size > 0 &&
|
||||||
(line_len = mg_get_line_len(block_begin, data_size)) != 0) {
|
(line_len = mg_get_line_len(block_begin, data_size)) != 0) {
|
||||||
if (line_len > (int) sizeof(CONTENT_DISPOSITION) &&
|
if (line_len > (int) sizeof(CONTENT_DISPOSITION) &&
|
||||||
@ -6336,11 +6424,16 @@ static int mg_http_multipart_process_boundary(struct mg_connection *c) {
|
|||||||
|
|
||||||
header.p = block_begin + sizeof(CONTENT_DISPOSITION) - 1;
|
header.p = block_begin + sizeof(CONTENT_DISPOSITION) - 1;
|
||||||
header.len = line_len - sizeof(CONTENT_DISPOSITION) - 1;
|
header.len = line_len - sizeof(CONTENT_DISPOSITION) - 1;
|
||||||
mg_http_parse_header(&header, "name", var_name, sizeof(var_name) - 2);
|
|
||||||
mg_http_parse_header(&header, "filename", file_name,
|
altbuf_reset(&ab_var_name);
|
||||||
sizeof(file_name) - 2);
|
mg_http_parse_header_internal(&header, "name", &ab_var_name);
|
||||||
|
|
||||||
|
altbuf_reset(&ab_file_name);
|
||||||
|
mg_http_parse_header_internal(&header, "filename", &ab_file_name);
|
||||||
|
|
||||||
block_begin += line_len;
|
block_begin += line_len;
|
||||||
data_size -= line_len;
|
data_size -= line_len;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6351,10 +6444,16 @@ static int mg_http_multipart_process_boundary(struct mg_connection *c) {
|
|||||||
mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_END, NULL, 0);
|
mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_END, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Reserve 2 bytes for "\r\n" in file_name and var_name */
|
||||||
|
altbuf_append(&ab_file_name, '\0');
|
||||||
|
altbuf_append(&ab_file_name, '\0');
|
||||||
|
altbuf_append(&ab_var_name, '\0');
|
||||||
|
altbuf_append(&ab_var_name, '\0');
|
||||||
|
|
||||||
MG_FREE((void *) pd->mp_stream.file_name);
|
MG_FREE((void *) pd->mp_stream.file_name);
|
||||||
pd->mp_stream.file_name = strdup(file_name);
|
pd->mp_stream.file_name = altbuf_get_buf(&ab_file_name, 1 /* trim */);
|
||||||
MG_FREE((void *) pd->mp_stream.var_name);
|
MG_FREE((void *) pd->mp_stream.var_name);
|
||||||
pd->mp_stream.var_name = strdup(var_name);
|
pd->mp_stream.var_name = altbuf_get_buf(&ab_var_name, 1 /* trim */);
|
||||||
|
|
||||||
mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_BEGIN, NULL, 0);
|
mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_BEGIN, NULL, 0);
|
||||||
pd->mp_stream.state = MPS_WAITING_FOR_CHUNK;
|
pd->mp_stream.state = MPS_WAITING_FOR_CHUNK;
|
||||||
@ -6367,6 +6466,9 @@ static int mg_http_multipart_process_boundary(struct mg_connection *c) {
|
|||||||
|
|
||||||
pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY;
|
pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY;
|
||||||
|
|
||||||
|
altbuf_reset(&ab_var_name);
|
||||||
|
altbuf_reset(&ab_file_name);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6921,14 +7023,12 @@ void mg_printf_html_escape(struct mg_connection *nc, const char *fmt, ...) {
|
|||||||
/* LCOV_EXCL_STOP */
|
/* LCOV_EXCL_STOP */
|
||||||
}
|
}
|
||||||
|
|
||||||
int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
|
static void mg_http_parse_header_internal(struct mg_str *hdr,
|
||||||
size_t buf_size) {
|
const char *var_name,
|
||||||
int ch = ' ', ch1 = ',', len = 0, n = strlen(var_name);
|
struct altbuf *ab) {
|
||||||
|
int ch = ' ', ch1 = ',', n = strlen(var_name);
|
||||||
const char *p, *end = hdr ? hdr->p + hdr->len : NULL, *s = NULL;
|
const char *p, *end = hdr ? hdr->p + hdr->len : NULL, *s = NULL;
|
||||||
|
|
||||||
if (buf != NULL && buf_size > 0) buf[0] = '\0';
|
|
||||||
if (hdr == NULL) return 0;
|
|
||||||
|
|
||||||
/* Find where variable starts */
|
/* Find where variable starts */
|
||||||
for (s = hdr->p; s != NULL && s + n < end; s++) {
|
for (s = hdr->p; s != NULL && s + n < end; s++) {
|
||||||
if ((s == hdr->p || s[-1] == ch || s[-1] == ch1 || s[-1] == ';') &&
|
if ((s == hdr->p || s[-1] == ch || s[-1] == ch1 || s[-1] == ';') &&
|
||||||
@ -6942,19 +7042,51 @@ int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
|
|||||||
ch = ch1 = *s++;
|
ch = ch1 = *s++;
|
||||||
}
|
}
|
||||||
p = s;
|
p = s;
|
||||||
while (p < end && p[0] != ch && p[0] != ch1 && len < (int) buf_size) {
|
while (p < end && p[0] != ch && p[0] != ch1) {
|
||||||
if (ch != ' ' && p[0] == '\\' && p[1] == ch) p++;
|
if (ch != ' ' && p[0] == '\\' && p[1] == ch) p++;
|
||||||
buf[len++] = *p++;
|
altbuf_append(ab, *p++);
|
||||||
}
|
}
|
||||||
if (len >= (int) buf_size || (ch != ' ' && *p != ch)) {
|
|
||||||
len = 0;
|
if (ch != ' ' && *p != ch) {
|
||||||
} else {
|
altbuf_reset(ab);
|
||||||
if (len > 0 && s[len - 1] == ',') len--;
|
|
||||||
if (len > 0 && s[len - 1] == ';') len--;
|
|
||||||
buf[len] = '\0';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If there is some data, append a NUL. */
|
||||||
|
if (ab->len > 0) {
|
||||||
|
altbuf_append(ab, '\0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf,
|
||||||
|
size_t buf_size) {
|
||||||
|
struct altbuf ab;
|
||||||
|
altbuf_init(&ab, *buf, buf_size);
|
||||||
|
if (hdr == NULL) return 0;
|
||||||
|
if (*buf != NULL && buf_size > 0) *buf[0] = '\0';
|
||||||
|
|
||||||
|
mg_http_parse_header_internal(hdr, var_name, &ab);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get a (trimmed) buffer, and return a len without a NUL byte which might
|
||||||
|
* have been added.
|
||||||
|
*/
|
||||||
|
*buf = altbuf_get_buf(&ab, 1 /* trim */);
|
||||||
|
return ab.len > 0 ? ab.len - 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
|
||||||
|
size_t buf_size) {
|
||||||
|
char *buf2 = buf;
|
||||||
|
|
||||||
|
int len = mg_http_parse_header2(hdr, var_name, &buf2, buf_size);
|
||||||
|
|
||||||
|
if (buf2 != buf) {
|
||||||
|
/* Buffer was not enough and was reallocated: free it and just return 0 */
|
||||||
|
MG_FREE(buf2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7092,27 +7224,34 @@ static int mg_check_nonce(const char *nonce) {
|
|||||||
|
|
||||||
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
||||||
FILE *fp) {
|
FILE *fp) {
|
||||||
|
int ret = 0;
|
||||||
struct mg_str *hdr;
|
struct mg_str *hdr;
|
||||||
char username[50], cnonce[64], response[40], uri[200], qop[20], nc[20],
|
char username_buf[50], cnonce_buf[64], response_buf[40], uri_buf[200],
|
||||||
nonce[30];
|
qop_buf[20], nc_buf[20], nonce_buf[16];
|
||||||
|
|
||||||
|
char *username = username_buf, *cnonce = cnonce_buf, *response = response_buf,
|
||||||
|
*uri = uri_buf, *qop = qop_buf, *nc = nc_buf, *nonce = nonce_buf;
|
||||||
|
|
||||||
/* Parse "Authorization:" header, fail fast on parse error */
|
/* Parse "Authorization:" header, fail fast on parse error */
|
||||||
if (hm == NULL || fp == NULL ||
|
if (hm == NULL || fp == NULL ||
|
||||||
(hdr = mg_get_http_header(hm, "Authorization")) == NULL ||
|
(hdr = mg_get_http_header(hm, "Authorization")) == NULL ||
|
||||||
mg_http_parse_header(hdr, "username", username, sizeof(username)) == 0 ||
|
mg_http_parse_header2(hdr, "username", &username, sizeof(username_buf)) ==
|
||||||
mg_http_parse_header(hdr, "cnonce", cnonce, sizeof(cnonce)) == 0 ||
|
0 ||
|
||||||
mg_http_parse_header(hdr, "response", response, sizeof(response)) == 0 ||
|
mg_http_parse_header2(hdr, "cnonce", &cnonce, sizeof(cnonce_buf)) == 0 ||
|
||||||
mg_http_parse_header(hdr, "uri", uri, sizeof(uri)) == 0 ||
|
mg_http_parse_header2(hdr, "response", &response, sizeof(response_buf)) ==
|
||||||
mg_http_parse_header(hdr, "qop", qop, sizeof(qop)) == 0 ||
|
0 ||
|
||||||
mg_http_parse_header(hdr, "nc", nc, sizeof(nc)) == 0 ||
|
mg_http_parse_header2(hdr, "uri", &uri, sizeof(uri_buf)) == 0 ||
|
||||||
mg_http_parse_header(hdr, "nonce", nonce, sizeof(nonce)) == 0 ||
|
mg_http_parse_header2(hdr, "qop", &qop, sizeof(qop_buf)) == 0 ||
|
||||||
|
mg_http_parse_header2(hdr, "nc", &nc, sizeof(nc_buf)) == 0 ||
|
||||||
|
mg_http_parse_header2(hdr, "nonce", &nonce, sizeof(nonce_buf)) == 0 ||
|
||||||
mg_check_nonce(nonce) == 0) {
|
mg_check_nonce(nonce) == 0) {
|
||||||
return 0;
|
ret = 0;
|
||||||
|
goto clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */
|
/* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */
|
||||||
|
|
||||||
return mg_check_digest_auth(
|
ret = mg_check_digest_auth(
|
||||||
hm->method,
|
hm->method,
|
||||||
mg_mk_str_n(
|
mg_mk_str_n(
|
||||||
hm->uri.p,
|
hm->uri.p,
|
||||||
@ -7120,6 +7259,17 @@ int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
|||||||
mg_mk_str(username), mg_mk_str(cnonce), mg_mk_str(response),
|
mg_mk_str(username), mg_mk_str(cnonce), mg_mk_str(response),
|
||||||
mg_mk_str(qop), mg_mk_str(nc), mg_mk_str(nonce), mg_mk_str(auth_domain),
|
mg_mk_str(qop), mg_mk_str(nc), mg_mk_str(nonce), mg_mk_str(auth_domain),
|
||||||
fp);
|
fp);
|
||||||
|
|
||||||
|
clean:
|
||||||
|
if (username != username_buf) MG_FREE(username);
|
||||||
|
if (cnonce != cnonce_buf) MG_FREE(cnonce);
|
||||||
|
if (response != response_buf) MG_FREE(response);
|
||||||
|
if (uri != uri_buf) MG_FREE(uri);
|
||||||
|
if (qop != qop_buf) MG_FREE(qop);
|
||||||
|
if (nc != nc_buf) MG_FREE(nc);
|
||||||
|
if (nonce != nonce_buf) MG_FREE(nonce);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
||||||
@ -8189,8 +8339,24 @@ size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name,
|
|||||||
struct mg_str header;
|
struct mg_str header;
|
||||||
header.p = buf + n + cdl;
|
header.p = buf + n + cdl;
|
||||||
header.len = ll - (cdl + 2);
|
header.len = ll - (cdl + 2);
|
||||||
mg_http_parse_header(&header, "name", var_name, var_name_len);
|
{
|
||||||
mg_http_parse_header(&header, "filename", file_name, file_name_len);
|
char *var_name2 = var_name;
|
||||||
|
mg_http_parse_header2(&header, "name", &var_name2, var_name_len);
|
||||||
|
/* TODO: handle reallocated buffer correctly */
|
||||||
|
if (var_name2 != var_name) {
|
||||||
|
MG_FREE(var_name2);
|
||||||
|
var_name[0] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
char *file_name2 = file_name;
|
||||||
|
mg_http_parse_header2(&header, "filename", &file_name2, file_name_len);
|
||||||
|
/* TODO: handle reallocated buffer correctly */
|
||||||
|
if (file_name2 != file_name) {
|
||||||
|
MG_FREE(file_name2);
|
||||||
|
file_name[0] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
mongoose.h
35
mongoose.h
@ -4647,21 +4647,42 @@ 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_header2(struct mg_str *hdr, const char *var_name, char **buf,
|
||||||
|
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,
|
int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
|
||||||
size_t buf_size);
|
size_t buf_size)
|
||||||
|
#ifdef __GNUC__
|
||||||
|
__attribute__((deprecated));
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gets and parses the Authorization: Basic header
|
* Gets and parses the Authorization: Basic header
|
||||||
|
238
src/mg_http.c
238
src/mg_http.c
@ -9,6 +9,86 @@
|
|||||||
#include "mg_internal.h"
|
#include "mg_internal.h"
|
||||||
#include "mg_util.h"
|
#include "mg_util.h"
|
||||||
|
|
||||||
|
/* altbuf {{{ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Alternate buffer: fills the client-provided buffer with data; and if it's
|
||||||
|
* not large enough, allocates another buffer (via mbuf), similar to asprintf.
|
||||||
|
*/
|
||||||
|
struct altbuf {
|
||||||
|
struct mbuf m;
|
||||||
|
char *user_buf;
|
||||||
|
size_t len;
|
||||||
|
size_t user_buf_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initializes altbuf; `buf`, `buf_size` is the client-provided buffer.
|
||||||
|
*/
|
||||||
|
MG_INTERNAL void altbuf_init(struct altbuf *ab, char *buf, size_t buf_size) {
|
||||||
|
mbuf_init(&ab->m, 0);
|
||||||
|
ab->user_buf = buf;
|
||||||
|
ab->user_buf_size = buf_size;
|
||||||
|
ab->len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Appends a single char to the altbuf.
|
||||||
|
*/
|
||||||
|
MG_INTERNAL void altbuf_append(struct altbuf *ab, char c) {
|
||||||
|
if (ab->len < ab->user_buf_size) {
|
||||||
|
/* The data fits into the original buffer */
|
||||||
|
ab->user_buf[ab->len++] = c;
|
||||||
|
} else {
|
||||||
|
/* The data can't fit into the original buffer, so write it to mbuf. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First of all, see if that's the first byte which overflows the original
|
||||||
|
* buffer: if so, copy the existing data from there to a newly allocated
|
||||||
|
* mbuf.
|
||||||
|
*/
|
||||||
|
if (ab->len > 0 && ab->m.len == 0) {
|
||||||
|
mbuf_append(&ab->m, ab->user_buf, ab->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
mbuf_append(&ab->m, &c, 1);
|
||||||
|
ab->len = ab->m.len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Resets any data previously appended to altbuf.
|
||||||
|
*/
|
||||||
|
MG_INTERNAL void altbuf_reset(struct altbuf *ab) {
|
||||||
|
mbuf_free(&ab->m);
|
||||||
|
ab->len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns whether the additional buffer was allocated (and thus the data
|
||||||
|
* is in the mbuf, not the client-provided buffer)
|
||||||
|
*/
|
||||||
|
MG_INTERNAL int altbuf_reallocated(struct altbuf *ab) {
|
||||||
|
return ab->len > ab->user_buf_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the actual buffer with data, either the client-provided or a newly
|
||||||
|
* allocated one. If `trim` is non-zero, mbuf-backed buffer is trimmed first.
|
||||||
|
*/
|
||||||
|
MG_INTERNAL char *altbuf_get_buf(struct altbuf *ab, int trim) {
|
||||||
|
if (altbuf_reallocated(ab)) {
|
||||||
|
if (trim) {
|
||||||
|
mbuf_trim(&ab->m);
|
||||||
|
}
|
||||||
|
return ab->m.buf;
|
||||||
|
} else {
|
||||||
|
return ab->user_buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
static const char *mg_version_header = "Mongoose/" MG_VERSION;
|
static const char *mg_version_header = "Mongoose/" MG_VERSION;
|
||||||
|
|
||||||
enum mg_http_proto_data_type { DATA_NONE, DATA_FILE, DATA_PUT };
|
enum mg_http_proto_data_type { DATA_NONE, DATA_FILE, DATA_PUT };
|
||||||
@ -818,7 +898,8 @@ static void mg_http_multipart_begin(struct mg_connection *nc,
|
|||||||
struct mg_str *ct;
|
struct mg_str *ct;
|
||||||
struct mbuf *io = &nc->recv_mbuf;
|
struct mbuf *io = &nc->recv_mbuf;
|
||||||
|
|
||||||
char boundary[100];
|
char boundary_buf[100];
|
||||||
|
char *boundary = boundary_buf;
|
||||||
int boundary_len;
|
int boundary_len;
|
||||||
|
|
||||||
ct = mg_get_http_header(hm, "Content-Type");
|
ct = mg_get_http_header(hm, "Content-Type");
|
||||||
@ -833,7 +914,7 @@ static void mg_http_multipart_begin(struct mg_connection *nc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
boundary_len =
|
boundary_len =
|
||||||
mg_http_parse_header(ct, "boundary", boundary, sizeof(boundary));
|
mg_http_parse_header2(ct, "boundary", &boundary, sizeof(boundary_buf));
|
||||||
if (boundary_len == 0) {
|
if (boundary_len == 0) {
|
||||||
/*
|
/*
|
||||||
* Content type is multipart, but there is no boundary,
|
* Content type is multipart, but there is no boundary,
|
||||||
@ -870,7 +951,7 @@ static void mg_http_multipart_begin(struct mg_connection *nc,
|
|||||||
mbuf_remove(io, req_len);
|
mbuf_remove(io, req_len);
|
||||||
}
|
}
|
||||||
exit_mp:
|
exit_mp:
|
||||||
;
|
if (boundary != boundary_buf) MG_FREE(boundary);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CONTENT_DISPOSITION "Content-Disposition: "
|
#define CONTENT_DISPOSITION "Content-Disposition: "
|
||||||
@ -952,17 +1033,24 @@ static int mg_http_multipart_wait_for_boundary(struct mg_connection *c) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mg_http_parse_header_internal(struct mg_str *hdr,
|
||||||
|
const char *var_name,
|
||||||
|
struct altbuf *ab);
|
||||||
|
|
||||||
static int mg_http_multipart_process_boundary(struct mg_connection *c) {
|
static int mg_http_multipart_process_boundary(struct mg_connection *c) {
|
||||||
int data_size;
|
int data_size;
|
||||||
const char *boundary, *block_begin;
|
const char *boundary, *block_begin;
|
||||||
struct mbuf *io = &c->recv_mbuf;
|
struct mbuf *io = &c->recv_mbuf;
|
||||||
struct mg_http_proto_data *pd = mg_http_get_proto_data(c);
|
struct mg_http_proto_data *pd = mg_http_get_proto_data(c);
|
||||||
char file_name[100], var_name[100];
|
struct altbuf ab_file_name, ab_var_name;
|
||||||
int line_len;
|
int line_len;
|
||||||
boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len);
|
boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len);
|
||||||
block_begin = boundary + pd->mp_stream.boundary_len + 2;
|
block_begin = boundary + pd->mp_stream.boundary_len + 2;
|
||||||
data_size = io->len - (block_begin - io->buf);
|
data_size = io->len - (block_begin - io->buf);
|
||||||
|
|
||||||
|
altbuf_init(&ab_file_name, NULL, 0);
|
||||||
|
altbuf_init(&ab_var_name, NULL, 0);
|
||||||
|
|
||||||
while (data_size > 0 &&
|
while (data_size > 0 &&
|
||||||
(line_len = mg_get_line_len(block_begin, data_size)) != 0) {
|
(line_len = mg_get_line_len(block_begin, data_size)) != 0) {
|
||||||
if (line_len > (int) sizeof(CONTENT_DISPOSITION) &&
|
if (line_len > (int) sizeof(CONTENT_DISPOSITION) &&
|
||||||
@ -972,11 +1060,16 @@ static int mg_http_multipart_process_boundary(struct mg_connection *c) {
|
|||||||
|
|
||||||
header.p = block_begin + sizeof(CONTENT_DISPOSITION) - 1;
|
header.p = block_begin + sizeof(CONTENT_DISPOSITION) - 1;
|
||||||
header.len = line_len - sizeof(CONTENT_DISPOSITION) - 1;
|
header.len = line_len - sizeof(CONTENT_DISPOSITION) - 1;
|
||||||
mg_http_parse_header(&header, "name", var_name, sizeof(var_name) - 2);
|
|
||||||
mg_http_parse_header(&header, "filename", file_name,
|
altbuf_reset(&ab_var_name);
|
||||||
sizeof(file_name) - 2);
|
mg_http_parse_header_internal(&header, "name", &ab_var_name);
|
||||||
|
|
||||||
|
altbuf_reset(&ab_file_name);
|
||||||
|
mg_http_parse_header_internal(&header, "filename", &ab_file_name);
|
||||||
|
|
||||||
block_begin += line_len;
|
block_begin += line_len;
|
||||||
data_size -= line_len;
|
data_size -= line_len;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -987,10 +1080,16 @@ static int mg_http_multipart_process_boundary(struct mg_connection *c) {
|
|||||||
mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_END, NULL, 0);
|
mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_END, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Reserve 2 bytes for "\r\n" in file_name and var_name */
|
||||||
|
altbuf_append(&ab_file_name, '\0');
|
||||||
|
altbuf_append(&ab_file_name, '\0');
|
||||||
|
altbuf_append(&ab_var_name, '\0');
|
||||||
|
altbuf_append(&ab_var_name, '\0');
|
||||||
|
|
||||||
MG_FREE((void *) pd->mp_stream.file_name);
|
MG_FREE((void *) pd->mp_stream.file_name);
|
||||||
pd->mp_stream.file_name = strdup(file_name);
|
pd->mp_stream.file_name = altbuf_get_buf(&ab_file_name, 1 /* trim */);
|
||||||
MG_FREE((void *) pd->mp_stream.var_name);
|
MG_FREE((void *) pd->mp_stream.var_name);
|
||||||
pd->mp_stream.var_name = strdup(var_name);
|
pd->mp_stream.var_name = altbuf_get_buf(&ab_var_name, 1 /* trim */);
|
||||||
|
|
||||||
mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_BEGIN, NULL, 0);
|
mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_BEGIN, NULL, 0);
|
||||||
pd->mp_stream.state = MPS_WAITING_FOR_CHUNK;
|
pd->mp_stream.state = MPS_WAITING_FOR_CHUNK;
|
||||||
@ -1003,6 +1102,9 @@ static int mg_http_multipart_process_boundary(struct mg_connection *c) {
|
|||||||
|
|
||||||
pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY;
|
pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY;
|
||||||
|
|
||||||
|
altbuf_reset(&ab_var_name);
|
||||||
|
altbuf_reset(&ab_file_name);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1557,14 +1659,12 @@ void mg_printf_html_escape(struct mg_connection *nc, const char *fmt, ...) {
|
|||||||
/* LCOV_EXCL_STOP */
|
/* LCOV_EXCL_STOP */
|
||||||
}
|
}
|
||||||
|
|
||||||
int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
|
static void mg_http_parse_header_internal(struct mg_str *hdr,
|
||||||
size_t buf_size) {
|
const char *var_name,
|
||||||
int ch = ' ', ch1 = ',', len = 0, n = strlen(var_name);
|
struct altbuf *ab) {
|
||||||
|
int ch = ' ', ch1 = ',', n = strlen(var_name);
|
||||||
const char *p, *end = hdr ? hdr->p + hdr->len : NULL, *s = NULL;
|
const char *p, *end = hdr ? hdr->p + hdr->len : NULL, *s = NULL;
|
||||||
|
|
||||||
if (buf != NULL && buf_size > 0) buf[0] = '\0';
|
|
||||||
if (hdr == NULL) return 0;
|
|
||||||
|
|
||||||
/* Find where variable starts */
|
/* Find where variable starts */
|
||||||
for (s = hdr->p; s != NULL && s + n < end; s++) {
|
for (s = hdr->p; s != NULL && s + n < end; s++) {
|
||||||
if ((s == hdr->p || s[-1] == ch || s[-1] == ch1 || s[-1] == ';') &&
|
if ((s == hdr->p || s[-1] == ch || s[-1] == ch1 || s[-1] == ';') &&
|
||||||
@ -1578,19 +1678,51 @@ int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
|
|||||||
ch = ch1 = *s++;
|
ch = ch1 = *s++;
|
||||||
}
|
}
|
||||||
p = s;
|
p = s;
|
||||||
while (p < end && p[0] != ch && p[0] != ch1 && len < (int) buf_size) {
|
while (p < end && p[0] != ch && p[0] != ch1) {
|
||||||
if (ch != ' ' && p[0] == '\\' && p[1] == ch) p++;
|
if (ch != ' ' && p[0] == '\\' && p[1] == ch) p++;
|
||||||
buf[len++] = *p++;
|
altbuf_append(ab, *p++);
|
||||||
}
|
}
|
||||||
if (len >= (int) buf_size || (ch != ' ' && *p != ch)) {
|
|
||||||
len = 0;
|
if (ch != ' ' && *p != ch) {
|
||||||
} else {
|
altbuf_reset(ab);
|
||||||
if (len > 0 && s[len - 1] == ',') len--;
|
|
||||||
if (len > 0 && s[len - 1] == ';') len--;
|
|
||||||
buf[len] = '\0';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If there is some data, append a NUL. */
|
||||||
|
if (ab->len > 0) {
|
||||||
|
altbuf_append(ab, '\0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf,
|
||||||
|
size_t buf_size) {
|
||||||
|
struct altbuf ab;
|
||||||
|
altbuf_init(&ab, *buf, buf_size);
|
||||||
|
if (hdr == NULL) return 0;
|
||||||
|
if (*buf != NULL && buf_size > 0) *buf[0] = '\0';
|
||||||
|
|
||||||
|
mg_http_parse_header_internal(hdr, var_name, &ab);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get a (trimmed) buffer, and return a len without a NUL byte which might
|
||||||
|
* have been added.
|
||||||
|
*/
|
||||||
|
*buf = altbuf_get_buf(&ab, 1 /* trim */);
|
||||||
|
return ab.len > 0 ? ab.len - 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
|
||||||
|
size_t buf_size) {
|
||||||
|
char *buf2 = buf;
|
||||||
|
|
||||||
|
int len = mg_http_parse_header2(hdr, var_name, &buf2, buf_size);
|
||||||
|
|
||||||
|
if (buf2 != buf) {
|
||||||
|
/* Buffer was not enough and was reallocated: free it and just return 0 */
|
||||||
|
MG_FREE(buf2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1728,27 +1860,34 @@ static int mg_check_nonce(const char *nonce) {
|
|||||||
|
|
||||||
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
||||||
FILE *fp) {
|
FILE *fp) {
|
||||||
|
int ret = 0;
|
||||||
struct mg_str *hdr;
|
struct mg_str *hdr;
|
||||||
char username[50], cnonce[64], response[40], uri[200], qop[20], nc[20],
|
char username_buf[50], cnonce_buf[64], response_buf[40], uri_buf[200],
|
||||||
nonce[30];
|
qop_buf[20], nc_buf[20], nonce_buf[16];
|
||||||
|
|
||||||
|
char *username = username_buf, *cnonce = cnonce_buf, *response = response_buf,
|
||||||
|
*uri = uri_buf, *qop = qop_buf, *nc = nc_buf, *nonce = nonce_buf;
|
||||||
|
|
||||||
/* Parse "Authorization:" header, fail fast on parse error */
|
/* Parse "Authorization:" header, fail fast on parse error */
|
||||||
if (hm == NULL || fp == NULL ||
|
if (hm == NULL || fp == NULL ||
|
||||||
(hdr = mg_get_http_header(hm, "Authorization")) == NULL ||
|
(hdr = mg_get_http_header(hm, "Authorization")) == NULL ||
|
||||||
mg_http_parse_header(hdr, "username", username, sizeof(username)) == 0 ||
|
mg_http_parse_header2(hdr, "username", &username, sizeof(username_buf)) ==
|
||||||
mg_http_parse_header(hdr, "cnonce", cnonce, sizeof(cnonce)) == 0 ||
|
0 ||
|
||||||
mg_http_parse_header(hdr, "response", response, sizeof(response)) == 0 ||
|
mg_http_parse_header2(hdr, "cnonce", &cnonce, sizeof(cnonce_buf)) == 0 ||
|
||||||
mg_http_parse_header(hdr, "uri", uri, sizeof(uri)) == 0 ||
|
mg_http_parse_header2(hdr, "response", &response, sizeof(response_buf)) ==
|
||||||
mg_http_parse_header(hdr, "qop", qop, sizeof(qop)) == 0 ||
|
0 ||
|
||||||
mg_http_parse_header(hdr, "nc", nc, sizeof(nc)) == 0 ||
|
mg_http_parse_header2(hdr, "uri", &uri, sizeof(uri_buf)) == 0 ||
|
||||||
mg_http_parse_header(hdr, "nonce", nonce, sizeof(nonce)) == 0 ||
|
mg_http_parse_header2(hdr, "qop", &qop, sizeof(qop_buf)) == 0 ||
|
||||||
|
mg_http_parse_header2(hdr, "nc", &nc, sizeof(nc_buf)) == 0 ||
|
||||||
|
mg_http_parse_header2(hdr, "nonce", &nonce, sizeof(nonce_buf)) == 0 ||
|
||||||
mg_check_nonce(nonce) == 0) {
|
mg_check_nonce(nonce) == 0) {
|
||||||
return 0;
|
ret = 0;
|
||||||
|
goto clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */
|
/* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */
|
||||||
|
|
||||||
return mg_check_digest_auth(
|
ret = mg_check_digest_auth(
|
||||||
hm->method,
|
hm->method,
|
||||||
mg_mk_str_n(
|
mg_mk_str_n(
|
||||||
hm->uri.p,
|
hm->uri.p,
|
||||||
@ -1756,6 +1895,17 @@ int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
|||||||
mg_mk_str(username), mg_mk_str(cnonce), mg_mk_str(response),
|
mg_mk_str(username), mg_mk_str(cnonce), mg_mk_str(response),
|
||||||
mg_mk_str(qop), mg_mk_str(nc), mg_mk_str(nonce), mg_mk_str(auth_domain),
|
mg_mk_str(qop), mg_mk_str(nc), mg_mk_str(nonce), mg_mk_str(auth_domain),
|
||||||
fp);
|
fp);
|
||||||
|
|
||||||
|
clean:
|
||||||
|
if (username != username_buf) MG_FREE(username);
|
||||||
|
if (cnonce != cnonce_buf) MG_FREE(cnonce);
|
||||||
|
if (response != response_buf) MG_FREE(response);
|
||||||
|
if (uri != uri_buf) MG_FREE(uri);
|
||||||
|
if (qop != qop_buf) MG_FREE(qop);
|
||||||
|
if (nc != nc_buf) MG_FREE(nc);
|
||||||
|
if (nonce != nonce_buf) MG_FREE(nonce);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
||||||
@ -2825,8 +2975,24 @@ size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name,
|
|||||||
struct mg_str header;
|
struct mg_str header;
|
||||||
header.p = buf + n + cdl;
|
header.p = buf + n + cdl;
|
||||||
header.len = ll - (cdl + 2);
|
header.len = ll - (cdl + 2);
|
||||||
mg_http_parse_header(&header, "name", var_name, var_name_len);
|
{
|
||||||
mg_http_parse_header(&header, "filename", file_name, file_name_len);
|
char *var_name2 = var_name;
|
||||||
|
mg_http_parse_header2(&header, "name", &var_name2, var_name_len);
|
||||||
|
/* TODO: handle reallocated buffer correctly */
|
||||||
|
if (var_name2 != var_name) {
|
||||||
|
MG_FREE(var_name2);
|
||||||
|
var_name[0] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
char *file_name2 = file_name;
|
||||||
|
mg_http_parse_header2(&header, "filename", &file_name2, file_name_len);
|
||||||
|
/* TODO: handle reallocated buffer correctly */
|
||||||
|
if (file_name2 != file_name) {
|
||||||
|
MG_FREE(file_name2);
|
||||||
|
file_name[0] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,21 +31,42 @@ 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_header2(struct mg_str *hdr, const char *var_name, char **buf,
|
||||||
|
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,
|
int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
|
||||||
size_t buf_size);
|
size_t buf_size)
|
||||||
|
#ifdef __GNUC__
|
||||||
|
__attribute__((deprecated));
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gets and parses the Authorization: Basic header
|
* Gets and parses the Authorization: Basic header
|
||||||
|
@ -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) {
|
|||||||
"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");
|
||||||
|
Loading…
Reference in New Issue
Block a user