aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Tkachev2016-08-02 12:32:48 +0600
committerAlexander Tkachev2016-08-24 16:07:55 +0600
commit97cf2be7ef4ee5041ade5cd6f1b150d014653ed3 (patch)
treeb6631b79767cf73b4325497eaa5d996d1a5bc8f5
parentdc02a789b6b2fc4e7693b2e7e69ff57b2767b889 (diff)
downloadscummvm-rg350-97cf2be7ef4ee5041ade5cd6f1b150d014653ed3.tar.gz
scummvm-rg350-97cf2be7ef4ee5041ade5cd6f1b150d014653ed3.tar.bz2
scummvm-rg350-97cf2be7ef4ee5041ade5cd6f1b150d014653ed3.zip
CLOUD: Update LocalWebserver
Reader now reads headers into stream, and some checks are added there and in UploadFileClientHandler, so if headers are too long, they are treated as bad request.
-rw-r--r--backends/networking/sdl_net/reader.cpp143
-rw-r--r--backends/networking/sdl_net/reader.h11
-rw-r--r--backends/networking/sdl_net/uploadfileclienthandler.cpp26
3 files changed, 100 insertions, 80 deletions
diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index 0e4cc9a106..8f3199f51c 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -37,7 +37,7 @@ Reader::Reader() {
_windowUsed = 0;
_windowSize = 0;
- _headers = "";
+ _headersStream = nullptr;
_firstBlock = true;
_contentLength = 0;
@@ -65,6 +65,9 @@ Reader &Reader::operator=(Reader &r) {
_windowSize = r._windowSize;
r._window = nullptr;
+ _headersStream = r._headersStream;
+ r._headersStream = nullptr;
+
_headers = r._headers;
_method = r._method;
_path = r._path;
@@ -84,6 +87,9 @@ Reader &Reader::operator=(Reader &r) {
void Reader::cleanup() {
//_content is not to be freed, it's not owned by Reader
+ if (_headersStream != nullptr)
+ delete _headersStream;
+
if (_window != nullptr)
freeWindow();
}
@@ -92,14 +98,20 @@ bool Reader::readAndHandleFirstHeaders() {
Common::String boundary = "\r\n\r\n";
if (_window == nullptr) {
makeWindow(boundary.size());
- _headers = "";
+ }
+ if (_headersStream == nullptr) {
+ _headersStream = new Common::MemoryReadWriteStream(DisposeAfterUse::YES);
}
- while (readOneByteInString(_headers, boundary)) {
+ while (readOneByteInStream(_headersStream, boundary)) {
+ if (_headersStream->size() > SUSPICIOUS_HEADERS_SIZE) {
+ _isBadRequest = true;
+ return true;
+ }
if (!bytesLeft())
return false;
}
- handleFirstHeaders(_headers);
+ handleFirstHeaders(_headersStream);
freeWindow();
_state = RS_READING_CONTENT;
@@ -136,22 +148,23 @@ void readFromThatUntilLineEnd(const char *cstr, Common::String needle, Common::S
}
}
-void Reader::handleFirstHeaders(Common::String headers) {
+void Reader::handleFirstHeaders(Common::MemoryReadWriteStream *headersStream) {
if (!_boundary.empty()) {
warning("Reader: handleFirstHeaders() called when first headers were already handled");
return;
}
//parse method, path, query, fragment
- parseFirstLine(headers);
+ _headers = readEverythingFromMemoryStream(headersStream);
+ parseFirstLine(_headers);
//find boundary
_boundary = "";
- readFromThatUntilLineEnd(headers.c_str(), "boundary=", _boundary);
+ readFromThatUntilLineEnd(_headers.c_str(), "boundary=", _boundary);
//find content length
Common::String contentLength = "";
- readFromThatUntilLineEnd(headers.c_str(), "Content-Length: ", contentLength);
+ readFromThatUntilLineEnd(_headers.c_str(), "Content-Length: ", contentLength);
_contentLength = contentLength.asUint64();
_availableBytes = _contentLength;
}
@@ -160,48 +173,43 @@ void Reader::parseFirstLine(const Common::String &headers) {
uint32 headersSize = headers.size();
bool bad = false;
- const uint32 SUSPICIOUS_HEADERS_SIZE = 128 * 1024;
- if (headersSize > SUSPICIOUS_HEADERS_SIZE) bad = true;
-
- if (!bad) {
- if (headersSize > 0) {
- const char *cstr = headers.c_str();
- const char *position = strstr(cstr, "\r\n");
- if (position) { //we have at least one line - and we want the first one
- //"<METHOD> <path> HTTP/<VERSION>\r\n"
- Common::String method, path, http, buf;
- uint32 length = position - cstr;
- if (headersSize > length)
- headersSize = length;
- for (uint32 i = 0; i < headersSize; ++i) {
- if (headers[i] != ' ')
- buf += headers[i];
- if (headers[i] == ' ' || i == headersSize - 1) {
- if (method == "") {
- method = buf;
- } else if (path == "") {
- path = buf;
- } else if (http == "") {
- http = buf;
- } else {
- bad = true;
- break;
- }
- buf = "";
+ if (headersSize > 0) {
+ const char *cstr = headers.c_str();
+ const char *position = strstr(cstr, "\r\n");
+ if (position) { //we have at least one line - and we want the first one
+ //"<METHOD> <path> HTTP/<VERSION>\r\n"
+ Common::String method, path, http, buf;
+ uint32 length = position - cstr;
+ if (headersSize > length)
+ headersSize = length;
+ for (uint32 i = 0; i < headersSize; ++i) {
+ if (headers[i] != ' ')
+ buf += headers[i];
+ if (headers[i] == ' ' || i == headersSize - 1) {
+ if (method == "") {
+ method = buf;
+ } else if (path == "") {
+ path = buf;
+ } else if (http == "") {
+ http = buf;
+ } else {
+ bad = true;
+ break;
}
+ buf = "";
}
+ }
- //check that method is supported
- if (method != "GET" && method != "PUT" && method != "POST")
- bad = true;
+ //check that method is supported
+ if (method != "GET" && method != "PUT" && method != "POST")
+ bad = true;
- //check that HTTP/<VERSION> is OK
- if (!http.hasPrefix("HTTP/"))
- bad = true;
+ //check that HTTP/<VERSION> is OK
+ if (!http.hasPrefix("HTTP/"))
+ bad = true;
- _method = method;
- parsePathQueryAndAnchor(path);
- }
+ _method = method;
+ parsePathQueryAndAnchor(path);
}
}
@@ -308,37 +316,33 @@ void Reader::freeWindow() {
_windowUsed = _windowSize = 0;
}
-bool Reader::readOneByteInStream(Common::WriteStream *stream, const Common::String &boundary) {
- byte b = readOne();
- _window[_windowUsed++] = b;
- if (_windowUsed < _windowSize)
- return true;
-
- //when window is filled, check whether that's the boundary
- if (Common::String((char *)_window, _windowSize) == boundary)
+namespace {
+bool windowEqualsString(const byte *window, uint32 windowSize, const Common::String &boundary) {
+ if (boundary.size() != windowSize)
return false;
- //if not, add the first byte of the window to the string
- if (stream)
- stream->writeByte(_window[0]);
- for (uint32 i = 1; i < _windowSize; ++i)
- _window[i - 1] = _window[i];
- --_windowUsed;
+ for (uint32 i = 0; i < windowSize; ++i) {
+ if (window[i] != boundary[i])
+ return false;
+ }
+
return true;
}
+}
-bool Reader::readOneByteInString(Common::String &buffer, const Common::String &boundary) {
+bool Reader::readOneByteInStream(Common::WriteStream *stream, const Common::String &boundary) {
byte b = readOne();
_window[_windowUsed++] = b;
if (_windowUsed < _windowSize)
return true;
//when window is filled, check whether that's the boundary
- if (Common::String((char *)_window, _windowSize) == boundary)
+ if (windowEqualsString(_window, _windowSize, boundary))
return false;
//if not, add the first byte of the window to the string
- buffer += _window[0];
+ if (stream)
+ stream->writeByte(_window[0]);
for (uint32 i = 1; i < _windowSize; ++i)
_window[i - 1] = _window[i];
--_windowUsed;
@@ -419,7 +423,7 @@ bool Reader::readBlockContent(Common::WriteStream *stream) {
return true;
}
-uint32 Reader::bytesLeft() { return _bytesLeft; }
+uint32 Reader::bytesLeft() const { return _bytesLeft; }
void Reader::setContent(Common::MemoryReadWriteStream *stream) {
_content = stream;
@@ -442,4 +446,17 @@ Common::String Reader::queryParameter(Common::String name) const { return _query
Common::String Reader::anchor() const { return _anchor; }
+Common::String Reader::readEverythingFromMemoryStream(Common::MemoryReadWriteStream *stream) {
+ Common::String result;
+ char buf[1024];
+ uint32 readBytes;
+ while (true) {
+ readBytes = stream->read(buf, 1024);
+ if (readBytes == 0)
+ break;
+ result += Common::String(buf, readBytes);
+ }
+ return result;
+}
+
} // End of namespace Networking
diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h
index d4a0ba4e88..16d62a27eb 100644
--- a/backends/networking/sdl_net/reader.h
+++ b/backends/networking/sdl_net/reader.h
@@ -80,6 +80,8 @@ class Reader {
byte *_window;
uint32 _windowUsed, _windowSize;
+ Common::MemoryReadWriteStream *_headersStream;
+
Common::String _headers;
Common::String _method, _path, _query, _anchor;
Common::HashMap<Common::String, Common::String> _queryParameters;
@@ -96,7 +98,7 @@ class Reader {
bool readBlockHeadersIntoStream(Common::WriteStream *stream); //true when ended reading
bool readContentIntoStream(Common::WriteStream *stream); //true when ended reading
- void handleFirstHeaders(Common::String headers);
+ void handleFirstHeaders(Common::MemoryReadWriteStream *headers);
void parseFirstLine(const Common::String &headers);
void parsePathQueryAndAnchor(Common::String path);
void parseQueryParameters();
@@ -104,12 +106,13 @@ class Reader {
void makeWindow(uint32 size);
void freeWindow();
bool readOneByteInStream(Common::WriteStream *stream, const Common::String &boundary);
- bool readOneByteInString(Common::String &buffer, const Common::String &boundary);
byte readOne();
- uint32 bytesLeft();
+ uint32 bytesLeft() const;
public:
+ static const uint32 SUSPICIOUS_HEADERS_SIZE = 1024 * 1024; // 1 MB is really a lot
+
Reader();
~Reader();
@@ -131,6 +134,8 @@ public:
Common::String query() const;
Common::String queryParameter(Common::String name) const;
Common::String anchor() const;
+
+ static Common::String readEverythingFromMemoryStream(Common::MemoryReadWriteStream *stream);
};
} // End of namespace Networking
diff --git a/backends/networking/sdl_net/uploadfileclienthandler.cpp b/backends/networking/sdl_net/uploadfileclienthandler.cpp
index bceeedcc3f..ebf341682c 100644
--- a/backends/networking/sdl_net/uploadfileclienthandler.cpp
+++ b/backends/networking/sdl_net/uploadfileclienthandler.cpp
@@ -24,6 +24,7 @@
#include "backends/fs/fs-factory.h"
#include "backends/networking/sdl_net/handlerutils.h"
#include "backends/networking/sdl_net/localwebserver.h"
+#include "backends/networking/sdl_net/reader.h"
#include "common/file.h"
#include "common/memstream.h"
#include "common/translation.h"
@@ -62,6 +63,11 @@ void UploadFileClientHandler::handle(Client *client) {
handleBlockHeaders(client);
continue;
}
+
+ // fail on suspicious headers
+ if (_headersStream->size() > Reader::SUSPICIOUS_HEADERS_SIZE) {
+ setErrorMessageHandler(*client, _("Invalid request: headers are too long!"));
+ }
break;
case UFH_READING_BLOCK_CONTENT:
@@ -95,26 +101,18 @@ void readFromThatUntilDoubleQuote(const char *cstr, Common::String needle, Commo
}
}
}
-
-Common::String readEverythingFromMemoryStream(Common::MemoryReadWriteStream *stream) {
- Common::String result;
- char buf[1024];
- uint32 readBytes;
- while (true) {
- readBytes = stream->read(buf, 1024);
- if (readBytes == 0)
- break;
- result += Common::String(buf, readBytes);
- }
- return result;
-}
}
void UploadFileClientHandler::handleBlockHeaders(Client *client) {
_state = UFH_READING_BLOCK_CONTENT;
+ // fail on suspicious headers
+ if (_headersStream->size() > Reader::SUSPICIOUS_HEADERS_SIZE) {
+ setErrorMessageHandler(*client, _("Invalid request: headers are too long!"));
+ }
+
// search for "upload_file" field
- Common::String headers = readEverythingFromMemoryStream(_headersStream);
+ Common::String headers = Reader::readEverythingFromMemoryStream(_headersStream);
Common::String fieldName = "";
readFromThatUntilDoubleQuote(headers.c_str(), "name=\"", fieldName);
if (!fieldName.hasPrefix("upload_file"))