httpd: handle headers split across segments correctly

Signed-off-by: João Silva <jgc3silva@gmail.com>
This commit is contained in:
João Silva committed 2026-10-19 19:21:32 +01:00
1 parent 138acfadb1
commit d104b3129d
2 files changed
+229 -7

No files matched your search

+53 -7
View File
@@ -90,6 +90,7 @@ __xdata uint32_t last_session_use;
#define TSTATE_POST 4
#define TSTATE_MULTIPART 5
#define TSTATE_POSTBODY 6
#define TSTATE_POSTHEADER 7
extern __xdata uint16_t crc_value;
__xdata uint16_t crc_final;
@@ -746,11 +747,47 @@ static void post_body_continue(void)
}
/*
* Buffer a POST request header across TCP receive events. Stop copying at
* CRLFCRLF so any body bytes in the final segment stay in uip_appdata.
*
* Return the body offset in the current receive buffer, 0 while incomplete,
* or 0xffff after sending an error response.
*/
static uint16_t post_header_take(void)
{
uint16_t i;
for (i = 0; i < uip_len; i++) {
if (pre_acc + 1 >= CONFIG_UPLOAD_BUF) {
uip_conn->appstate.tstate = TSTATE_NONE;
send_bad_request();
return 0xffff;
}
config_buf[pre_acc++] = ((__xdata uint8_t *)uip_appdata)[i];
config_buf[pre_acc] = NUL;
if (pre_acc >= 4
&& config_buf[pre_acc - 4] == '\r'
&& config_buf[pre_acc - 3] == '\n'
&& config_buf[pre_acc - 2] == '\r'
&& config_buf[pre_acc - 1] == '\n') {
uip_conn->appstate.tstate = TSTATE_NONE;
return i + 1;
}
}
postbody_start = ticks;
uip_conn->appstate.tstate = TSTATE_POSTHEADER;
return 0;
}
void handle_post(void)
{
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
__xdata uint8_t *p = uip_appdata;
__xdata uint8_t *request_path = p + 6;
uint16_t body_off;
if (s->tstate == TSTATE_POSTBODY) {
post_body_continue();
@@ -759,6 +796,14 @@ void handle_post(void)
// Was the multipart header sent in multiple packets?
if (s->tstate != TSTATE_MULTIPART) {
if (s->tstate != TSTATE_POSTHEADER)
pre_acc = 0;
body_off = post_header_take();
if (!body_off || body_off == 0xffff)
return;
p = config_buf;
request_path = p + 6;
dbg_string("Is POST\n");
p += 5; // Skip post
// Find end of request path
@@ -772,9 +817,12 @@ void handle_post(void)
dbg_string("Boundary: >"); dbg_string_x(boundary); dbg_string("<\n");
if (!*p || !content_type) {
dbg_string("Bad Request!\n");
send_not_found();
send_bad_request();
return;
}
pre_acc = 0;
p = (__xdata uint8_t *)uip_appdata + body_off;
if (is_word(request_path, "upload")) {
if (!authenticated) {
send_unauthorized();
@@ -824,7 +872,6 @@ void handle_post(void)
handle_firmware_fragment(p);
return;
} else if (is_word(request_path, "cmd")) {
p += 4;
if (!authenticated) {
send_unauthorized();
return;
@@ -843,7 +890,6 @@ void handle_post(void)
return;
}
p += 4;
postbody_endpoint = POSTBODY_LOGIN;
if (!post_body_take(p))
return;
@@ -890,9 +936,9 @@ void httpd_appcall(void)
dbg_string("Closing because everything has been transmitted\n");
uip_close();
s->tstate = TSTATE_CLOSED;
} else if (s->tstate == TSTATE_POSTBODY
} else if ((s->tstate == TSTATE_POSTBODY || s->tstate == TSTATE_POSTHEADER)
&& (uint16_t)ticks - postbody_start > POSTBODY_TIMEOUT) {
dbg_string("Body never arrived\n");
dbg_string("POST request never completed\n");
uip_abort();
s->tstate = TSTATE_CLOSED;
}
@@ -964,11 +1010,11 @@ void httpd_appcall(void)
#endif
p = uip_appdata;
if (is_word(p, "POST") || s->tstate == TSTATE_MULTIPART
|| s->tstate == TSTATE_POSTBODY) {
|| s->tstate == TSTATE_POSTBODY || s->tstate == TSTATE_POSTHEADER) {
handle_post();
// If this is an ongoing post stream, then wait for the next packet
if (s->tstate == TSTATE_POST || s->tstate == TSTATE_MULTIPART
|| s->tstate == TSTATE_POSTBODY) {
|| s->tstate == TSTATE_POSTBODY || s->tstate == TSTATE_POSTHEADER) {
uip_len = 0;
return;
}