From: Christos Tsantilas Date: Thu, 22 May 2014 15:38:42 +0000 (+0300) Subject: merge from trunk r13421 X-Git-Tag: SQUID_3_5_0_1~89^2~17 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=20b79af2dab9e030c07ddb122c54ce705d2a140e;p=thirdparty%2Fsquid.git merge from trunk r13421 --- 20b79af2dab9e030c07ddb122c54ce705d2a140e diff --cc src/FwdState.cc index 8d11b75d6d,8dc6595849..07aa7166fa --- a/src/FwdState.cc +++ b/src/FwdState.cc @@@ -700,10 -708,10 +708,10 @@@ FwdState::connectDone(const Comm::Conne HttpRequest::Pointer requestPointer = request; AsyncCall::Pointer callback = asyncCall(17,4, - "FwdState::ConnectedToPeer", - FwdStatePeerAnswerDialer(&FwdState::connectedToPeer, this)); + "FwdState::ConnectedToPeer", + FwdStatePeerAnswerDialer(&FwdState::connectedToPeer, this)); Ssl::PeerConnector *connector = - new Ssl::PeerConnector(requestPointer, serverConnection(), callback); + new Ssl::PeerConnector(requestPointer, serverConnection(), clientConn, callback); AsyncJob::Start(connector); // will call our callback return; } diff --cc src/PeerPoolMgr.cc index 0000000000,04124000ea..14b99683e9 mode 000000,100644..100644 --- a/src/PeerPoolMgr.cc +++ b/src/PeerPoolMgr.cc @@@ -1,0 -1,284 +1,284 @@@ + #include "squid.h" + #include "base/AsyncJobCalls.h" + #include "base/RunnersRegistry.h" + #include "CachePeer.h" + #include "comm/Connection.h" + #include "comm/ConnOpener.h" + #include "Debug.h" + #include "fd.h" + #include "FwdState.h" + #include "globals.h" + #include "HttpRequest.h" + #include "neighbors.h" + #include "pconn.h" + #include "PeerPoolMgr.h" + #include "SquidConfig.h" + #if USE_OPENSSL + #include "ssl/PeerConnector.h" + #endif + + CBDATA_CLASS_INIT(PeerPoolMgr); + + #if USE_OPENSSL + /// Gives Ssl::PeerConnector access to Answer in the PeerPoolMgr callback dialer. + class MyAnswerDialer: public UnaryMemFunT, + public Ssl::PeerConnector::CbDialer + { + public: + MyAnswerDialer(const JobPointer &aJob, Method aMethod): + UnaryMemFunT(aJob, aMethod, Ssl::PeerConnectorAnswer()) {} + + /* Ssl::PeerConnector::CbDialer API */ + virtual Ssl::PeerConnectorAnswer &answer() { return arg1; } + }; + #endif + + PeerPoolMgr::PeerPoolMgr(CachePeer *aPeer): AsyncJob("PeerPoolMgr"), + peer(cbdataReference(aPeer)), + request(), + opener(), + securer(), + closer(), + addrUsed(0) + { + } + + PeerPoolMgr::~PeerPoolMgr() + { + cbdataReferenceDone(peer); + } + + void + PeerPoolMgr::start() + { + AsyncJob::start(); + + // ErrorState, getOutgoingAddress(), and other APIs may require a request. + // We fake one. TODO: Optionally send this request to peers? + request = new HttpRequest(Http::METHOD_OPTIONS, AnyP::PROTO_HTTP, "*"); + request->SetHost(peer->host); + + checkpoint("peer initialized"); + } + + void + PeerPoolMgr::swanSong() + { + AsyncJob::swanSong(); + } + + bool + PeerPoolMgr::validPeer() const + { + return peer && cbdataReferenceValid(peer) && peer->standby.pool; + } + + bool + PeerPoolMgr::doneAll() const + { + return !(validPeer() && peer->standby.limit) && AsyncJob::doneAll(); + } + + void + PeerPoolMgr::handleOpenedConnection(const CommConnectCbParams ¶ms) + { + opener = NULL; + + if (!validPeer()) { + debugs(48, 3, "peer gone"); + if (params.conn != NULL) + params.conn->close(); + return; + } + + if (params.flag != COMM_OK) { + /* it might have been a timeout with a partially open link */ + if (params.conn != NULL) + params.conn->close(); + peerConnectFailed(peer); + checkpoint("conn opening failure"); // may retry + return; + } + + Must(params.conn != NULL); + + #if USE_OPENSSL + // Handle SSL peers. + if (peer->use_ssl) { + typedef CommCbMemFunT CloserDialer; + closer = JobCallback(48, 3, CloserDialer, this, + PeerPoolMgr::handleSecureClosure); + comm_add_close_handler(params.conn->fd, closer); + + securer = asyncCall(48, 4, "PeerPoolMgr::handleSecuredPeer", + MyAnswerDialer(this, &PeerPoolMgr::handleSecuredPeer)); + Ssl::PeerConnector *connector = - new Ssl::PeerConnector(request, params.conn, securer); ++ new Ssl::PeerConnector(request, params.conn, NULL, securer); + AsyncJob::Start(connector); // will call our callback + return; + } + #endif + + pushNewConnection(params.conn); + } + + void + PeerPoolMgr::pushNewConnection(const Comm::ConnectionPointer &conn) + { + Must(validPeer()); + Must(Comm::IsConnOpen(conn)); + peer->standby.pool->push(conn, NULL /* domain */); + // push() will trigger a checkpoint() + } + + #if USE_OPENSSL + void + PeerPoolMgr::handleSecuredPeer(Ssl::PeerConnectorAnswer &answer) + { + Must(securer != NULL); + securer = NULL; + + if (closer != NULL) { + if (answer.conn != NULL) + comm_remove_close_handler(answer.conn->fd, closer); + else + closer->cancel("securing completed"); + closer = NULL; + } + + if (!validPeer()) { + debugs(48, 3, "peer gone"); + if (answer.conn != NULL) + answer.conn->close(); + return; + } + + if (answer.error.get()) { + if (answer.conn != NULL) + answer.conn->close(); + // PeerConnector calls peerConnectFailed() for us; + checkpoint("conn securing failure"); // may retry + return; + } + + pushNewConnection(answer.conn); + } + + void + PeerPoolMgr::handleSecureClosure(const CommCloseCbParams ¶ms) + { + Must(closer != NULL); + Must(securer != NULL); + securer->cancel("conn closed by a 3rd party"); + securer = NULL; + closer = NULL; + // allow the closing connection to fully close before we check again + Checkpoint(this, "conn closure while securing"); + } + #endif + + void + PeerPoolMgr::openNewConnection() + { + // KISS: Do nothing else when we are already doing something. + if (opener != NULL || securer != NULL || shutting_down) { + debugs(48, 7, "busy: " << opener << '|' << securer << '|' << shutting_down); + return; // there will be another checkpoint when we are done opening/securing + } + + // Do not talk to a peer until it is ready. + if (!neighborUp(peer)) // provides debugging + return; // there will be another checkpoint when peer is up + + // Do not violate peer limits. + if (!peerCanOpenMore(peer)) { // provides debugging + peer->standby.waitingForClose = true; // may already be true + return; // there will be another checkpoint when a peer conn closes + } + + // Do not violate global restrictions. + if (fdUsageHigh()) { + debugs(48, 7, "overwhelmed"); + peer->standby.waitingForClose = true; // may already be true + // There will be another checkpoint when a peer conn closes OR when + // a future pop() fails due to an empty pool. See PconnPool::pop(). + return; + } + + peer->standby.waitingForClose = false; + + Comm::ConnectionPointer conn = new Comm::Connection; + Must(peer->n_addresses); // guaranteed by neighborUp() above + // cycle through all available IP addresses + conn->remote = peer->addresses[addrUsed++ % peer->n_addresses]; + conn->remote.port(peer->http_port); + conn->peerType = STANDBY_POOL; // should be reset by peerSelect() + conn->setPeer(peer); + getOutgoingAddress(request.getRaw(), conn); + GetMarkingsToServer(request.getRaw(), *conn); + + const int ctimeout = peer->connect_timeout > 0 ? + peer->connect_timeout : Config.Timeout.peer_connect; + typedef CommCbMemFunT Dialer; + opener = JobCallback(48, 5, Dialer, this, PeerPoolMgr::handleOpenedConnection); + Comm::ConnOpener *cs = new Comm::ConnOpener(conn, opener, ctimeout); + AsyncJob::Start(cs); + } + + void + PeerPoolMgr::closeOldConnections(const int howMany) + { + debugs(48, 8, howMany); + peer->standby.pool->closeN(howMany); + } + + void + PeerPoolMgr::checkpoint(const char *reason) + { + if (!validPeer()) { + debugs(48, 3, reason << " and peer gone"); + return; // nothing to do after our owner dies; the job will quit + } + + const int count = peer->standby.pool->count(); + const int limit = peer->standby.limit; + debugs(48, 7, reason << " with " << count << " ? " << limit); + + if (count < limit) + openNewConnection(); + else if (count > limit) + closeOldConnections(count - limit); + } + + void + PeerPoolMgr::Checkpoint(const Pointer &mgr, const char *reason) + { + CallJobHere1(48, 5, mgr, PeerPoolMgr, checkpoint, reason); + } + + /// launches PeerPoolMgrs for peers configured with standby.limit + class PeerPoolMgrsRr: public RegisteredRunner + { + public: + /* RegisteredRunner API */ + virtual void useConfig() { syncConfig(); } + virtual void syncConfig(); + }; + + RunnerRegistrationEntry(PeerPoolMgrsRr); + + void + PeerPoolMgrsRr::syncConfig() + { + for (CachePeer *p = Config.peers; p; p = p->next) { + // On reconfigure, Squid deletes the old config (and old peers in it), + // so should always be dealing with a brand new configuration. + assert(!p->standby.mgr); + assert(!p->standby.pool); + if (p->standby.limit) { + p->standby.mgr = new PeerPoolMgr(p); + p->standby.pool = new PconnPool(p->name, p->standby.mgr); + AsyncJob::Start(p->standby.mgr.get()); + } + } + } diff --cc src/ssl/PeerConnector.cc index 339d127a31,4fa8be988d..62dcad1f3e --- a/src/ssl/PeerConnector.cc +++ b/src/ssl/PeerConnector.cc @@@ -30,13 -28,11 +29,13 @@@ CBDATA_NAMESPACED_CLASS_INIT(Ssl, PeerC Ssl::PeerConnector::PeerConnector( HttpRequestPointer &aRequest, const Comm::ConnectionPointer &aServerConn, + const Comm::ConnectionPointer &aClientConn, AsyncCall::Pointer &aCallback): - AsyncJob("Ssl::PeerConnector"), - request(aRequest), - serverConn(aServerConn), - clientConn(aClientConn), - callback(aCallback) + AsyncJob("Ssl::PeerConnector"), + request(aRequest), + serverConn(aServerConn), ++ clientConn(aClientConn), + callback(aCallback) { // if this throws, the caller's cb dialer is not our CbDialer Must(dynamic_cast(callback->getDialer())); @@@ -136,30 -134,6 +135,33 @@@ Ssl::PeerConnector::initializeSsl( if (peer->sslSession) SSL_set_session(ssl, peer->sslSession); + } else if (request->clientConnectionManager->sslBumpMode == Ssl::bumpPeek || request->clientConnectionManager->sslBumpMode == Ssl::bumpStare) { ++ // client connection is required for Peek or Stare mode in the case we need to splice ++ // or terminate client and server connections ++ assert(clientConn != NULL); + SSL *clientSsl = fd_table[request->clientConnectionManager->clientConnection->fd].ssl; + BIO *b = SSL_get_rbio(clientSsl); + Ssl::ClientBio *clnBio = static_cast(b->ptr); + const Ssl::Bio::sslFeatures &features = clnBio->getFeatures(); + if (features.sslVersion != -1) { + SSL_set_ssl_method(ssl, Ssl::method(features.toSquidSSLVersion())); + if (!features.serverName.empty()) + SSL_set_tlsext_host_name(ssl, features.serverName.c_str()); + if (!features.clientRequestedCiphers.empty()) + SSL_set_cipher_list(ssl, features.clientRequestedCiphers.c_str()); +#ifdef SSL_OP_NO_COMPRESSION /* XXX: OpenSSL 0.9.8k lacks SSL_OP_NO_COMPRESSION */ + if (features.compressMethod == 0) + SSL_set_options(ssl, SSL_OP_NO_COMPRESSION); +#endif + // Should we allow it for all protocols? + if (features.sslVersion >= 3) { + b = SSL_get_rbio(ssl); + Ssl::ServerBio *srvBio = static_cast(b->ptr); + srvBio->setClientFeatures(features); + srvBio->recordInput(true); + srvBio->mode(request->clientConnectionManager->sslBumpMode); + } + } } else { // While we are peeking at the certificate, we may not know the server // name that the client will request (after interception or CONNECT) @@@ -460,60 -376,40 +462,60 @@@ Ssl::PeerConnector::handleNegotiateErro unsigned long ssl_lib_error = SSL_ERROR_NONE; SSL *ssl = fd_table[fd].ssl; int ssl_error = SSL_get_error(ssl, ret); + BIO *b = SSL_get_rbio(ssl); + Ssl::ServerBio *srvBio = static_cast(b->ptr); #ifdef EPROTO - int sysErrNo = EPROTO; + int sysErrNo = EPROTO; #else - int sysErrNo = EACCES; + int sysErrNo = EACCES; #endif - switch (ssl_error) { + switch (ssl_error) { - case SSL_ERROR_WANT_READ: - Comm::SetSelect(fd, COMM_SELECT_READ, &NegotiateSsl, this, 0); - return; + case SSL_ERROR_WANT_READ: + Comm::SetSelect(fd, COMM_SELECT_READ, &NegotiateSsl, this, 0); + return; - case SSL_ERROR_WANT_WRITE: - if ((request->clientConnectionManager->sslBumpMode == Ssl::bumpPeek || request->clientConnectionManager->sslBumpMode == Ssl::bumpStare) && srvBio->holdWrite()) { - debugs(81, DBG_IMPORTANT, "hold write on SSL connection on FD " << fd); - checkForPeekAndSplice(false, Ssl::bumpNone); - return; - } - Comm::SetSelect(fd, COMM_SELECT_WRITE, &NegotiateSsl, this, 0); + case SSL_ERROR_WANT_WRITE: ++ if ((request->clientConnectionManager->sslBumpMode == Ssl::bumpPeek || request->clientConnectionManager->sslBumpMode == Ssl::bumpStare) && srvBio->holdWrite()) { ++ debugs(81, DBG_IMPORTANT, "hold write on SSL connection on FD " << fd); ++ checkForPeekAndSplice(false, Ssl::bumpNone); + return; ++ } + Comm::SetSelect(fd, COMM_SELECT_WRITE, &NegotiateSsl, this, 0); + return; - case SSL_ERROR_SSL: - case SSL_ERROR_SYSCALL: - ssl_lib_error = ERR_get_error(); + case SSL_ERROR_SSL: + case SSL_ERROR_SYSCALL: + ssl_lib_error = ERR_get_error(); - // 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 - // occure in the next SSL_connect call, and we will fail again. ++ // 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 ++ // occure in the next SSL_connect call, and we will fail again. +#if 1 - if ((request->clientConnectionManager->sslBumpMode == Ssl::bumpPeek || request->clientConnectionManager->sslBumpMode == Ssl::bumpStare) && srvBio->holdWrite()) { - debugs(81, DBG_IMPORTANT, "fwdNegotiateSSL: Error but, hold write on SSL connection on FD " << fd); - checkForPeekAndSplice(false, Ssl::bumpNone); - return; - } ++ if ((request->clientConnectionManager->sslBumpMode == Ssl::bumpPeek || request->clientConnectionManager->sslBumpMode == Ssl::bumpStare) && srvBio->holdWrite()) { ++ debugs(81, DBG_IMPORTANT, "fwdNegotiateSSL: Error but, hold write on SSL connection on FD " << fd); ++ checkForPeekAndSplice(false, Ssl::bumpNone); ++ return; ++ } +#endif + - // store/report errno when ssl_error is SSL_ERROR_SYSCALL, ssl_lib_error is 0, and ret is -1 - if (ssl_error == SSL_ERROR_SYSCALL && ret == -1 && ssl_lib_error == 0) - sysErrNo = errno; + // store/report errno when ssl_error is SSL_ERROR_SYSCALL, ssl_lib_error is 0, and ret is -1 + if (ssl_error == SSL_ERROR_SYSCALL && ret == -1 && ssl_lib_error == 0) + sysErrNo = errno; - debugs(83, DBG_IMPORTANT, "Error negotiating SSL on FD " << fd << - ": " << ERR_error_string(ssl_lib_error, NULL) << " (" << - ssl_error << "/" << ret << "/" << errno << ")"); + debugs(83, DBG_IMPORTANT, "Error negotiating SSL on FD " << fd << + ": " << ERR_error_string(ssl_lib_error, NULL) << " (" << + ssl_error << "/" << ret << "/" << errno << ")"); - break; // proceed to the general error handling code + break; // proceed to the general error handling code - default: - break; // no special error handling for all other errors - } + default: + break; // no special error handling for all other errors + } ErrorState *const anErr = ErrorState::NewForwarding(ERR_SECURE_CONNECT_FAIL, request.getRaw()); anErr->xerrno = sysErrNo; diff --cc src/ssl/PeerConnector.h index 73c7b27135,44732c47a0..6d1f671529 --- a/src/ssl/PeerConnector.h +++ b/src/ssl/PeerConnector.h @@@ -29,8 -29,8 +29,9 @@@ #ifndef SQUID_SSL_PEER_CONNECTOR_H #define SQUID_SSL_PEER_CONNECTOR_H - #include "base/AsyncJob.h" ++#include "acl/Acl.h" #include "base/AsyncCbdataCalls.h" + #include "base/AsyncJob.h" #include "ssl/support.h" #include diff --cc src/tunnel.cc index 519d1f927e,0ba51faf74..0b00abebd1 --- a/src/tunnel.cc +++ b/src/tunnel.cc @@@ -955,18 -948,18 +956,19 @@@ tunnelStart(ClientHttpRequest * http, i } void - TunnelStateData::connectToPeer() { + TunnelStateData::connectToPeer() + { const Comm::ConnectionPointer &srv = server.conn; + const Comm::ConnectionPointer &cln = client.conn; #if USE_OPENSSL if (CachePeer *p = srv->getPeer()) { if (p->use_ssl) { AsyncCall::Pointer callback = asyncCall(5,4, - "TunnelStateData::ConnectedToPeer", - MyAnswerDialer(&TunnelStateData::connectedToPeer, this)); + "TunnelStateData::ConnectedToPeer", + MyAnswerDialer(&TunnelStateData::connectedToPeer, this)); Ssl::PeerConnector *connector = - new Ssl::PeerConnector(request, srv, callback); + new Ssl::PeerConnector(request, srv, cln, callback); AsyncJob::Start(connector); // will call our callback return; }