1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-21 10:26:06 +03:00

Rewrite multipart boundary detection (#7728)

Use a simpler, cleaner implementation of multipart form detection as
defined in https://tools.ietf.org/html/rfc7578 .

Implements a simple state machine that detects the `\r\n--<boundary>`
stream in input a character at a time, instead of buffering and
comparing in chunks which can miss things due to alignment issues and
which also had a problem with replacing characters in a binary stream.

Adjust the private _uploadReadByte function to return -1 on error (like
a read()), and the main file upload handler to use that return value
instead of duplicating logic.

Fixes #7723
This commit is contained in:
Earle F. Philhower, III 2020-11-28 17:22:34 -08:00 committed by GitHub
parent 04b0c270e4
commit 92175d7090
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 27 additions and 38 deletions

View File

@ -246,7 +246,7 @@ protected:
bool _parseForm(ClientType& client, const String& boundary, uint32_t len); bool _parseForm(ClientType& client, const String& boundary, uint32_t len);
bool _parseFormUploadAborted(); bool _parseFormUploadAborted();
void _uploadWriteByte(uint8_t b); void _uploadWriteByte(uint8_t b);
uint8_t _uploadReadByte(ClientType& client); int _uploadReadByte(ClientType& client);
void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength);
bool _collectHeader(const char* headerName, const char* headerValue); bool _collectHeader(const char* headerName, const char* headerValue);

View File

@ -347,14 +347,14 @@ void ESP8266WebServerTemplate<ServerType>::_uploadWriteByte(uint8_t b){
} }
template <typename ServerType> template <typename ServerType>
uint8_t ESP8266WebServerTemplate<ServerType>::_uploadReadByte(ClientType& client){ int ESP8266WebServerTemplate<ServerType>::_uploadReadByte(ClientType& client){
int res = client.read(); int res = client.read();
if(res == -1){ if(res == -1){
while(!client.available() && client.connected()) while(!client.available() && client.connected())
yield(); yield();
res = client.read(); res = client.read();
} }
return (uint8_t)res; return res;
} }
@ -444,45 +444,34 @@ bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const
_currentHandler->upload(*this, _currentUri, *_currentUpload); _currentHandler->upload(*this, _currentUri, *_currentUpload);
_currentUpload->status = UPLOAD_FILE_WRITE; _currentUpload->status = UPLOAD_FILE_WRITE;
int bLen = boundary.length(); int fastBoundaryLen = 4 /* \r\n-- */ + boundary.length() + 1 /* \0 */;
uint8_t boundBuf[2 + bLen + 1]; // "--" + boundary + null terminator char fastBoundary[ fastBoundaryLen ];
boundBuf[2 + bLen] = '\0'; snprintf(fastBoundary, fastBoundaryLen, "\r\n--%s", boundary.c_str());
uint8_t argByte; int boundaryPtr = 0;
bool first = true; while ( true ) {
while (1) { int ret = _uploadReadByte(client);
//attempt to fill up boundary buffer with length of boundary string if (ret < 0) {
int i; // Unexpected, we should have had data available per above
for (i = 0; i < 2 + bLen; i++) { return _parseFormUploadAborted();
if (!client.connected()) return _parseFormUploadAborted(); }
argByte = _uploadReadByte(client); char in = (char) ret;
if (argByte == '\r') if (in == fastBoundary[ boundaryPtr ]) {
// The input matched the current expected character, advance and possibly exit this file
boundaryPtr++;
if (boundaryPtr == fastBoundaryLen - 1) {
// We read the whole boundary line, we're done here!
break; break;
boundBuf[i] = argByte;
}
if ((strncmp((const char*)boundBuf, "--", 2) == 0) && (strcmp((const char*)(boundBuf + 2), boundary.c_str()) == 0))
break; //found the boundary, done parsing this file
if (first) first = false; //only add newline characters after the first line
else {
_uploadWriteByte('\r');
_uploadWriteByte('\n');
}
// current line does not contain boundary, upload all bytes in boundary buffer
for (int j = 0; j < i; j++)
_uploadWriteByte(boundBuf[j]);
// the initial pass (filling up the boundary buffer) did not reach the end of the line. Upload the rest of the line now
if (i >= 2 + bLen) {
if (!client.connected()) return _parseFormUploadAborted();
argByte = _uploadReadByte(client);
while (argByte != '\r') {
if (!client.connected()) return _parseFormUploadAborted();
_uploadWriteByte(argByte);
argByte = _uploadReadByte(client);
} }
} else {
// The char doesn't match what we want, so dump whatever matches we had, the read in char, and reset ptr to start
for (int i = 0; i < boundaryPtr; i++) {
_uploadWriteByte( fastBoundary[ i ] );
}
_uploadWriteByte( in );
boundaryPtr = 0;
} }
if (!client.connected()) return _parseFormUploadAborted();
_uploadReadByte(client); // '\n'
} }
//Found the boundary string, finish processing this file upload // Found the boundary string, finish processing this file upload
if (_currentHandler && _currentHandler->canUpload(_currentUri)) if (_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, *_currentUpload); _currentHandler->upload(*this, _currentUri, *_currentUpload);
_currentUpload->totalSize += _currentUpload->currentSize; _currentUpload->totalSize += _currentUpload->currentSize;