]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
merge from trunk r14590
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Fri, 18 Mar 2016 17:42:31 +0000 (19:42 +0200)
committerChristos Tsantilas <chtsanti@users.sourceforge.net>
Fri, 18 Mar 2016 17:42:31 +0000 (19:42 +0200)
17 files changed:
1  2 
src/Debug.h
src/Downloader.cc
src/Downloader.h
src/Makefile.am
src/client_side.cc
src/client_side.h
src/client_side_reply.cc
src/client_side_request.cc
src/debug.cc
src/servers/Server.cc
src/ssl/PeerConnector.cc
src/ssl/PeerConnector.h
src/ssl/bio.cc
src/ssl/bio.h
src/ssl/support.cc
src/ssl/support.h
src/tunnel.cc

diff --cc src/Debug.h
Simple merge
index 5584dd84f7e6df4f11b1f0c6896748f8812b2245,0000000000000000000000000000000000000000..167abc99682f86fa661028666166a646cf1f7930
mode 100644,000000..100644
--- /dev/null
@@@ -1,223 -1,0 +1,224 @@@
-     if (ClientSocketContext *context = parseOneRequest()) {
 +#include "squid.h"
 +#include "client_side.h"
 +#include "client_side_request.h"
 +#include "client_side_reply.h"
 +#include "Downloader.h"
 +#include "http/one/RequestParser.h"
++#include "http/Stream.h"
 +
 +CBDATA_CLASS_INIT(Downloader);
 +
 +Downloader::Downloader(SBuf &url, const MasterXaction::Pointer &xact, AsyncCall::Pointer &aCallback, unsigned int level):
 +    AsyncJob("Downloader"),
 +    ConnStateData(xact),
 +    url_(url),
 +    callback(aCallback),
 +    status(Http::scNone),
 +    level_(level)
 +{
 +    transferProtocol = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1);
 +}
 +
 +Downloader::~Downloader()
 +{
 +    debugs(33 , 2, "Downloader Finished");
 +}
 +
 +void
 +Downloader::callException(const std::exception &e)
 +{
 +    debugs(33 , 2, "Downloader caught:" << e.what());
 +    AsyncJob::callException(e);
 +}
 +
 +bool
 +Downloader::doneAll() const
 +{
 +    return (!callback || callback->canceled()) && AsyncJob::doneAll();
 +}
 +
 +void
 +Downloader::start()
 +{
 +    BodyProducer::start();
 +    HttpControlMsgSink::start();
- ClientSocketContext *
++    if (Http::Stream *context = parseOneRequest()) {
 +        context->registerWithConn();
 +        processParsedRequest(context);
 +
 +        /**/
 +        if (context->flags.deferred) {
 +            if (context != context->http->getConn()->pipeline.front().getRaw())
 +                context->deferRecipientForLater(context->deferredparams.node, context->deferredparams.rep, context->deferredparams.queuedBuffer);
 +            else
 +                context->http->getConn()->handleReply(context->deferredparams.rep, context->deferredparams.queuedBuffer); 
 +        }
 +        /**/
 +
 +    }
 +    
 +}
 +
 +void
 +Downloader::noteMoreBodySpaceAvailable(BodyPipe::Pointer)
 +{
 +    // This method required only if we need to support uploading data to server
 +    // Currently only GET requests are supported
 +    assert(0);
 +}
 +
 +void
 +Downloader::noteBodyConsumerAborted(BodyPipe::Pointer)
 +{
 +    // This method required only if we need to support uploading data to server
 +    // Currently only GET requests are supported
 +    assert(0);
 +}
 +
-     ClientSocketContext *const context = new ClientSocketContext(NULL, http);
++Http::Stream *
 +Downloader::parseOneRequest()
 +{ 
 +    const HttpRequestMethod method = Http::METHOD_GET;
 +
 +    char *uri = strdup(url_.c_str());
 +    HttpRequest *const request = HttpRequest::CreateFromUrlAndMethod(uri, method);
 +    if (!request) {
 +        debugs(33, 5, "Invalid FTP URL: " << uri);
 +        safe_free(uri);
 +        return NULL; //earlyError(...)
 +    }
 +    request->http_ver = Http::ProtocolVersion();
 +    request->header.putStr(Http::HdrType::HOST, request->url.host());
 +    request->header.putTime(Http::HdrType::DATE, squid_curtime);
 +
 +    ClientHttpRequest *const http = new ClientHttpRequest(this);
 +    http->request = request;
 +    HTTPMSGLOCK(http->request);
 +    http->req_sz = 0;
 +    http->uri = uri;
 +
- Downloader::processParsedRequest(ClientSocketContext *context)
++    Http::Stream *const context = new Http::Stream(NULL, http);
 +    StoreIOBuffer tempBuffer;
 +    tempBuffer.data = context->reqbuf;
 +    tempBuffer.length = HTTP_REQBUF_SZ;
 +
 +    ClientStreamData newServer = new clientReplyContext(http);
 +    ClientStreamData newClient = context;
 +    clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
 +                     clientReplyStatus, newServer, clientSocketRecipient,
 +                     clientSocketDetach, newClient, tempBuffer);
 +
 +    context->flags.parsed_ok = 1;
 +    return context;
 +}
 +
 +void
