2016-08-23 3:08 GMT+03:00 Alex Rousskov <[email protected]>:
> I do not understand why you decided to use maxMethodLength in
> parseRequestFirstLine(). AFAICT, parseMethodField() already does
> everything we need: It logs an error message and sets parseStatusCode
> accordingly.
Yes, it does partly what we need but it does not parse the delimiter(
and inform about possible failure). skipDelimiter() does this with
message:
> invalid request-line: missing delimiter
which does not hint that our case is a 'bad' method and
less informative than:
> invalid request-line: method exceeds 32-byte limit
Probably this does not make much sense, so I followed your
sketch and refreshed the patch.
Eduard.
MUST respond with 414 (URI Too Long) when request target exceeds limits.
Before the fix, Squid simply closed client connection after receiving a
huge URI (or a huge request-line), violating the RFC 7230 MUST. This
happened because a high-level Must(have buffer space) check in
ConnStateData::clientParseRequests() would throw an exception. Now these
problems are detected inside the low-level RequestParser code, where we
can distinguish huge URIs from huge methods.
=== modified file 'src/http/one/RequestParser.cc'
--- src/http/one/RequestParser.cc 2016-05-20 08:28:33 +0000
+++ src/http/one/RequestParser.cc 2016-08-23 09:01:24 +0000
@@ -58,60 +58,65 @@
while (!buf_.isEmpty() && (buf_[0] == '\n' || (buf_[0] == '\r' && buf_[1] == '\n'))) {
buf_.consume(1);
}
}
}
/**
* Attempt to parse the method field out of an HTTP message request-line.
*
* Governed by:
* RFC 1945 section 5.1
* RFC 7230 section 2.6, 3.1 and 3.5
*/
bool
Http::One::RequestParser::parseMethodField(Http1::Tokenizer &tok)
{
// method field is a sequence of TCHAR.
// Limit to 32 characters to prevent overly long sequences of non-HTTP
// being sucked in before mismatch is detected. 32 is itself annoyingly
// big but there are methods registered by IANA that reach 17 bytes:
// http://www.iana.org/assignments/http-methods
static const size_t maxMethodLength = 32; // TODO: make this configurable?
SBuf methodFound;
if (!tok.prefix(methodFound, CharacterSet::TCHAR, maxMethodLength)) {
debugs(33, ErrorLevel(), "invalid request-line: missing or malformed method");
parseStatusCode = Http::scBadRequest;
return false;
}
method_ = HttpRequestMethod(methodFound);
+
+ const CharacterSet &delimiters = DelimiterCharacters();
+ if (!skipDelimiter(tok.skipAll(delimiters)))
+ return false;
+
return true;
}
/// the characters which truly are valid within URI
static const CharacterSet &
UriValidCharacters()
{
/* RFC 3986 section 2:
* "
* A URI is composed from a limited set of characters consisting of
* digits, letters, and a few graphic symbols.
* "
*/
static const CharacterSet UriChars =
CharacterSet("URI-Chars","") +
// RFC 3986 section 2.2 - reserved characters
CharacterSet("gen-delims", ":/?#[]@") +
CharacterSet("sub-delims", "!$&'()*+,;=") +
// RFC 3986 section 2.3 - unreserved characters
CharacterSet::ALPHA +
CharacterSet::DIGIT +
CharacterSet("unreserved", "-._~") +
// RFC 3986 section 2.1 - percent encoding "%" HEXDIG
CharacterSet("pct-encoded", "%") +
CharacterSet::HEXDIG;
return UriChars;
}
/// characters which Squid will accept in the HTTP request-target (URI)
@@ -243,73 +248,83 @@
return false;
}
}
return true;
}
/**
* Attempt to parse the first line of a new request message.
*
* Governed by:
* RFC 1945 section 5.1
* RFC 7230 section 2.6, 3.1 and 3.5
*
* \retval -1 an error occurred. parseStatusCode indicates HTTP status result.
* \retval 1 successful parse. member fields contain the request-line items
* \retval 0 more data is needed to complete the parse
*/
int
Http::One::RequestParser::parseRequestFirstLine()
{
debugs(74, 5, "parsing possible request: buf.length=" << buf_.length());
debugs(74, DBG_DATA, buf_);
SBuf line;
// Earlier, skipGarbageLines() took care of any leading LFs (if allowed).
// Now, the request line has to end at the first LF.
static const CharacterSet lineChars = CharacterSet::LF.complement("notLF");
::Parser::Tokenizer lineTok(buf_);
if (!lineTok.prefix(line, lineChars) || !lineTok.skip('\n')) {
+ if (buf_.length() >= Config.maxRequestHeaderSize) {
+ /* who should we blame for our failure to parse this line? */
+
+ Http1::Tokenizer methodTok(buf_);
+ if (!parseMethodField(methodTok))
+ return -1; // blame a bad method (or its delimiter)
+
+ // assume it is the URI
+ debugs(74, ErrorLevel(), "invalid request-line exceeds " <<
+ Config.maxRequestHeaderSize << "-byte limit");
+ parseStatusCode = Http::scUriTooLong;
+ return -1;
+ }
debugs(74, 5, "Parser needs more data");
return 0;
}
Http1::Tokenizer tok(line);
const CharacterSet &delimiters = DelimiterCharacters();
if (!parseMethodField(tok))
return -1;
- if (!skipDelimiter(tok.skipAll(delimiters)))
- return -1;
-
/* now parse backwards, to leave just the URI */
if (!skipTrailingCrs(tok))
return -1;
if (!parseHttpVersionField(tok))
return -1;
if (!http0() && !skipDelimiter(tok.skipAllTrailing(delimiters)))
return -1;
/* parsed everything before and after the URI */
if (!parseUriField(tok))
return -1;
if (!tok.atEnd()) {
debugs(33, ErrorLevel(), "invalid request-line: garbage after URI");
parseStatusCode = Http::scBadRequest;
return -1;
}
parseStatusCode = Http::scOkay;
buf_ = lineTok.remaining(); // incremental parse checkpoint
return 1;
}
bool
Http::One::RequestParser::parse(const SBuf &aBuf)
{
buf_ = aBuf;
_______________________________________________
squid-dev mailing list
[email protected]
http://lists.squid-cache.org/listinfo/squid-dev