mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-09-02 15:02:51 +08:00
Merge pull request #375 from bloqaudio/fix/post-body-split
httpd: buffer a plain POST body that arrives after the headers
This commit is contained in:
+159
-34
@@ -54,6 +54,7 @@ __xdata uint8_t config_buf[CONFIG_UPLOAD_BUF];
|
|||||||
__xdata uint16_t pre_acc;
|
__xdata uint16_t pre_acc;
|
||||||
__xdata uint8_t * __xdata content_type = 0;
|
__xdata uint8_t * __xdata content_type = 0;
|
||||||
__xdata uint8_t * __xdata session = 0;
|
__xdata uint8_t * __xdata session = 0;
|
||||||
|
__xdata uint16_t content_length;
|
||||||
|
|
||||||
// Global variables holding POST state
|
// Global variables holding POST state
|
||||||
__xdata uint16_t bindex; // Current index into the boundary
|
__xdata uint16_t bindex; // Current index into the boundary
|
||||||
@@ -61,6 +62,12 @@ __xdata uint8_t verify_crc;
|
|||||||
__xdata uint32_t max_upload;
|
__xdata uint32_t max_upload;
|
||||||
__xdata uint16_t short_parsed;
|
__xdata uint16_t short_parsed;
|
||||||
|
|
||||||
|
#define POSTBODY_CMD 1
|
||||||
|
#define POSTBODY_LOGIN 2
|
||||||
|
#define POSTBODY_TIMEOUT (5 * SYS_TICK_HZ)
|
||||||
|
__xdata uint8_t postbody_endpoint;
|
||||||
|
__xdata uint16_t postbody_start;
|
||||||
|
|
||||||
__xdata char passwd[21];
|
__xdata char passwd[21];
|
||||||
// Set when a verified firmware upload awaits its response ACK, after
|
// Set when a verified firmware upload awaits its response ACK, after
|
||||||
// which the chip resets to apply the staged image
|
// which the chip resets to apply the staged image
|
||||||
@@ -77,6 +84,7 @@ __xdata uint32_t last_session_use;
|
|||||||
#define TSTATE_CLOSED 3
|
#define TSTATE_CLOSED 3
|
||||||
#define TSTATE_POST 4
|
#define TSTATE_POST 4
|
||||||
#define TSTATE_MULTIPART 5
|
#define TSTATE_MULTIPART 5
|
||||||
|
#define TSTATE_POSTBODY 6
|
||||||
|
|
||||||
extern __xdata uint16_t crc_value;
|
extern __xdata uint16_t crc_value;
|
||||||
__xdata uint16_t crc_final;
|
__xdata uint16_t crc_final;
|
||||||
@@ -138,6 +146,24 @@ bool is_word(__xdata uint8_t *xdata_str_p, __code uint8_t * __xdata code_str_p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* name must be lower-case, starting with the '\n' of the previous line's end */
|
||||||
|
__xdata uint8_t *header_value(__xdata uint8_t *p, __code uint8_t *name)
|
||||||
|
{
|
||||||
|
uint8_t u, c;
|
||||||
|
|
||||||
|
while ((c = *name++)) {
|
||||||
|
u = *p++;
|
||||||
|
if (u >= 'A' && u <= 'Z')
|
||||||
|
u += 'a' - 'A';
|
||||||
|
if (u != c)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
while (*p == ' ' || *p == '\t')
|
||||||
|
p++;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool is_url_word_x(__xdata uint8_t *uri_str_p, __xdata uint8_t *src_str_p)
|
bool is_url_word_x(__xdata uint8_t *uri_str_p, __xdata uint8_t *src_str_p)
|
||||||
{
|
{
|
||||||
uint8_t u, s;
|
uint8_t u, s;
|
||||||
@@ -213,6 +239,9 @@ uint8_t parse_short(__xdata uint8_t *p)
|
|||||||
c = *p++ - '0';
|
c = *p++ - '0';
|
||||||
if (c > 9) { break; }
|
if (c > 9) { break; }
|
||||||
err = 0;
|
err = 0;
|
||||||
|
if (short_parsed > 6552)
|
||||||
|
short_parsed = 0xffff;
|
||||||
|
else
|
||||||
short_parsed = (short_parsed * 10) + c;
|
short_parsed = (short_parsed * 10) + c;
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
@@ -246,30 +275,48 @@ void send_unauthorized(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void send_length_required(void)
|
||||||
|
{
|
||||||
|
slen = strtox(outbuf, "HTTP/1.1 411 Length Required\r\nConnection: close\r\n\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void send_ok(void)
|
||||||
|
{
|
||||||
|
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
__xdata uint8_t *scan_header(__xdata uint8_t * __xdata p)
|
__xdata uint8_t *scan_header(__xdata uint8_t * __xdata p)
|
||||||
{
|
{
|
||||||
|
__xdata uint8_t *v;
|
||||||
|
|
||||||
content_type = 0;
|
content_type = 0;
|
||||||
|
content_length = 0;
|
||||||
session = 0;
|
session = 0;
|
||||||
authenticated = 0;
|
authenticated = 0;
|
||||||
|
|
||||||
while (*p != '\r' || *(p + 1) != '\n' || *(p + 2) != '\r' || *(p + 3) != '\n') {
|
while (!strstart(p, "\r\n\r\n")) {
|
||||||
dbg_char(*p);
|
dbg_char(*p);
|
||||||
if (!*p++)
|
if (!*p)
|
||||||
break;
|
break;
|
||||||
if (is_word(p, "\nContent-Type:"))
|
p++;
|
||||||
content_type = p + 15;
|
if ((v = header_value(p, "\ncontent-type:")))
|
||||||
else if (is_word(p, "\nCookie:")) {
|
content_type = v;
|
||||||
|
else if ((v = header_value(p, "\ncontent-length:"))) {
|
||||||
|
parse_short(v);
|
||||||
|
content_length = short_parsed;
|
||||||
|
} else if ((v = header_value(p, "\ncookie:"))) {
|
||||||
/* Scan for the "session" key: the header may hold several
|
/* Scan for the "session" key: the header may hold several
|
||||||
* cookies in any order. Match "session" not "session=" -
|
* cookies in any order. Match "session" not "session=" -
|
||||||
* is_word() requires a separator after the match and '=' is
|
* is_word() requires a separator after the match and '=' is
|
||||||
* one, so this also rejects a longer key like "sessionx". */
|
* one, so this also rejects a longer key like "sessionx". */
|
||||||
__xdata uint8_t *c = p + 8; /* past "\nCookie:" */
|
while (*v && *v != '\r' && *v != '\n') {
|
||||||
while (*c && *c != '\r' && *c != '\n') {
|
if (is_word(v, "session")) {
|
||||||
if (is_word(c, "session")) {
|
session = v + 8; /* past "session=" */
|
||||||
session = c + 8; /* past "session=" */
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
c++;
|
v++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -531,7 +578,7 @@ static void handle_config_fragment(__xdata uint8_t *p)
|
|||||||
send_bad_request();
|
send_bad_request();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n");
|
send_ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -576,12 +623,95 @@ static void handle_firmware_fragment(__xdata uint8_t *p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void run_cmd_body(__xdata uint8_t *body)
|
||||||
|
{
|
||||||
|
execute_commands(body);
|
||||||
|
if (err_status != ERR_OK) {
|
||||||
|
send_bad_request();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
send_ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void run_login_body(__xdata uint8_t *body)
|
||||||
|
{
|
||||||
|
if (strstart(body, "pwd=") && is_url_word_x(body + 4, passwd)) {
|
||||||
|
dbg_string("Password accepted!\n");
|
||||||
|
read_reg_timer(&last_session_use);
|
||||||
|
gen_random_hex_chars(session_id, SESSION_ID_LENGTH);
|
||||||
|
session_id[SESSION_ID_LENGTH] = NUL;
|
||||||
|
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nConnection: close\r\nLocation: index.html\r\n" \
|
||||||
|
"Set-Cookie: session=");
|
||||||
|
for (uint8_t i = 0; i < SESSION_ID_LENGTH; i++)
|
||||||
|
outbuf[slen++] = session_id[i];
|
||||||
|
slen += strtox(outbuf + slen, "; SameSite=Strict\r\n\r\n");
|
||||||
|
} else {
|
||||||
|
dbg_string("Password invalid!\n");
|
||||||
|
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nConnection: close\r\nLocation: login.html\r\n\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t post_body_take(__xdata uint8_t *p)
|
||||||
|
{
|
||||||
|
uint16_t have;
|
||||||
|
|
||||||
|
if (!content_length) {
|
||||||
|
send_length_required();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (content_length >= CONFIG_UPLOAD_BUF) {
|
||||||
|
send_bad_request();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
have = uip_len - (p - uip_appdata);
|
||||||
|
if (have >= content_length) {
|
||||||
|
p[content_length] = NUL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
memcpy(config_buf, p, have);
|
||||||
|
pre_acc = have;
|
||||||
|
postbody_start = ticks;
|
||||||
|
uip_conn->appstate.tstate = TSTATE_POSTBODY;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void post_body_continue(void)
|
||||||
|
{
|
||||||
|
uint16_t take;
|
||||||
|
|
||||||
|
// no header scan runs while the body is pending: content_length is this request's
|
||||||
|
take = content_length - pre_acc;
|
||||||
|
if (take > uip_len)
|
||||||
|
take = uip_len;
|
||||||
|
memcpy(config_buf + pre_acc, uip_appdata, take);
|
||||||
|
pre_acc += take;
|
||||||
|
if (pre_acc < content_length) {
|
||||||
|
postbody_start = ticks;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
config_buf[pre_acc] = NUL;
|
||||||
|
uip_conn->appstate.tstate = TSTATE_NONE;
|
||||||
|
if (postbody_endpoint == POSTBODY_CMD)
|
||||||
|
run_cmd_body(config_buf);
|
||||||
|
else
|
||||||
|
run_login_body(config_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void handle_post(void)
|
void handle_post(void)
|
||||||
{
|
{
|
||||||
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
|
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
|
||||||
__xdata uint8_t *p = uip_appdata;
|
__xdata uint8_t *p = uip_appdata;
|
||||||
__xdata uint8_t *request_path = p + 6;
|
__xdata uint8_t *request_path = p + 6;
|
||||||
|
|
||||||
|
if (s->tstate == TSTATE_POSTBODY) {
|
||||||
|
post_body_continue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Was the multipart header sent in multiple packets?
|
// Was the multipart header sent in multiple packets?
|
||||||
if (s->tstate != TSTATE_MULTIPART) {
|
if (s->tstate != TSTATE_MULTIPART) {
|
||||||
dbg_string("Is POST\n");
|
dbg_string("Is POST\n");
|
||||||
@@ -634,11 +764,11 @@ void handle_post(void)
|
|||||||
send_unauthorized();
|
send_unauthorized();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
execute_commands(p);
|
postbody_endpoint = POSTBODY_CMD;
|
||||||
if (err_status != ERR_OK) {
|
if (!post_body_take(p))
|
||||||
send_bad_request();
|
return;
|
||||||
|
run_cmd_body(p);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
} else if (is_word(request_path, "login")) {
|
} else if (is_word(request_path, "login")) {
|
||||||
dbg_string("POST login\n");
|
dbg_string("POST login\n");
|
||||||
|
|
||||||
@@ -648,21 +778,11 @@ void handle_post(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
p += 8; // Read also over "pwd="
|
p += 4;
|
||||||
if (is_url_word_x(p, passwd)) {
|
postbody_endpoint = POSTBODY_LOGIN;
|
||||||
dbg_string("Password accepted!\n");
|
if (!post_body_take(p))
|
||||||
read_reg_timer(&last_session_use);
|
return;
|
||||||
gen_random_hex_chars(session_id, SESSION_ID_LENGTH);
|
run_login_body(p);
|
||||||
session_id[SESSION_ID_LENGTH] = NUL;
|
|
||||||
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nConnection: close\r\nLocation: index.html\r\n" \
|
|
||||||
"Set-Cookie: session=");
|
|
||||||
for (uint8_t i = 0; i < SESSION_ID_LENGTH; i++)
|
|
||||||
outbuf[slen++] = session_id[i];
|
|
||||||
slen += strtox(outbuf + slen, "; SameSite=Strict\r\n\r\n");
|
|
||||||
} else {
|
|
||||||
dbg_string("Password invalid!\n");
|
|
||||||
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nConnection: close\r\nLocation: login.html\r\n\r\n");
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
} else if (s->tstate == TSTATE_MULTIPART || is_word(request_path, "upload") || is_word(request_path, "config")) {
|
} else if (s->tstate == TSTATE_MULTIPART || is_word(request_path, "upload") || is_word(request_path, "config")) {
|
||||||
dbg_string("POST upload/config request\n");
|
dbg_string("POST upload/config request\n");
|
||||||
@@ -684,8 +804,6 @@ void handle_post(void)
|
|||||||
send_not_found();
|
send_not_found();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -716,6 +834,11 @@ void httpd_appcall(void)
|
|||||||
dbg_string("Closing because everything has been transmitted\n");
|
dbg_string("Closing because everything has been transmitted\n");
|
||||||
uip_close();
|
uip_close();
|
||||||
s->tstate = TSTATE_CLOSED;
|
s->tstate = TSTATE_CLOSED;
|
||||||
|
} else if (s->tstate == TSTATE_POSTBODY
|
||||||
|
&& (uint16_t)ticks - postbody_start > POSTBODY_TIMEOUT) {
|
||||||
|
dbg_string("Body never arrived\n");
|
||||||
|
uip_abort();
|
||||||
|
s->tstate = TSTATE_CLOSED;
|
||||||
}
|
}
|
||||||
} else if (uip_acked() && s->tstate == TSTATE_TX) {
|
} else if (uip_acked() && s->tstate == TSTATE_TX) {
|
||||||
dbg_string("ACK\n");
|
dbg_string("ACK\n");
|
||||||
@@ -783,10 +906,12 @@ void httpd_appcall(void)
|
|||||||
dbg_char('\n');
|
dbg_char('\n');
|
||||||
#endif
|
#endif
|
||||||
p = uip_appdata;
|
p = uip_appdata;
|
||||||
if (is_word(p, "POST") || s->tstate == TSTATE_MULTIPART) {
|
if (is_word(p, "POST") || s->tstate == TSTATE_MULTIPART
|
||||||
|
|| s->tstate == TSTATE_POSTBODY) {
|
||||||
handle_post();
|
handle_post();
|
||||||
// If this is an ongoing post stream, then wait for the next packet
|
// If this is an ongoing post stream, then wait for the next packet
|
||||||
if (s->tstate == TSTATE_POST || s->tstate == TSTATE_MULTIPART) {
|
if (s->tstate == TSTATE_POST || s->tstate == TSTATE_MULTIPART
|
||||||
|
|| s->tstate == TSTATE_POSTBODY) {
|
||||||
uip_len = 0;
|
uip_len = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user