-     ClientSocketContext::Pointer context = pipeline.front();
++Downloader::processParsedRequest(Http::Stream *context)
 +{
 +    Must(context != NULL);
 +    Must(pipeline.nrequests == 1);
 +
 +    ClientHttpRequest *const http = context->http;
 +    assert(http != NULL);
 +
 +    debugs(33, 4, "forwarding request to server side");
 +    assert(http->storeEntry() == NULL);
 +    clientProcessRequest(this, Http1::RequestParserPointer(), context);
 +}
 +
 +time_t
 +Downloader::idleTimeout() const
 +{
 +    // No need to be implemented for connection-less ConnStateData object.
 +    assert(0);
 +    return 0;
 +}
 +
 +void
 +Downloader::writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call)
 +{
 +}
 +
 +void
 +Downloader::handleReply(HttpReply *reply, StoreIOBuffer receivedData)
 +{
++    Http::StreamPointer context = pipeline.front();
 +    bool existingContent = reply ? reply->content_length : 0;
 +    bool exceedSize = (context->startOfOutput() && existingContent > -1 && (size_t)existingContent > MaxObjectSize) || 
 +        ((object.length() + receivedData.length) > MaxObjectSize);
 +
 +    if (exceedSize) {
 +        status = Http::scInternalServerError;
 +        callBack();
 +        return;
 +    }
 +
 +    debugs(33, 4, "Received " << receivedData.length <<
 +           " object data, offset: " << receivedData.offset <<
 +           " error flag:" << receivedData.flags.error);
 +
 +    if (receivedData.length > 0) {
 +        object.append(receivedData.data, receivedData.length);
 +        context->http->out.size += receivedData.length;
 +        context->noteSentBodyBytes(receivedData.length);
 +    }
 +
 +    switch (context->socketState()) {
 +    case STREAM_NONE:
 +         debugs(33, 3, "Get more data");
 +        context->pullData();
 +        break;
 +    case STREAM_COMPLETE:
 +        debugs(33, 3, "Object data transfer successfully complete");
 +        status = Http::scOkay;
 +        callBack();
 +        break;
 +    case STREAM_UNPLANNED_COMPLETE:
 +        debugs(33, 3, "Object data transfer failed: STREAM_UNPLANNED_COMPLETE");
 +        status = Http::scInternalServerError;
 +        callBack();
 +        break;
 +    case STREAM_FAILED:
 +        debugs(33, 3, "Object data transfer failed: STREAM_FAILED");
 +        status = Http::scInternalServerError;
 +        callBack();
 +        break;
 +    default:
 +        fatal("unreachable code");
 +    }
 +}
 +
 +void
 +Downloader::downloadFinished()
 +{
 +    debugs(33, 3, "fake call, to just delete the Downloader");
 +
 +    // Not really needed. Squid will delete this object because "doneAll" is true.
 +    //deleteThis("completed");
 +}
 +
 +void
 +Downloader::callBack()
 +{
 +     CbDialer *dialer = dynamic_cast<CbDialer*>(callback->getDialer());
 +     Must(dialer);
 +     dialer->status = status;
 +     if (status == Http::scOkay)
 +         dialer->object = object;
 +     ScheduleCallHere(callback);
 +     callback = NULL;
 +     // Calling deleteThis method here to finish Downloader
 +     // may result to squid crash.
 +     // This method called by handleReply method which maybe called
 +     // by ClientHttpRequest::doCallouts. The doCallouts after this object deleted
 +     // may operate on non valid objects.
 +     // Schedule a fake call here just to force squid to delete this object
 +     CallJobHere(33, 7, CbcPointer<Downloader>(this), Downloader, downloadFinished);
 +}
 +
 +bool
 +Downloader::isOpen() const
 +{
 +    return cbdataReferenceValid(this) && // XXX: checking "this" in a method
 +        callback != NULL;
 +}
index 152d2d8e0b0e79b70bf2363e7581db0be7322c14,0000000000000000000000000000000000000000..be9293fd83d24530d4fe75ccaca35913497b98ee
mode 100644,000000..100644
--- /dev/null
@@@ -1,69 -1,0 +1,69 @@@
-     virtual ClientSocketContext *parseOneRequest();
-     virtual void processParsedRequest(ClientSocketContext *context);
 +#ifndef SQUID_DOWNLOADER_H
 +#define SQUID_DOWNLOADER_H
 +
 +#include "client_side.h"
 +#include "cbdata.h"
 +
 +class Downloader: public ConnStateData
 +{
 +    CBDATA_CLASS(Downloader);
 +    // XXX CBDATA_CLASS expands to nonvirtual toCbdata, AsyncJob::toCbdata
 +    //     is pure virtual. breaks build on clang if override is used
 +
 +public:
 +
 +    /// Callback data to use with Downloader callbacks;
 +    class CbDialer {
 +    public:
 +        CbDialer(): status(Http::scNone) {}
 +        virtual ~CbDialer() {}
 +        SBuf object;
 +        Http::StatusCode status;
 +    };
 +
 +    explicit Downloader(SBuf &url, const MasterXaction::Pointer &xact, AsyncCall::Pointer &aCallback, unsigned int level = 0);
 +    virtual ~Downloader();
 +
 +    /// Fake call used internally by Downloader.
 +    void downloadFinished();
 +
 +    /// The nested level of Downloader object (downloads inside downloads)
 +    unsigned int nestedLevel() const {return level_;}
 +    
 +    /* ConnStateData API */
 +    virtual bool isOpen() const;
 +
 +    /* AsyncJob API */
 +    virtual void callException(const std::exception &e);
 +    virtual bool doneAll() const;
 +
 +    /*Bodypipe API*/
 +    virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer);
 +    virtual void noteBodyConsumerAborted(BodyPipe::Pointer);
 +
 +protected:
 +    /* ConnStateData API */
++    virtual Http::Stream *parseOneRequest();
++    virtual void processParsedRequest(Http::Stream *context);
 +    virtual time_t idleTimeout() const;
 +    virtual void writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call);
 +    virtual void handleReply(HttpReply *header, StoreIOBuffer receivedData);
 +
 +    /* AsyncJob API */
 +    virtual void start();
 +
 +private:
 +    /// Schedules for execution the "callback" with parameters the status
 +    /// and object
 +    void callBack();
 +
 +    static const size_t MaxObjectSize = 1*1024*1024; ///< The maximum allowed object size.
 +
 +    SBuf url_; ///< The url to download
 +    AsyncCall::Pointer callback; ///< callback to call when download finishes
 +    Http::StatusCode status; ///< The download status code
 +    SBuf object; //object data
 +    unsigned int level_; ///< Holds the nested downloads level
 +};
 +
 +#endif
