From: Eduard Bagdasaryan Date: Tue, 4 Oct 2016 14:25:15 +0000 (+1300) Subject: HTTP/1.1: handle syntactically valid requests with unsupported HTTP versions X-Git-Tag: SQUID_4_0_15~15 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=294083a1e447831d15dfb429791d2c479b4281c1;p=thirdparty%2Fsquid.git HTTP/1.1: handle syntactically valid requests with unsupported HTTP versions Before this change, when a syntactically valid HTTP/1 request indicated HTTP major version 2, Squid mangled and forwarded the request as an HTTP/1 message. Since Squid does not and cannot correctly interpret an HTTP/1 request using HTTP/2 rules, returning an error and closing the connection appears to be the only correct action possible. If a version label matches the "HTTP/" 1*DIGIT "." 1*DIGIT pattern from RFC 2616 it should not be handled as 0.9 syntax. All unacceptible versions that begin with "HTTP/" should get a 505. To be compliant with RFC 7230 as well: - versions 1.2 thru 1.9 accept and handle normally. That is a SHOULD requirement in RFC 7230 section 2.6 final paragraph. - other single-digit versions should get the 505 error. - versions with multiple digits should get the 505 error. --- diff --git a/src/http/one/RequestParser.cc b/src/http/one/RequestParser.cc index 19dd028646..cd8955f0a5 100644 --- a/src/http/one/RequestParser.cc +++ b/src/http/one/RequestParser.cc @@ -185,14 +185,36 @@ Http::One::RequestParser::parseUriField(Http1::Tokenizer &tok) bool Http::One::RequestParser::parseHttpVersionField(Http1::Tokenizer &tok) { + static const SBuf http1p0("HTTP/1.0"); + static const SBuf http1p1("HTTP/1.1"); const auto savedTok = tok; - SBuf digit; - // Searching for Http1magic precludes detecting HTTP/2+ versions. - // Rewrite if we ever _need_ to return 505 (Version Not Supported) errors. - if (tok.suffix(digit, CharacterSet::DIGIT) && tok.skipSuffix(Http1magic)) { - msgProtocol_ = Http::ProtocolVersion(1, (*digit.rawContent() - '0')); + // Optimization: Expect (and quickly parse) HTTP/1.1 or HTTP/1.0 in + // the vast majority of cases. + if (tok.skipSuffix(http1p1)) { + msgProtocol_ = Http::ProtocolVersion(1, 1); return true; + } else if (tok.skipSuffix(http1p0)) { + msgProtocol_ = Http::ProtocolVersion(1, 0); + return true; + } else { + // RFC 7230 section 2.6: + // HTTP-version = HTTP-name "/" DIGIT "." DIGIT + static const CharacterSet period("Decimal point", "."); + static const SBuf proto("HTTP/"); + SBuf majorDigit; + SBuf minorDigit; + if (tok.suffix(minorDigit, CharacterSet::DIGIT) && + tok.skipOneTrailing(period) && + tok.suffix(majorDigit, CharacterSet::DIGIT) && + tok.skipSuffix(proto)) { + const bool multiDigits = majorDigit.length() > 1 || minorDigit.length() > 1; + // use '0.0' for unsupported multiple digit version numbers + const unsigned int major = multiDigits ? 0 : (*majorDigit.rawContent() - '0'); + const unsigned int minor = multiDigits ? 0 : (*minorDigit.rawContent() - '0'); + msgProtocol_ = Http::ProtocolVersion(major, minor); + return true; + } } // A GET request might use HTTP/0.9 syntax