]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Merge from trunk rev.13866
authorAmos Jeffries <squid3@treenet.co.nz>
Tue, 20 Jan 2015 12:33:23 +0000 (04:33 -0800)
committerAmos Jeffries <squid3@treenet.co.nz>
Tue, 20 Jan 2015 12:33:23 +0000 (04:33 -0800)
15 files changed:
1  2 
src/client_side.cc
src/clients/Client.cc
src/clients/Client.h
src/comm/Read.cc
src/comm/Read.h
src/http.cc
src/http.h
src/http/one/Makefile.am
src/http/one/Parser.cc
src/http/one/Parser.h
src/http/one/RequestParser.cc
src/http/one/RequestParser.h
src/http/one/forward.h
src/servers/Http1Server.cc
src/tests/testHttp1Parser.cc

index c5b47eb45099c4dcf33808fef853f1c3d5194556,f075afd95bc66a4df443fd92d226fbbc6dd315db..d6f142a4f77098e6cf0703e508a43aac468d3958
@@@ -2185,9 -2185,20 +2185,20 @@@ parseHttpRequest(ConnStateData *csd, co
          return csd->abortRequestParsing("error:method-not-allowed");
      }
  
 -        hp->request_parse_status = Http::scMethodNotAllowed;
+     /* draft-ietf-httpbis-http2-16 section 11.6 registers the method PRI as HTTP/2 specific
+      * Deny "PRI" method if used in HTTP/1.x or 0.9 versions.
+      * If seen it signals a broken client or proxy has corrupted the traffic.
+      */
+     if (hp->method() == Http::METHOD_PRI && hp->messageProtocol() < Http::ProtocolVersion(2,0)) {
+         debugs(33, DBG_IMPORTANT, "WARNING: PRI method received on " << csd->transferProtocol << " port " << csd->port->s.port());
+         debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
++        hp->parseStatusCode = Http::scMethodNotAllowed;
+         return csd->abortRequestParsing("error:method-not-allowed");
+     }
      if (hp->method() == Http::METHOD_NONE) {
          debugs(33, DBG_IMPORTANT, "WARNING: Unsupported method: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
 -        hp->request_parse_status = Http::scMethodNotAllowed;
 +        hp->parseStatusCode = Http::scMethodNotAllowed;
          return csd->abortRequestParsing("error:unsupported-request-method");
      }
  
Simple merge
Simple merge
Simple merge
diff --cc src/comm/Read.h
Simple merge
diff --cc src/http.cc
index bdbb271416c82d1651c5a9c60f831c839278bdbf,1757073d7f9faf47f88f068b85033a60c9a332fd..15debce102b87d5c6c9d4f4eaddecc09b482486f
@@@ -698,44 -702,29 +701,44 @@@ HttpStateData::processReplyHeader(
          return;
      }
  
 -    Http::StatusCode error = Http::scNone;
 +    /* Attempt to parse the first line; this will define where the protocol, status, reason-phrase and header begin */
 +    {
 +        if (hp == NULL)
 +            hp = new Http1::ResponseParser;
 +
 +        bool parsedOk = hp->parse(inBuf);
 +
 +        // sync the buffers after parsing.
 +        inBuf = hp->remaining();
 +
 +        if (hp->needsMoreData()) {
 +            if (eof) { // no more data coming
 +                /* Bug 2879: Replies may terminate with \r\n then EOF instead of \r\n\r\n.
 +                 * We also may receive truncated responses.
 +                 * Ensure here that we have at minimum two \r\n when EOF is seen.
 +                 */
 +                inBuf.append("\r\n\r\n", 4);
 +                // retry the parse
 +                parsedOk = hp->parse(inBuf);
 +                // sync the buffers after parsing.
 +                inBuf = hp->remaining();
 +            } else {
 +                debugs(33, 5, "Incomplete response, waiting for end of response headers");
 +                ctx_exit(ctx);
 +                return;
 +            }
 +        }
  
 -    HttpReply *newrep = new HttpReply;
 -    const bool parsed = newrep->parse(readBuf, eof, &error);
 -
 -    if (!parsed && readBuf->contentSize() > 5 && strncmp(readBuf->content(), "HTTP/", 5) != 0 && strncmp(readBuf->content(), "ICY", 3) != 0) {
 -        MemBuf *mb;
 -        HttpReply *tmprep = new HttpReply;
 -        tmprep->setHeaders(Http::scOkay, "Gatewaying", NULL, -1, -1, -1);
 -        tmprep->header.putExt("X-Transformed-From", "HTTP/0.9");
 -        mb = tmprep->pack();
 -        newrep->parse(mb, eof, &error);
 -        delete mb;
 -        delete tmprep;
 -    } else {
 -        if (!parsed && error > 0) { // unrecoverable parsing error
 -            debugs(11, 3, "processReplyHeader: Non-HTTP-compliant header: '" <<  readBuf->content() << "'");
 -            flags.headers_parsed = true;
 -            // XXX: when sanityCheck is gone and Http::StatusLine is used to parse,
 -            //   the sline should be already set the appropriate values during that parser stage
 -            newrep->sline.set(Http::ProtocolVersion(), error);
 +        flags.headers_parsed = true;
 +
 +        if (!parsedOk) {
 +            // unrecoverable parsing error
 +            debugs(11, 3, "Non-HTTP-compliant header:\n---------\n" << inBuf << "\n----------");
 +            HttpReply *newrep = new HttpReply;
-             newrep->sline.set(Http::ProtocolVersion(1,1), hp->messageStatus());
++            newrep->sline.set(Http::ProtocolVersion(), hp->messageStatus());
              HttpReply *vrep = setVirginReply(newrep);
              entry->replaceHttpReply(vrep);
 +            // XXX: close the server connection ?
              ctx_exit(ctx);
              return;
          }
@@@ -1211,14 -1163,23 +1214,16 @@@ HttpStateData::readReply(const CommIoCb
  
          // update peer response time stats (%<pt)
          const timeval &sent = request->hier.peer_http_request_sent;
-         request->hier.peer_response_time =
-             sent.tv_sec ? tvSubMsec(sent, current_time) : -1;
+         if (sent.tv_sec)
+             tvSub(request->hier.peer_response_time, sent, current_time);
+         else
+             request->hier.peer_response_time.tv_sec = -1;
      }
  
 -    /** \par
 -     * Here the RFC says we should ignore whitespace between replies, but we can't as
 -     * doing so breaks HTTP/0.9 replies beginning with witespace, and in addition
 -     * the response splitting countermeasures is extremely likely to trigger on this,
 -     * not allowing connection reuse in the first place.
 -     *
 -     * 2012-02-10: which RFC? not 2068 or 2616,
 -     *     tolerance there is all about whitespace between requests and header tokens.
 -     */
 +        /* Continue to process previously read data */
 +        break;
  
 -    if (len == 0) { // reached EOF?
 +    case Comm::ENDFILE: // close detected by 0-byte read
          eof = 1;
          flags.do_next_read = false;
  
diff --cc src/http.h
index 44bee1844528902df12457dc57e3d579b6767002,20dd4c5d9a53eb1654ab87383fff70b23ca9540f..b3fa6ebb613c72aa0d3b8bbe55c73b9a43118e20
@@@ -42,12 -42,15 +42,12 @@@ public
      // Determine whether the response is a cacheable representation
      int cacheableReply();
  
-     CachePeer *_peer;         /* CachePeer request made to */
-     int eof;                  /* reached end-of-object? */
-     int lastChunk;            /* reached last chunk of a chunk-encoded reply */
+     CachePeer *_peer;       /* CachePeer request made to */
+     int eof;            /* reached end-of-object? */
+     int lastChunk;      /* reached last chunk of a chunk-encoded reply */
      HttpStateFlags flags;
      size_t read_sz;
 -    int header_bytes_read;  // to find end of response,
 -    int64_t reply_bytes_read;   // without relying on StoreEntry
 -    int body_bytes_truncated; // positive when we read more than we wanted
 -    MemBuf *readBuf;
 +    SBuf inBuf;                ///< I/O buffer for receiving server responses
      bool ignoreCacheControl;
      bool surrogateNoStore;
  
Simple merge
index 313cc6fd1c2fce9de469191edaa118308b077460,1bf408b023476c3c3a1f30407c560c7e68bfbc62..964bde6d82e33b4e1336021bf8c37d16e7373243
@@@ -24,46 -23,8 +24,46 @@@ Http::One::Parser::clear(
      mimeHeaderBlock_.clear();
  }
  
 +bool
 +Http::One::Parser::findMimeBlock(const char *which, size_t limit)
 +{
 +    if (msgProtocol_.major == 1) {
 +        /* NOTE: HTTP/0.9 messages do not have a mime header block.
 +         *       So the rest of the code will need to deal with '0'-byte headers
 +         *       (ie, none, so don't try parsing em)
 +         */
 +        int64_t mimeHeaderBytes = 0;
 +        // XXX: c_str() reallocates. performance regression.
 +        if ((mimeHeaderBytes = headersEnd(buf_.c_str(), buf_.length())) == 0) {
 +            if (buf_.length()+firstLineSize() >= limit) {
 +                debugs(33, 5, "Too large " << which);
 +                parseStatusCode = Http::scHeaderTooLarge;
 +                parsingStage_ = HTTP_PARSE_DONE;
 +            } else
 +                debugs(33, 5, "Incomplete " << which << ", waiting for end of headers");
 +            return false;
 +        }
 +        mimeHeaderBlock_ = buf_.consume(mimeHeaderBytes);
 +        debugs(74, 5, "mime header (0-" << mimeHeaderBytes << ") {" << mimeHeaderBlock_ << "}");
 +
 +    } else
 +        debugs(33, 3, "Missing HTTP/1.x identifier");
 +
 +    // NP: we do not do any further stages here yet so go straight to DONE
 +    parsingStage_ = HTTP_PARSE_DONE;
 +
 +    // Squid could handle these headers, but admin does not want to
 +    if (messageHeaderSize() >= limit) {
 +        debugs(33, 5, "Too large " << which);
 +        parseStatusCode = Http::scHeaderTooLarge;
 +        return false;
 +    }
 +
 +    return true;
 +}
 +
  // arbitrary maximum-length for headers which can be found by Http1Parser::getHeaderField()
- #define GET_HDR_SZ    1024
+ #define GET_HDR_SZ  1024
  
  // BUG: returns only the first header line with given name,
  //      ignores multi-line headers and obs-fold headers
Simple merge
index b5ff770746b8bd2ee21d3b48001f0febfc319cb7,4d55204d7ae109fc3d2acd39d52b4321567692e9..3bd18452e48ba99872ac8f77e2ae418e6b28f8d4
@@@ -6,7 -15,8 +14,7 @@@
  #include "SquidConfig.h"
  
  Http::One::RequestParser::RequestParser() :
-         Parser()
 -    Parser(),
 -    request_parse_status(Http::scNone)
++    Parser()
  {
      req.start = req.end = -1;
      req.m_start = req.m_end = -1;
@@@ -139,9 -149,15 +147,15 @@@ Http::One::RequestParser::parseRequestF
              // However it does explicitly state an exact syntax which omits un-encoded CR
              // and defines 400 (Bad Request) as the required action when
              // handed an invalid request-line.
 -            request_parse_status = Http::scBadRequest;
 +            parseStatusCode = Http::scBadRequest;
              return -1;
          }
 -            request_parse_status = Http::scBadRequest;
+         // We are expecting printable ascii characters for method/first word
+         if (first_whitespace < 0 && (!xisascii(buf_[i]) || !xisprint(buf_[i]))) {
++            parseStatusCode = Http::scBadRequest;
+             return -1;
+         }
      }
  
      if (req.end == -1) {
Simple merge
Simple merge
index 75ffe722e6e595c97a1cda413a67f218dcba93b9,d6f80b9ac28ae598d2ea5d7753fda3bc4ad86457..6cddee659658f1159dd8afdb2292ed85b38e3f82
@@@ -145,19 -96,12 +96,12 @@@ Http::One::Server::buildHttpRequest(Cli
      HttpRequest::Pointer request;
      ClientHttpRequest *http = context->http;
      if (context->flags.parsed_ok == 0) {
-         clientStreamNode *node = context->getClientReplyContext();
          debugs(33, 2, "Invalid Request");
-         quitAfterError(NULL);
-         // setLogUri should called before repContext->setReplyToError
-         setLogUri(http, http->uri, true);
-         clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
-         assert(repContext);
          // determine which error page templates to use for specific parsing errors
          err_type errPage = ERR_INVALID_REQ;
 -        switch (parser_->request_parse_status) {
 +        switch (parser_->parseStatusCode) {
          case Http::scRequestHeaderFieldsTooLarge:
-             // fall through to next case
+         // fall through to next case
          case Http::scUriTooLong:
              errPage = ERR_TOO_BIG;
              break;
              errPage = ERR_UNSUP_HTTPVERSION;
              break;
          default:
-             // use default ERR_INVALID_REQ set above.
+             if (parser_->method() == METHOD_NONE || parser_->requestUri().length() == 0)
+                 // no method or url parsed, probably is wrong protocol
+                 errPage = ERR_PROTOCOL_UNKNOWN;
+             // else use default ERR_INVALID_REQ set above.
              break;
          }
-         repContext->setReplyToError(errPage, parser_->parseStatusCode, parser_->method(), http->uri,
-                                     clientConnection->remote, NULL, in.buf.c_str(), NULL);
-         assert(context->http->out.offset == 0);
-         context->pullData();
+         // setLogUri should called before repContext->setReplyToError
+         setLogUri(http, http->uri, true);
+         const char * requestErrorBytes = in.buf.c_str();
 -        if (!clientTunnelOnError(this, context, request.getRaw(), parser_->method(), errPage, parser_->request_parse_status, requestErrorBytes)) {
++        if (!clientTunnelOnError(this, context, request.getRaw(), parser_->method(), errPage, parser_->parseStatusCode, requestErrorBytes)) {
+             // HttpRequest object not build yet, there is no reason to call
+             // clientProcessRequestFinished method
+         }
          return false;
      }
  
Simple merge