diff --cc src/Makefile.am
Simple merge
index b5b7d52e8cf990b35e5c8df016844a3eda08dfe2,9df9c99ba79a312030103da30099b87a4f849734..df79f9a5c8763d5eb3c3c358ba048e271e7cfbfe
@@@ -2348,12 -1640,12 +1646,14 @@@ clientProcessRequest(ConnStateData *con
  
      request->flags.accelerated = http->flags.accel;
      request->flags.sslBumped=conn->switchedToHttps();
 -    request->flags.ignoreCc = conn->port->ignore_cc;
 -    // TODO: decouple http->flags.accel from request->flags.sslBumped
 -    request->flags.noDirect = (request->flags.accelerated && !request->flags.sslBumped) ?
 -                              !conn->port->allow_direct : 0;
 -    request->sources |= isFtp ? HttpMsg::srcFtp :
 -                        ((request->flags.sslBumped || conn->port->transport.protocol == AnyP::PROTO_HTTPS) ? HttpMsg::srcHttps : HttpMsg::srcHttp);
 +    if (!conn->connectionless()) {
 +        request->flags.ignoreCc = conn->port->ignore_cc;
 +        // TODO: decouple http->flags.accel from request->flags.sslBumped
 +        request->flags.noDirect = (request->flags.accelerated && !request->flags.sslBumped) ?
-             !conn->port->allow_direct : 0;
++                                  !conn->port->allow_direct : 0;
++        request->sources |= isFtp ? HttpMsg::srcFtp :
++                            ((request->flags.sslBumped || conn->port->transport.protocol == AnyP::PROTO_HTTPS) ? HttpMsg::srcHttps : HttpMsg::srcHttp);
 +    }
  #if USE_AUTH
      if (request->flags.sslBumped) {
          if (conn->getAuth() != NULL)
@@@ -3955,12 -3251,16 +3263,16 @@@ void httpsSslBumpStep2AccessCheckDone(a
  void
  ConnStateData::splice()
  {
-     //Normally we can splice here, because we just got client hello message
-     auto ssl = fd_table[clientConnection->fd].ssl;
+     // normally we can splice here, because we just got client hello message
+     auto ssl = fd_table[clientConnection->fd].ssl.get();
+     //retrieve received TLS client information
+     clientConnection->tlsNegotiations()->fillWith(ssl);
      BIO *b = SSL_get_rbio(ssl);
      Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
 -    MemBuf const &rbuf = bio->rBufData();
 -    debugs(83,5, "Bio for  " << clientConnection << " read " << rbuf.contentSize() << " helo bytes");
 +    SBuf const &rbuf = bio->rBufData();
 +    debugs(83,5, "Bio for  " << clientConnection << " read " << rbuf.length() << " helo bytes");
      // Do splice:
      fd_table[clientConnection->fd].read_method = &default_read_method;
      fd_table[clientConnection->fd].write_method = &default_write_method;
          // reset the current protocol to HTTP/1.1 (was "HTTPS" for the bumping process)
          transferProtocol = Http::ProtocolVersion();
          // inBuf still has the "CONNECT ..." request data, reset it to SSL hello message
 -        inBuf.append(rbuf.content(), rbuf.contentSize());
 +        inBuf.append(rbuf);
-         ClientSocketContext::Pointer context = pipeline.front();
+         Http::StreamPointer context = pipeline.front();
          ClientHttpRequest *http = context->http;
          tunnelStart(http);
      }
Simple merge
Simple merge
Simple merge
diff --cc src/debug.cc
Simple merge
Simple merge
index 28e105054fec9bc5c5ab4265925f03a20a9a6a8e,695abc8bd83af55f3a4474494bc3496664d1cb8f..5d633f921ee4eb05d4ed8b701e46a0e0b279e945
  
  #include "squid.h"
  #include "acl/FilledChecklist.h"
- #include "base/AsyncCbdataCalls.h"
- #include "CachePeer.h"
- #include "client_side.h"
  #include "comm/Loops.h"
 +#include "Downloader.h"
  #include "errorpage.h"
  #include "fde.h"
- #include "globals.h"
- #include "helper/ResultCode.h"
++#include "http/Stream.h"
  #include "HttpRequest.h"
- #include "neighbors.h"
  #include "SquidConfig.h"
 +#include "ssl/bio.h"
  #include "ssl/cert_validate_message.h"
  #include "ssl/Config.h"
- #include "ssl/ErrorDetail.h"
  #include "ssl/helper.h"
  #include "ssl/PeerConnector.h"
- #include "ssl/ServerBump.h"
- #include "ssl/support.h"
  
  CBDATA_NAMESPACED_CLASS_INIT(Ssl, PeerConnector);
- CBDATA_NAMESPACED_CLASS_INIT(Ssl, BlindPeerConnector);
- CBDATA_NAMESPACED_CLASS_INIT(Ssl, PeekingPeerConnector);
  
- Ssl::PeerConnector::PeerConnector(const Comm::ConnectionPointer &aServerConn, AsyncCall::Pointer &aCallback, const time_t timeout) :
+ Ssl::PeerConnector::PeerConnector(const Comm::ConnectionPointer &aServerConn, AsyncCall::Pointer &aCallback, const AccessLogEntryPointer &alp, const time_t timeout) :
      AsyncJob("Ssl::PeerConnector"),
      serverConn(aServerConn),
-     certErrors(NULL),
+     al(alp),
      callback(aCallback),
      negotiationTimeout(timeout),
      startTime(squid_curtime),
-     useCertValidator_(false),
 -    useCertValidator_(true)
++    useCertValidator_(true),
 +    certsDownloads(0)
  {
      // if this throws, the caller's cb dialer is not our CbDialer
      Must(dynamic_cast<CbDialer*>(callback->getDialer()));
@@@ -453,29 -338,8 +342,29 @@@ Ssl::PeerConnector::handleNegotiateErro
  void
  Ssl::PeerConnector::noteWantRead()
  {
 -    setReadTimeout();
      const int fd = serverConnection()->fd;
-     SSL *ssl = fd_table[fd].ssl;
++    Security::SessionPtr ssl = fd_table[fd].ssl.get();
 +    BIO *b = SSL_get_rbio(ssl);
 +    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
 +    if (srvBio->holdRead()) {
 +        if (srvBio->gotHello()) {
 +            if (checkForMissingCertificates())
 +                return; // Wait to download certificates before proceed.
 +
 +            srvBio->holdRead(false);
 +            // Schedule a negotiateSSl to allow openSSL parse received data
 +            Ssl::PeerConnector::NegotiateSsl(fd, this);
 +            return;
 +        } else if (srvBio->gotHelloFailed()) {
 +            srvBio->holdRead(false);
 +            debugs(83, DBG_IMPORTANT, "Error parsing SSL Server Hello Message on FD " << fd);
 +            // Schedule a negotiateSSl to allow openSSL parse received data
 +            Ssl::PeerConnector::NegotiateSsl(fd, this);
 +            return;
 +        }
 +    }
 +
 +    setReadTimeout();
      Comm::SetSelect(fd, COMM_SELECT_READ, &NegotiateSsl, this, 0);
  }
  
@@@ -608,408 -467,3 +492,101 @@@ Ssl::PeerConnector::status() cons
      return buf.content();
  }
  
-     SSL *ssl = fd_table[fd].ssl;
 +/// CallDialer to allow use Downloader objects within PeerConnector class.
 +class PeerConnectorCertDownloaderDialer: public CallDialer, public Downloader::CbDialer
 +{
 +public:
 +    typedef void (Ssl::PeerConnector::*Method)(SBuf &object, int status);
 +
 +    PeerConnectorCertDownloaderDialer(Method method, Ssl::PeerConnector *pc):
 +        method_(method),
 +        peerConnector_(pc) {}
 +
 +    /* CallDialer API */
 +    virtual bool canDial(AsyncCall &call) { return peerConnector_.valid(); }
 +    void dial(AsyncCall &call) { ((&(*peerConnector_))->*method_)(object, status); }
 +    virtual void print(std::ostream &os) const {
 +        os << '(' << peerConnector_.get() << ", Http Status:" << status << ')';
 +    }
 +
 +    Method method_; ///< The Ssl::PeerConnector method to dial
 +    CbcPointer<Ssl::PeerConnector> peerConnector_; ///< The Ssl::PeerConnector object
 +};
 +
 +void
 +Ssl::PeerConnector::startCertDownloading(SBuf &url)
 +{
 +    AsyncCall::Pointer certCallback = asyncCall(81, 4,
 +                                            "Ssl::PeerConnector::certDownloadingDone",
 +                                            PeerConnectorCertDownloaderDialer(&Ssl::PeerConnector::certDownloadingDone, this));
 +
 +    const Downloader *csd = dynamic_cast<const Downloader*>(request->clientConnectionManager.valid());
 +    MasterXaction *xaction = new MasterXaction;
 +    Downloader *dl = new Downloader(url, xaction, certCallback, csd ? csd->nestedLevel() + 1 : 1);
 +    AsyncJob::Start(dl);
 +}
 +
 +void
 +Ssl::PeerConnector::certDownloadingDone(SBuf &obj, int downloadStatus)
 +{
 +    certsDownloads++;
 +    debugs(81, 5, "Certificate downloading status: " << downloadStatus << " certificate size: " << obj.length());
 +
 +    // Get ServerBio from SSL object
 +    const int fd = serverConnection()->fd;
-     SSL *ssl = fd_table[fd].ssl;
++    Security::SessionPtr ssl = fd_table[fd].ssl.get();
 +    BIO *b = SSL_get_rbio(ssl);
 +    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
 +
 +    // Parse Certificate. Assume that it is in DER format.
 +    const unsigned char *raw = (const unsigned char*)obj.rawContent();
 +    if (X509 *cert = d2i_X509(NULL, &raw, obj.length())) {
 +        char buffer[1024];
 +        debugs(81, 5, "Retrieved certificate: " << X509_NAME_oneline(X509_get_subject_name(cert), buffer, 1024));
 +        const Ssl::X509_STACK_Pointer &certsList = srvBio->serverCertificates();
 +        if (const char *issuerUri = Ssl::uriOfIssuerIfMissing(cert,  certsList)) {
 +            urlsOfMissingCerts.push(SBuf(issuerUri));
 +        }
 +        Ssl::SSL_add_untrusted_cert(ssl, cert);
 +    }
 +
 +    // Check if has uri to download from and if yes add it to urlsOfMissingCerts
 +    if (urlsOfMissingCerts.size() && certsDownloads <= MaxCertsDownloads) {
 +        startCertDownloading(urlsOfMissingCerts.front());
 +        urlsOfMissingCerts.pop();
 +        return;
 +    }
 +
 +    srvBio->holdRead(false);
 +    Ssl::PeerConnector::NegotiateSsl(serverConnection()->fd, this);
 +}
 +
 +bool
 +Ssl::PeerConnector::checkForMissingCertificates ()
 +{
 +    // Check for nested SSL certificates downloads. For example when the
 +    // certificate located in an SSL site which requires to download a
 +    // a missing certificate (... from an SSL site which requires to ...)
 +    const Downloader *csd = dynamic_cast<const Downloader*>(request->clientConnectionManager.valid());
 +    if (csd && csd->nestedLevel() >= MaxNestedDownloads)
 +        return false;
 +
 +    const int fd = serverConnection()->fd;
- Security::ContextPtr
- Ssl::BlindPeerConnector::getSslContext()
- {
-     if (const CachePeer *peer = serverConnection()->getPeer()) {
-         assert(peer->secure.encryptTransport);
-         Security::ContextPtr sslContext(peer->sslContext);
-         return sslContext;
-     }
-     return ::Config.ssl_client.sslContext;
- }
- SSL *
- Ssl::BlindPeerConnector::initializeSsl()
- {
-     SSL *ssl = Ssl::PeerConnector::initializeSsl();
-     if (!ssl)
-         return NULL;
-     if (const CachePeer *peer = serverConnection()->getPeer()) {
-         assert(peer);
-         // NP: domain may be a raw-IP but it is now always set
-         assert(!peer->secure.sslDomain.isEmpty());
-         // const loss is okay here, ssl_ex_index_server is only read and not assigned a destructor
-         SBuf *host = new SBuf(peer->secure.sslDomain);
-         SSL_set_ex_data(ssl, ssl_ex_index_server, host);
-         if (peer->sslSession)
-             SSL_set_session(ssl, peer->sslSession);
-     } else {
-         SBuf *hostName = new SBuf(request->url.host());
-         SSL_set_ex_data(ssl, ssl_ex_index_server, (void*)hostName);
-     }
-     return ssl;
- }
- void
- Ssl::BlindPeerConnector::noteNegotiationDone(ErrorState *error)
- {
-     if (error) {
-         // XXX: forward.cc calls peerConnectSucceeded() after an OK TCP connect but
-         // we call peerConnectFailed() if SSL failed afterwards. Is that OK?
-         // It is not clear whether we should call peerConnectSucceeded/Failed()
-         // based on TCP results, SSL results, or both. And the code is probably not
-         // consistent in this aspect across tunnelling and forwarding modules.
-         if (CachePeer *p = serverConnection()->getPeer())
-             peerConnectFailed(p);
-         return;
-     }
-     const int fd = serverConnection()->fd;
-     SSL *ssl = fd_table[fd].ssl;
-     if (serverConnection()->getPeer() && !SSL_session_reused(ssl)) {
-         if (serverConnection()->getPeer()->sslSession)
-             SSL_SESSION_free(serverConnection()->getPeer()->sslSession);
-         serverConnection()->getPeer()->sslSession = SSL_get1_session(ssl);
-     }
- }
- Security::ContextPtr
- Ssl::PeekingPeerConnector::getSslContext()
- {
-     // XXX: locate a per-server context in Security:: instead
-     return ::Config.ssl_client.sslContext;
- }
- SSL *
- Ssl::PeekingPeerConnector::initializeSsl()
- {
-     SSL *ssl = Ssl::PeerConnector::initializeSsl();
-     if (!ssl)
-         return NULL;
-     if (ConnStateData *csd = request->clientConnectionManager.valid()) {
-         // client connection is required in the case we need to splice
-         // or terminate client and server connections
-         assert(clientConn != NULL);
-         SBuf *hostName = NULL;
-         Ssl::ClientBio *cltBio = NULL;
-         //Enable Status_request tls extension, required to bump some clients
-         SSL_set_tlsext_status_type(ssl, TLSEXT_STATUSTYPE_ocsp);
-         // In server-first bumping mode, clientSsl is NULL.
-         if (SSL *clientSsl = fd_table[clientConn->fd].ssl) {
-             BIO *b = SSL_get_rbio(clientSsl);
-             cltBio = static_cast<Ssl::ClientBio *>(b->ptr);
-             const Ssl::Bio::sslFeatures &features = cltBio->getFeatures();
-             if (!features.serverName.isEmpty())
-                 hostName = new SBuf(features.serverName);
-         }
-         if (!hostName) {
-             // While we are peeking at the certificate, we may not know the server
-             // name that the client will request (after interception or CONNECT)
-             // unless it was the CONNECT request with a user-typed address.
-             const bool isConnectRequest = !csd->port->flags.isIntercepted();
-             if (!request->flags.sslPeek || isConnectRequest)
-                 hostName = new SBuf(request->url.host());
-         }
-         if (hostName)
-             SSL_set_ex_data(ssl, ssl_ex_index_server, (void*)hostName);
-         Must(!csd->serverBump() || csd->serverBump()->step <= Ssl::bumpStep2);
-         if (csd->sslBumpMode == Ssl::bumpPeek || csd->sslBumpMode == Ssl::bumpStare) {
-             assert(cltBio);
-             const Ssl::Bio::sslFeatures &features = cltBio->getFeatures();
-             if (features.sslVersion != -1) {
-                 features.applyToSSL(ssl, csd->sslBumpMode);
-                 // Should we allow it for all protocols?
-                 if (features.sslVersion >= 3) {
-                     BIO *b = SSL_get_rbio(ssl);
-                     Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
-                     // Inherite client features, like SSL version, SNI and other
-                     srvBio->setClientFeatures(features);
-                     srvBio->recordInput(true);
-                     srvBio->mode(csd->sslBumpMode);
-                 }
-             }
-         } else {
-             // Set client SSL options
-             SSL_set_options(ssl, ::Security::ProxyOutgoingConfig.parsedOptions);
-             // Use SNI TLS extension only when we connect directly
-             // to the origin server and we know the server host name.
-             const char *sniServer = NULL;
-             const bool redirected = request->flags.redirected && ::Config.onoff.redir_rewrites_host;
-             if (!hostName || redirected)
-                 sniServer = !request->url.hostIsNumeric() ? request->url.host() : NULL;
-             else
-                 sniServer = hostName->c_str();
-             if (sniServer)
-                 Ssl::setClientSNI(ssl, sniServer);
-         }
-         // store peeked cert to check SQUID_X509_V_ERR_CERT_CHANGE
-         X509 *peeked_cert;
-         if (csd->serverBump() &&
-                 (peeked_cert = csd->serverBump()->serverCert.get())) {
-             CRYPTO_add(&(peeked_cert->references),1,CRYPTO_LOCK_X509);
-             SSL_set_ex_data(ssl, ssl_ex_index_ssl_peeked_cert, peeked_cert);
-         }
-     }
-     return ssl;
- }
- void
- Ssl::PeekingPeerConnector::noteNegotiationDone(ErrorState *error)
- {
-     SSL *ssl = fd_table[serverConnection()->fd].ssl;
-     // Check the list error with
-     if (!request->clientConnectionManager.valid() || ! ssl)
-         return;
-     // remember the server certificate from the ErrorDetail object
-     if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
-         // remember validation errors, if any
-         if (certErrors) {
-             if (serverBump->sslErrors)
-                 cbdataReferenceDone(serverBump->sslErrors);
-             serverBump->sslErrors = cbdataReference(certErrors);
-         }
-         if (!serverBump->serverCert.get()) {
-             // remember the server certificate from the ErrorDetail object
-             if (error && error->detail && error->detail->peerCert())
-                 serverBump->serverCert.resetAndLock(error->detail->peerCert());
-             else {
-                 handleServerCertificate();
-             }
-         }
-         if (error) {
-             // For intercepted connections, set the host name to the server
-             // certificate CN. Otherwise, we just hope that CONNECT is using
-             // a user-entered address (a host name or a user-entered IP).
-             const bool isConnectRequest = !request->clientConnectionManager->port->flags.isIntercepted();
-             if (request->flags.sslPeek && !isConnectRequest) {
-                 if (X509 *srvX509 = serverBump->serverCert.get()) {
-                     if (const char *name = Ssl::CommonHostName(srvX509)) {
-                         request->url.host(name);
-                         debugs(83, 3, "reset request host: " << name);
-                     }
-                 }
-             }
-         }
-     }
-     if (!error) {
-         serverCertificateVerified();
-         if (splice)
-             switchToTunnel(request.getRaw(), clientConn, serverConn);
-     }
- }
- void
- Ssl::PeekingPeerConnector::noteWantWrite()
- {
-     const int fd = serverConnection()->fd;
-     SSL *ssl = fd_table[fd].ssl;
-     BIO *b = SSL_get_rbio(ssl);
-     Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
-     if ((srvBio->bumpMode() == Ssl::bumpPeek || srvBio->bumpMode() == Ssl::bumpStare) && srvBio->holdWrite()) {
-         debugs(81, DBG_IMPORTANT, "hold write on SSL connection on FD " << fd);
-         checkForPeekAndSplice();
-         return;
-     }
-     Ssl::PeerConnector::noteWantWrite();
- }
- void
- Ssl::PeekingPeerConnector::noteSslNegotiationError(const int result, const int ssl_error, const int ssl_lib_error)
- {
-     const int fd = serverConnection()->fd;
-     SSL *ssl = fd_table[fd].ssl;
-     BIO *b = SSL_get_rbio(ssl);
-     Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
-     // In Peek mode, the ClientHello message sent to the server. If the
-     // server resuming a previous (spliced) SSL session with the client,
-     // then probably we are here because local SSL object does not know
-     // anything about the session being resumed.
-     //
-     if (srvBio->bumpMode() == Ssl::bumpPeek && (resumingSession = srvBio->resumingSession())) {
-         // we currently splice all resumed sessions unconditionally
-         if (const bool spliceResumed = true) {
-             bypassCertValidator();
-             checkForPeekAndSpliceMatched(Ssl::bumpSplice);
-             return;
-         } // else fall through to find a matching ssl_bump action (with limited info)
-     }
-     // If we are in peek-and-splice mode and still we did not write to
-     // server yet, try to see if we should splice.
-     // In this case the connection can be saved.
-     // If the checklist decision is do not splice a new error will
-     // occur in the next SSL_connect call, and we will fail again.
-     // Abort on certificate validation errors to avoid splicing and
-     // thus hiding them.
-     // Abort if no certificate found probably because of malformed or
-     // unsupported server Hello message (TODO: make configurable).
-     if (!SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail) &&
-             (srvBio->bumpMode() == Ssl::bumpPeek  || srvBio->bumpMode() == Ssl::bumpStare) && srvBio->holdWrite()) {
-         Security::CertPointer serverCert(SSL_get_peer_certificate(ssl));
-         if (serverCert.get()) {
-             debugs(81, 3, "Error ("  << ERR_error_string(ssl_lib_error, NULL) <<  ") but, hold write on SSL connection on FD " << fd);
-             checkForPeekAndSplice();
-             return;
-         }
-     }
-     // else call parent noteNegotiationError to produce an error page
-     Ssl::PeerConnector::noteSslNegotiationError(result, ssl_error, ssl_lib_error);
- }
- void
- Ssl::PeekingPeerConnector::handleServerCertificate()
- {
-     if (serverCertificateHandled)
-         return;
-     if (ConnStateData *csd = request->clientConnectionManager.valid()) {
-         const int fd = serverConnection()->fd;
-         SSL *ssl = fd_table[fd].ssl;
-         Security::CertPointer serverCert(SSL_get_peer_certificate(ssl));
-         if (!serverCert.get())
-             return;
-         serverCertificateHandled = true;
-         // remember the server certificate for later use
-         if (Ssl::ServerBump *serverBump = csd->serverBump()) {
-             serverBump->serverCert.reset(serverCert.release());
-         }
-     }
- }
- void
- Ssl::PeekingPeerConnector::serverCertificateVerified()
- {
-     if (ConnStateData *csd = request->clientConnectionManager.valid()) {
-         Security::CertPointer serverCert;
-         if(Ssl::ServerBump *serverBump = csd->serverBump())
-             serverCert.resetAndLock(serverBump->serverCert.get());
-         else {
-             const int fd = serverConnection()->fd;
-             SSL *ssl = fd_table[fd].ssl;
-             serverCert.reset(SSL_get_peer_certificate(ssl));
-         }
-         if (serverCert.get()) {
-             csd->resetSslCommonName(Ssl::CommonHostName(serverCert.get()));
-             debugs(83, 5, "HTTPS server CN: " << csd->sslCommonName() <<
-                    " bumped: " << *serverConnection());
-         }
-     }
- }
++    Security::SessionPtr ssl = fd_table[fd].ssl.get();
 +    BIO *b = SSL_get_rbio(ssl);
 +    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
 +    const Ssl::X509_STACK_Pointer &certs = srvBio->serverCertificates();
 +
 +    if (certs.get() && sk_X509_num(certs.get())) {
 +        debugs(83, 5, "SSL server sent " << sk_X509_num(certs.get()) << " certificates");
 +        Ssl::missingChainCertificatesUrls(urlsOfMissingCerts, certs);
 +        if (urlsOfMissingCerts.size()) {
 +            startCertDownloading(urlsOfMissingCerts.front());
 +            urlsOfMissingCerts.pop();
 +            return true;
 +        }
 +    }
 +
 +    return false;
 +}
 +
index 34d2c12c1e1f6247a566ecf367683cfee564ba84,7a8f481d715289996d148fb62fcb935c4d974a55..ec461dcde8ee71455ec0fb1ee80956279a9b2612
  #include "CommCalls.h"
  #include "security/EncryptorAnswer.h"
  #include "ssl/support.h"
  #include <iosfwd>
 +#include <queue>
  
+ #if USE_OPENSSL
  class HttpRequest;
  class ErrorState;
+ class AccessLogEntry;
+ typedef RefCount<AccessLogEntry> AccessLogEntryPointer;
  
  namespace Ssl
  {
@@@ -182,108 -171,14 +184,23 @@@ private
  
      /// A wrapper function for negotiateSsl for use with Comm::SetSelect
      static void NegotiateSsl(int fd, void *data);
-     AsyncCall::Pointer callback; ///< we call this with the results
 +
 +    /// The maximum allowed missing certificates downloads
 +    static const unsigned int MaxCertsDownloads = 10;
 +    /// The maximum allowed nested certificates downloads
 +    static const unsigned int MaxNestedDownloads = 3;
 +
      AsyncCall::Pointer closeHandler; ///< we call this when the connection closed
      time_t negotiationTimeout; ///< the SSL connection timeout to use
      time_t startTime; ///< when the peer connector negotiation started
      bool useCertValidator_; ///< whether the certificate validator should bypassed
 +    /// The list of URLs where missing certificates should be downloaded
 +    std::queue<SBuf> urlsOfMissingCerts;
 +    unsigned int certsDownloads; ///< The number of downloaded missing certificates
  };
  
- /// A simple PeerConnector for SSL/TLS cache_peers. No SslBump capabilities.
- class BlindPeerConnector: public PeerConnector {
-     CBDATA_CLASS(BlindPeerConnector);
- public:
-     BlindPeerConnector(HttpRequestPointer &aRequest,
-                        const Comm::ConnectionPointer &aServerConn,
-                        AsyncCall::Pointer &aCallback, const time_t timeout = 0) :
-         AsyncJob("Ssl::BlindPeerConnector"),
-         PeerConnector(aServerConn, aCallback, timeout)
-     {
-         request = aRequest;
-     }
-     /* PeerConnector API */
-     /// Calls parent initializeSSL, configure the created SSL object to try reuse SSL session
-     /// and sets the hostname to use for certificates validation
-     virtual SSL *initializeSsl();
-     /// Return the configured Security::ContextPtr object
-     virtual Security::ContextPtr getSslContext();
-     /// On error calls peerConnectFailed function, on success store the used SSL session
-     /// for later use
-     virtual void noteNegotiationDone(ErrorState *error);
- };
- /// A PeerConnector for HTTP origin servers. Capable of SslBumping.
- class PeekingPeerConnector: public PeerConnector {
-     CBDATA_CLASS(PeekingPeerConnector);
- public:
-     PeekingPeerConnector(HttpRequestPointer &aRequest,
-                          const Comm::ConnectionPointer &aServerConn,
-                          const Comm::ConnectionPointer &aClientConn,
-                          AsyncCall::Pointer &aCallback, const time_t timeout = 0) :
-         AsyncJob("Ssl::PeekingPeerConnector"),
-         PeerConnector(aServerConn, aCallback, timeout),
-         clientConn(aClientConn),
-         splice(false),
-         resumingSession(false),
-         serverCertificateHandled(false)
-     {
-         request = aRequest;
-     }
-     /* PeerConnector API */
-     virtual SSL *initializeSsl();
-     virtual Security::ContextPtr getSslContext();
-     virtual void noteWantWrite();
-     virtual void noteSslNegotiationError(const int result, const int ssl_error, const int ssl_lib_error);
-     virtual void noteNegotiationDone(ErrorState *error);
-     /// Updates associated client connection manager members
-     /// if the server certificate was received from the server.
-     void handleServerCertificate();
-     /// Initiates the ssl_bump acl check in step3 SSL bump step to decide
-     /// about bumping, splicing or terminating the connection.
-     void checkForPeekAndSplice();
-     /// Callback function for ssl_bump acl check in step3  SSL bump step.
-     void checkForPeekAndSpliceDone(allow_t answer);
-     /// Handles the final bumping decision.
-     void checkForPeekAndSpliceMatched(const Ssl::BumpMode finalMode);
-     /// Guesses the final bumping decision when no ssl_bump rules match.
-     Ssl::BumpMode checkForPeekAndSpliceGuess() const;
-     /// Runs after the server certificate verified to update client
-     /// connection manager members
-     void serverCertificateVerified();
-     /// A wrapper function for checkForPeekAndSpliceDone for use with acl
-     static void cbCheckForPeekAndSpliceDone(allow_t answer, void *data);
- private:
-     Comm::ConnectionPointer clientConn; ///< TCP connection to the client
-     AsyncCall::Pointer callback; ///< we call this with the results
-     AsyncCall::Pointer closeHandler; ///< we call this when the connection closed
-     bool splice; ///< whether we are going to splice or not
-     bool resumingSession; ///< whether it is an SSL resuming session connection
-     bool serverCertificateHandled; ///< whether handleServerCertificate() succeeded
- };
  } // namespace Ssl
  
- #endif /* SQUID_PEER_CONNECTOR_H */
+ #endif /* USE_OPENSSL */
+ #endif /* SQUID_SRC_SSL_PEERCONNECTOR_H */
  
diff --cc src/ssl/bio.cc
index 3f82cc01b4fcf78c7d1d413dfbf7994dce027534,55e49fc07cf0644f793a49f4b4c0cd5136d3df89..91d5afd5dae0f54e0a5e6529cf0460a96f075d34
@@@ -863,7 -645,18 +870,17 @@@ squid_ssl_info(const SSL *ssl, int wher
      }
  }
  
- Ssl::Bio::sslFeatures::sslFeatures(): sslVersion(-1), compressMethod(-1), helloMsgSize(0), unknownCiphers(false), doHeartBeats(true), tlsTicketsExtension(false), hasTlsTicket(false), tlsStatusRequest(false), initialized_(false)
+ Ssl::Bio::sslFeatures::sslFeatures():
+     sslHelloVersion(-1),
+     sslVersion(-1),
+     compressMethod(-1),
+     helloMsgSize(0),
+     unknownCiphers(false),
+     doHeartBeats(true),
+     tlsTicketsExtension(false),
+     hasTlsTicket(false),
+     tlsStatusRequest(false),
 -    hasCcsOrNst(false),
+     initialized_(false)
  {
      memset(client_random, 0, SSL3_RANDOM_SIZE);
  }
diff --cc src/ssl/bio.h
index 3fc79f70b93a424b2be2f5f4f4fba726278273d6,c8f537a15972e8095abfe9e7eef77220f2847b60..0268874a2e569727d606f6a5f50ef907f13485dd
@@@ -265,8 -56,12 +266,9 @@@ public
          /// \retval >0 if the hello size is retrieved
          /// \retval 0 if the contents of the buffer are not enough
          /// \retval <0 if the contents of buf are not SSLv3 or TLS hello message
 -        int parseMsgHead(const MemBuf &);
 -        /// Parses msg buffer and return true if one of the Change Cipher Spec
 -        /// or New Session Ticket messages found
 -        bool checkForCcsOrNst(const unsigned char *msg, size_t size);
 +        int parseMsgHead(const SBuf &);
      public:
+         int sslHelloVersion; ///< The SSL hello message version
          int sslVersion; ///< The requested/used SSL version
          int compressMethod; ///< The requested/used compressed  method
          int helloMsgSize; ///< the hello message size
      /// Tells ssl connection to use BIO and monitor state via stateChanged()
      static void Link(SSL *ssl, BIO *bio);
  
 -    /// Prepare the rbuf buffer to accept hello data
 -    void prepReadBuf();
 -
      /// Reads data from socket and record them to a buffer
 -    int readAndBuffer(char *buf, int size, BIO *table, const char *description);
 +    int readAndBuffer(BIO *table, const char *description);
  
 -    const MemBuf &rBufData() {return rbuf;}
+     /// Return the TLS features requested by TLS client
+     const Bio::sslFeatures &receivedHelloFeatures() const {return receivedHelloFeatures_;}
 +    const SBuf &rBufData() {return rbuf;}
  protected:
      const int fd_; ///< the SSL socket we are reading and writing
 -    MemBuf rbuf;  ///< Used to buffer input data.
 +    SBuf rbuf;  ///< Used to buffer input data.
+     /// The features retrieved from client or Server TLS hello message
+     Bio::sslFeatures receivedHelloFeatures_;
  };
  
  /// BIO node to handle socket IO for squid client side
@@@ -394,12 -199,11 +397,16 @@@ public
      /// Sets the random number to use in client SSL HELLO message
      void setClientFeatures(const sslFeatures &features);
  
+     /// Parses server Hello message if it is recorded and extracts
+     /// server-supported features.
+     void extractHelloFeatures();
      bool resumingSession();
 +
 +    /// Reads Server hello message+certificates+ServerHelloDone message sent
 +    /// by server and buffer it to rbuf member
 +    int readAndBufferServerHelloMsg(BIO *table, const char *description);
 +
      /// The write hold state
      bool holdWrite() const {return holdWrite_;}
      /// Enables or disables the write hold state
index edcadbcbdcdcde785869538446f8e16e5a841f28,611c412d33063e41014b46f92669d1339be29e34..564da9cfe78186cfd03d5fd8e80810ba9e7671ac
  
  #include <cerrno>
  
- static void setSessionCallbacks(Security::ContextPtr ctx);
- Ipc::MemMap *SslSessionCache = NULL;
- const char *SslSessionCacheName = "ssl_session_cache";
 +// TODO: Move ssl_ex_index_* global variables from global.cc here.
 +int ssl_ex_index_ssl_untrusted_chain = -1;
 +
+ Ipc::MemMap *Ssl::SessionCache = NULL;
+ const char *Ssl::SessionCacheName = "ssl_session_cache";
  
  static Ssl::CertsIndexedList SquidUntrustedCerts;
  
Simple merge
diff --cc src/tunnel.cc
Simple merge