2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 17 Request Forwarding */
12 #include "acl/FilledChecklist.h"
13 #include "base/AsyncCbdataCalls.h"
14 #include "CachePeer.h"
15 #include "client_side.h"
16 #include "comm/Loops.h"
17 #include "errorpage.h"
20 #include "helper/ResultCode.h"
21 #include "HttpRequest.h"
22 #include "neighbors.h"
23 #include "SquidConfig.h"
25 #include "ssl/cert_validate_message.h"
26 #include "ssl/Config.h"
27 #include "ssl/ErrorDetail.h"
28 #include "ssl/helper.h"
29 #include "ssl/PeerConnector.h"
30 #include "ssl/ServerBump.h"
31 #include "ssl/support.h"
33 CBDATA_NAMESPACED_CLASS_INIT(Ssl
, PeerConnector
);
35 Ssl::PeerConnector::PeerConnector(
36 HttpRequestPointer
&aRequest
,
37 const Comm::ConnectionPointer
&aServerConn
,
38 const Comm::ConnectionPointer
&aClientConn
,
39 AsyncCall::Pointer
&aCallback
,
40 const time_t timeout
):
41 AsyncJob("Ssl::PeerConnector"),
43 serverConn(aServerConn
),
44 clientConn(aClientConn
),
46 negotiationTimeout(timeout
),
47 startTime(squid_curtime
),
50 // if this throws, the caller's cb dialer is not our CbDialer
51 Must(dynamic_cast<CbDialer
*>(callback
->getDialer()));
54 Ssl::PeerConnector::~PeerConnector()
56 debugs(83, 5, "Peer connector " << this << " gone");
59 bool Ssl::PeerConnector::doneAll() const
61 return (!callback
|| callback
->canceled()) && AsyncJob::doneAll();
64 /// Preps connection and SSL state. Calls negotiate().
66 Ssl::PeerConnector::start()
70 if (prepareSocket()) {
77 Ssl::PeerConnector::commCloseHandler(const CommCloseCbParams
¶ms
)
79 debugs(83, 5, "FD " << params
.fd
<< ", Ssl::PeerConnector=" << params
.data
);
80 connectionClosed("Ssl::PeerConnector::commCloseHandler");
84 Ssl::PeerConnector::connectionClosed(const char *reason
)
91 Ssl::PeerConnector::prepareSocket()
93 const int fd
= serverConnection()->fd
;
94 if (!Comm::IsConnOpen(serverConn
) || fd_table
[serverConn
->fd
].closing()) {
95 connectionClosed("Ssl::PeerConnector::prepareSocket");
99 // watch for external connection closures
100 typedef CommCbMemFunT
<Ssl::PeerConnector
, CommCloseCbParams
> Dialer
;
101 closeHandler
= JobCallback(9, 5, Dialer
, this, Ssl::PeerConnector::commCloseHandler
);
102 comm_add_close_handler(fd
, closeHandler
);
107 Ssl::PeerConnector::initializeSsl()
109 SSL_CTX
*sslContext
= NULL
;
110 const CachePeer
*peer
= serverConnection()->getPeer();
111 const int fd
= serverConnection()->fd
;
114 assert(peer
->use_ssl
);
115 sslContext
= peer
->sslContext
;
117 sslContext
= ::Config
.ssl_client
.sslContext
;
122 SSL
*ssl
= Ssl::CreateClient(sslContext
, fd
, "server https start");
124 ErrorState
*anErr
= new ErrorState(ERR_SOCKET_FAILURE
, Http::scInternalServerError
, request
.getRaw());
125 anErr
->xerrno
= errno
;
126 debugs(83, DBG_IMPORTANT
, "Error allocating SSL handle: " << ERR_error_string(ERR_get_error(), NULL
));
133 SSL_set_ex_data(ssl
, ssl_ex_index_server
, peer
->ssldomain
);
138 SSL_set_ex_data(ssl
, ssl_ex_index_server
, peer
->name
);
143 SSL_set_ex_data(ssl
, ssl_ex_index_server
, peer
->host
);
145 if (peer
->sslSession
)
146 SSL_set_session(ssl
, peer
->sslSession
);
148 } else if (request
->clientConnectionManager
->sslBumpMode
== Ssl::bumpPeek
|| request
->clientConnectionManager
->sslBumpMode
== Ssl::bumpStare
) {
149 // client connection is required for Peek or Stare mode in the case we need to splice
150 // or terminate client and server connections
151 assert(clientConn
!= NULL
);
152 SSL
*clientSsl
= fd_table
[request
->clientConnectionManager
->clientConnection
->fd
].ssl
;
153 BIO
*b
= SSL_get_rbio(clientSsl
);
154 Ssl::ClientBio
*clnBio
= static_cast<Ssl::ClientBio
*>(b
->ptr
);
155 const Ssl::Bio::sslFeatures
&features
= clnBio
->getFeatures();
156 if (features
.sslVersion
!= -1) {
157 features
.applyToSSL(ssl
);
158 // Should we allow it for all protocols?
159 if (features
.sslVersion
>= 3) {
160 b
= SSL_get_rbio(ssl
);
161 Ssl::ServerBio
*srvBio
= static_cast<Ssl::ServerBio
*>(b
->ptr
);
162 srvBio
->setClientFeatures(features
);
163 srvBio
->recordInput(true);
164 srvBio
->mode(request
->clientConnectionManager
->sslBumpMode
);
167 const bool isConnectRequest
= request
->clientConnectionManager
.valid() &&
168 !request
->clientConnectionManager
->port
->flags
.isIntercepted();
169 if (isConnectRequest
)
170 SSL_set_ex_data(ssl
, ssl_ex_index_server
, (void*)request
->GetHost());
171 else if (!features
.serverName
.isEmpty())
172 SSL_set_ex_data(ssl
, ssl_ex_index_server
, (void*)features
.serverName
.c_str());
175 // While we are peeking at the certificate, we may not know the server
176 // name that the client will request (after interception or CONNECT)
177 // unless it was the CONNECT request with a user-typed address.
178 const char *hostname
= request
->GetHost();
179 const bool hostnameIsIp
= request
->GetHostIsNumeric();
180 const bool isConnectRequest
= request
->clientConnectionManager
.valid() &&
181 !request
->clientConnectionManager
->port
->flags
.isIntercepted();
182 if (!request
->flags
.sslPeek
|| isConnectRequest
)
183 SSL_set_ex_data(ssl
, ssl_ex_index_server
, (void*)hostname
);
185 // Use SNI TLS extension only when we connect directly
186 // to the origin server and we know the server host name.
188 Ssl::setClientSNI(ssl
, hostname
);
191 // If CertValidation Helper used do not lookup checklist for errors,
192 // but keep a list of errors to send it to CertValidator
193 if (!Ssl::TheConfig
.ssl_crt_validator
) {
194 // Create the ACL check list now, while we have access to more info.
195 // The list is used in ssl_verify_cb() and is freed in ssl_free().
196 if (acl_access
*acl
= ::Config
.ssl_client
.cert_error
) {
197 ACLFilledChecklist
*check
= new ACLFilledChecklist(acl
, request
.getRaw(), dash_str
);
198 // check->fd(fd); XXX: need client FD here
199 SSL_set_ex_data(ssl
, ssl_ex_index_cert_error_check
, check
);
203 // store peeked cert to check SQUID_X509_V_ERR_CERT_CHANGE
205 if (request
->clientConnectionManager
.valid() &&
206 request
->clientConnectionManager
->serverBump() &&
207 (peeked_cert
= request
->clientConnectionManager
->serverBump()->serverCert
.get())) {
208 CRYPTO_add(&(peeked_cert
->references
),1,CRYPTO_LOCK_X509
);
209 SSL_set_ex_data(ssl
, ssl_ex_index_ssl_peeked_cert
, peeked_cert
);
214 Ssl::PeerConnector::setReadTimeout()
217 if (negotiationTimeout
) {
218 const int timeUsed
= squid_curtime
- startTime
;
219 const int timeLeft
= max(0, static_cast<int>(negotiationTimeout
- timeUsed
));
220 timeToRead
= min(static_cast<int>(::Config
.Timeout
.read
), timeLeft
);
222 timeToRead
= ::Config
.Timeout
.read
;
223 AsyncCall::Pointer nil
;
224 commSetConnTimeout(serverConnection(), timeToRead
, nil
);
228 Ssl::PeerConnector::negotiateSsl()
230 if (!Comm::IsConnOpen(serverConnection()) || fd_table
[serverConnection()->fd
].closing())
233 const int fd
= serverConnection()->fd
;
234 SSL
*ssl
= fd_table
[fd
].ssl
;
235 const int result
= SSL_connect(ssl
);
237 handleNegotiateError(result
);
238 return; // we might be gone by now
241 if (serverConnection()->getPeer() && !SSL_session_reused(ssl
)) {
242 if (serverConnection()->getPeer()->sslSession
)
243 SSL_SESSION_free(serverConnection()->getPeer()->sslSession
);
245 serverConnection()->getPeer()->sslSession
= SSL_get1_session(ssl
);
255 Ssl::PeerConnector::sslFinalized()
257 const int fd
= serverConnection()->fd
;
258 SSL
*ssl
= fd_table
[fd
].ssl
;
260 if (request
->clientConnectionManager
.valid()) {
261 // remember the server certificate from the ErrorDetail object
262 if (Ssl::ServerBump
*serverBump
= request
->clientConnectionManager
->serverBump()) {
263 serverBump
->serverCert
.reset(SSL_get_peer_certificate(ssl
));
265 // remember validation errors, if any
266 if (Ssl::CertErrors
*errs
= static_cast<Ssl::CertErrors
*>(SSL_get_ex_data(ssl
, ssl_ex_index_ssl_errors
)))
267 serverBump
->sslErrors
= cbdataReference(errs
);
271 if (Ssl::TheConfig
.ssl_crt_validator
) {
272 Ssl::CertValidationRequest validationRequest
;
273 // WARNING: Currently we do not use any locking for any of the
274 // members of the Ssl::CertValidationRequest class. In this code the
275 // Ssl::CertValidationRequest object used only to pass data to
276 // Ssl::CertValidationHelper::submit method.
277 validationRequest
.ssl
= ssl
;
278 validationRequest
.domainName
= request
->GetHost();
279 if (Ssl::CertErrors
*errs
= static_cast<Ssl::CertErrors
*>(SSL_get_ex_data(ssl
, ssl_ex_index_ssl_errors
)))
280 // validationRequest disappears on return so no need to cbdataReference
281 validationRequest
.errors
= errs
;
283 validationRequest
.errors
= NULL
;
285 debugs(83, 5, "Sending SSL certificate for validation to ssl_crtvd.");
286 Ssl::CertValidationHelper::GetInstance()->sslSubmit(validationRequest
, sslCrtvdHandleReplyWrapper
, this);
288 } catch (const std::exception
&e
) {
289 debugs(83, DBG_IMPORTANT
, "ERROR: Failed to compose ssl_crtvd " <<
290 "request for " << validationRequest
.domainName
<<
291 " certificate: " << e
.what() << "; will now block to " <<
292 "validate that certificate.");
293 // fall through to do blocking in-process generation.
294 ErrorState
*anErr
= new ErrorState(ERR_GATEWAY_FAILURE
, Http::scInternalServerError
, request
.getRaw());
296 if (serverConnection()->getPeer()) {
297 peerConnectFailed(serverConnection()->getPeer());
306 void switchToTunnel(HttpRequest
*request
, Comm::ConnectionPointer
& clientConn
, Comm::ConnectionPointer
&srvConn
);
309 Ssl::PeerConnector::cbCheckForPeekAndSpliceDone(allow_t answer
, void *data
)
311 Ssl::PeerConnector
*peerConnect
= (Ssl::PeerConnector
*) data
;
312 peerConnect
->checkForPeekAndSpliceDone((Ssl::BumpMode
)answer
.kind
);
316 Ssl::PeerConnector::checkForPeekAndSplice()
318 SSL
*ssl
= fd_table
[serverConn
->fd
].ssl
;
319 // Mark Step3 of bumping
320 if (request
->clientConnectionManager
.valid()) {
321 if (Ssl::ServerBump
*serverBump
= request
->clientConnectionManager
->serverBump()) {
322 serverBump
->step
= Ssl::bumpStep3
;
323 if (!serverBump
->serverCert
.get())
324 serverBump
->serverCert
.reset(SSL_get_peer_certificate(ssl
));
328 ACLFilledChecklist
*acl_checklist
= new ACLFilledChecklist(
329 ::Config
.accessList
.ssl_bump
,
330 request
.getRaw(), NULL
);
331 acl_checklist
->nonBlockingCheck(Ssl::PeerConnector::cbCheckForPeekAndSpliceDone
, this);
335 Ssl::PeerConnector::checkForPeekAndSpliceDone(Ssl::BumpMode
const action
)
337 SSL
*ssl
= fd_table
[serverConn
->fd
].ssl
;
338 BIO
*b
= SSL_get_rbio(ssl
);
339 Ssl::ServerBio
*srvBio
= static_cast<Ssl::ServerBio
*>(b
->ptr
);
340 debugs(83,5, "Will check for peek and splice on FD " << serverConn
->fd
);
342 Ssl::BumpMode finalAction
= action
;
343 // adjust the final bumping mode if needed
344 if (finalAction
< Ssl::bumpSplice
)
345 finalAction
= Ssl::bumpBump
;
347 if (finalAction
== Ssl::bumpSplice
&& !srvBio
->canSplice())
348 finalAction
= Ssl::bumpBump
;
349 else if (finalAction
== Ssl::bumpBump
&& !srvBio
->canBump())
350 finalAction
= Ssl::bumpSplice
;
352 // Record final decision
353 if (request
->clientConnectionManager
.valid()) {
354 request
->clientConnectionManager
->sslBumpMode
= finalAction
;
355 request
->clientConnectionManager
->serverBump()->act
.step3
= finalAction
;
358 if (finalAction
== Ssl::bumpTerminate
) {
359 comm_close(serverConn
->fd
);
360 comm_close(clientConn
->fd
);
361 } else if (finalAction
!= Ssl::bumpSplice
) {
362 //Allow write, proceed with the connection
363 srvBio
->holdWrite(false);
364 srvBio
->recordInput(false);
365 Comm::SetSelect(serverConn
->fd
, COMM_SELECT_WRITE
, &NegotiateSsl
, this, 0);
366 debugs(83,5, "Retry the fwdNegotiateSSL on FD " << serverConn
->fd
);
369 // Ssl Negotiation stops here. Last SSL checks for valid certificates
370 // and if done, switch to tunnel mode
372 switchToTunnel(request
.getRaw(), clientConn
, serverConn
);
377 Ssl::PeerConnector::sslCrtvdHandleReplyWrapper(void *data
, Ssl::CertValidationResponse
const &validationResponse
)
379 Ssl::PeerConnector
*connector
= (Ssl::PeerConnector
*)(data
);
380 connector
->sslCrtvdHandleReply(validationResponse
);
384 Ssl::PeerConnector::sslCrtvdHandleReply(Ssl::CertValidationResponse
const &validationResponse
)
386 Ssl::CertErrors
*errs
= NULL
;
387 Ssl::ErrorDetail
*errDetails
= NULL
;
388 bool validatorFailed
= false;
389 if (!Comm::IsConnOpen(serverConnection())) {
393 debugs(83,5, request
->GetHost() << " cert validation result: " << validationResponse
.resultCode
);
395 if (validationResponse
.resultCode
== ::Helper::Error
)
396 errs
= sslCrtvdCheckForErrors(validationResponse
, errDetails
);
397 else if (validationResponse
.resultCode
!= ::Helper::Okay
)
398 validatorFailed
= true;
400 if (!errDetails
&& !validatorFailed
) {
402 switchToTunnel(request
.getRaw(), clientConn
, serverConn
);
408 ErrorState
*anErr
= NULL
;
409 if (validatorFailed
) {
410 anErr
= new ErrorState(ERR_GATEWAY_FAILURE
, Http::scInternalServerError
, request
.getRaw());
413 // Check the list error with
414 if (errDetails
&& request
->clientConnectionManager
.valid()) {
415 // remember the server certificate from the ErrorDetail object
416 if (Ssl::ServerBump
*serverBump
= request
->clientConnectionManager
->serverBump()) {
417 // remember validation errors, if any
419 if (serverBump
->sslErrors
)
420 cbdataReferenceDone(serverBump
->sslErrors
);
421 serverBump
->sslErrors
= cbdataReference(errs
);
426 anErr
= new ErrorState(ERR_SECURE_CONNECT_FAIL
, Http::scServiceUnavailable
, request
.getRaw());
427 anErr
->detail
= errDetails
;
428 /*anErr->xerrno= Should preserved*/
432 if (serverConnection()->getPeer()) {
433 peerConnectFailed(serverConnection()->getPeer());
439 /// Checks errors in the cert. validator response against sslproxy_cert_error.
440 /// The first honored error, if any, is returned via errDetails parameter.
441 /// The method returns all seen errors except SSL_ERROR_NONE as Ssl::CertErrors.
443 Ssl::PeerConnector::sslCrtvdCheckForErrors(Ssl::CertValidationResponse
const &resp
, Ssl::ErrorDetail
*& errDetails
)
445 Ssl::CertErrors
*errs
= NULL
;
447 ACLFilledChecklist
*check
= NULL
;
448 if (acl_access
*acl
= ::Config
.ssl_client
.cert_error
)
449 check
= new ACLFilledChecklist(acl
, request
.getRaw(), dash_str
);
451 SSL
*ssl
= fd_table
[serverConnection()->fd
].ssl
;
452 typedef Ssl::CertValidationResponse::RecvdErrors::const_iterator SVCRECI
;
453 for (SVCRECI i
= resp
.errors
.begin(); i
!= resp
.errors
.end(); ++i
) {
454 debugs(83, 7, "Error item: " << i
->error_no
<< " " << i
->error_reason
);
456 assert(i
->error_no
!= SSL_ERROR_NONE
);
459 bool allowed
= false;
461 check
->sslErrors
= new Ssl::CertErrors(Ssl::CertError(i
->error_no
, i
->cert
.get()));
462 if (check
->fastCheck() == ACCESS_ALLOWED
)
465 // else the Config.ssl_client.cert_error access list is not defined
466 // and the first error will cause the error page
469 debugs(83, 3, "bypassing SSL error " << i
->error_no
<< " in " << "buffer");
471 debugs(83, 5, "confirming SSL error " << i
->error_no
);
472 X509
*brokenCert
= i
->cert
.get();
473 Ssl::X509_Pointer
peerCert(SSL_get_peer_certificate(ssl
));
474 const char *aReason
= i
->error_reason
.empty() ? NULL
: i
->error_reason
.c_str();
475 errDetails
= new Ssl::ErrorDetail(i
->error_no
, peerCert
.get(), brokenCert
, aReason
);
478 delete check
->sslErrors
;
479 check
->sslErrors
= NULL
;
484 errs
= new Ssl::CertErrors(Ssl::CertError(i
->error_no
, i
->cert
.get()));
486 errs
->push_back_unique(Ssl::CertError(i
->error_no
, i
->cert
.get()));
494 /// A wrapper for Comm::SetSelect() notifications.
496 Ssl::PeerConnector::NegotiateSsl(int, void *data
)
498 PeerConnector
*pc
= static_cast<PeerConnector
*>(data
);
499 // Use job calls to add done() checks and other job logic/protections.
500 CallJobHere(83, 7, pc
, Ssl::PeerConnector
, negotiateSsl
);
504 Ssl::PeerConnector::handleNegotiateError(const int ret
)
506 const int fd
= serverConnection()->fd
;
507 unsigned long ssl_lib_error
= SSL_ERROR_NONE
;
508 SSL
*ssl
= fd_table
[fd
].ssl
;
509 int ssl_error
= SSL_get_error(ssl
, ret
);
510 BIO
*b
= SSL_get_rbio(ssl
);
511 Ssl::ServerBio
*srvBio
= static_cast<Ssl::ServerBio
*>(b
->ptr
);
514 int sysErrNo
= EPROTO
;
516 int sysErrNo
= EACCES
;
521 case SSL_ERROR_WANT_READ
:
523 Comm::SetSelect(fd
, COMM_SELECT_READ
, &NegotiateSsl
, this, 0);
526 case SSL_ERROR_WANT_WRITE
:
527 if ((request
->clientConnectionManager
->sslBumpMode
== Ssl::bumpPeek
|| request
->clientConnectionManager
->sslBumpMode
== Ssl::bumpStare
) && srvBio
->holdWrite()) {
528 debugs(81, DBG_IMPORTANT
, "hold write on SSL connection on FD " << fd
);
529 checkForPeekAndSplice();
532 Comm::SetSelect(fd
, COMM_SELECT_WRITE
, &NegotiateSsl
, this, 0);
536 case SSL_ERROR_SYSCALL
:
537 ssl_lib_error
= ERR_get_error();
539 // If we are in peek-and-splice mode and still we did not write to
540 // server yet, try to see if we should splice.
541 // In this case the connection can be saved.
542 // If the checklist decision is do not splice a new error will
543 // occure in the next SSL_connect call, and we will fail again.
545 if ((request
->clientConnectionManager
->sslBumpMode
== Ssl::bumpPeek
|| request
->clientConnectionManager
->sslBumpMode
== Ssl::bumpStare
) && srvBio
->holdWrite()) {
546 debugs(81, 3, "Error (" << ERR_error_string(ssl_lib_error
, NULL
) << ") but, hold write on SSL connection on FD " << fd
);
547 checkForPeekAndSplice();
552 // store/report errno when ssl_error is SSL_ERROR_SYSCALL, ssl_lib_error is 0, and ret is -1
553 if (ssl_error
== SSL_ERROR_SYSCALL
&& ret
== -1 && ssl_lib_error
== 0)
556 debugs(83, DBG_IMPORTANT
, "Error negotiating SSL on FD " << fd
<<
557 ": " << ERR_error_string(ssl_lib_error
, NULL
) << " (" <<
558 ssl_error
<< "/" << ret
<< "/" << errno
<< ")");
560 break; // proceed to the general error handling code
563 break; // no special error handling for all other errors
566 ErrorState
*const anErr
= ErrorState::NewForwarding(ERR_SECURE_CONNECT_FAIL
, request
.getRaw());
567 anErr
->xerrno
= sysErrNo
;
569 Ssl::ErrorDetail
*errFromFailure
= (Ssl::ErrorDetail
*)SSL_get_ex_data(ssl
, ssl_ex_index_ssl_error_detail
);
570 if (errFromFailure
!= NULL
) {
571 // The errFromFailure is attached to the ssl object
572 // and will be released when ssl object destroyed.
573 // Copy errFromFailure to a new Ssl::ErrorDetail object
574 anErr
->detail
= new Ssl::ErrorDetail(*errFromFailure
);
576 // server_cert can be NULL here
577 X509
*server_cert
= SSL_get_peer_certificate(ssl
);
578 anErr
->detail
= new Ssl::ErrorDetail(SQUID_ERR_SSL_HANDSHAKE
, server_cert
, NULL
);
579 X509_free(server_cert
);
582 if (ssl_lib_error
!= SSL_ERROR_NONE
)
583 anErr
->detail
->setLibError(ssl_lib_error
);
585 if (request
->clientConnectionManager
.valid()) {
586 // remember the server certificate from the ErrorDetail object
587 if (Ssl::ServerBump
*serverBump
= request
->clientConnectionManager
->serverBump()) {
588 serverBump
->serverCert
.resetAndLock(anErr
->detail
->peerCert());
590 // remember validation errors, if any
591 if (Ssl::CertErrors
*errs
= static_cast<Ssl::CertErrors
*>(SSL_get_ex_data(ssl
, ssl_ex_index_ssl_errors
)))
592 serverBump
->sslErrors
= cbdataReference(errs
);
595 // For intercepted connections, set the host name to the server
596 // certificate CN. Otherwise, we just hope that CONNECT is using
597 // a user-entered address (a host name or a user-entered IP).
598 const bool isConnectRequest
= !request
->clientConnectionManager
->port
->flags
.isIntercepted();
599 if (request
->flags
.sslPeek
&& !isConnectRequest
) {
600 if (X509
*srvX509
= anErr
->detail
->peerCert()) {
601 if (const char *name
= Ssl::CommonHostName(srvX509
)) {
602 request
->SetHost(name
);
603 debugs(83, 3, HERE
<< "reset request host: " << name
);
613 Ssl::PeerConnector::bail(ErrorState
*error
)
615 Must(error
); // or the recepient will not know there was a problem
617 // XXX: forward.cc calls peerConnectSucceeded() after an OK TCP connect but
618 // we call peerConnectFailed() if SSL failed afterwards. Is that OK?
619 // It is not clear whether we should call peerConnectSucceeded/Failed()
620 // based on TCP results, SSL results, or both. And the code is probably not
621 // consistent in this aspect across tunnelling and forwarding modules.
622 if (CachePeer
*p
= serverConnection()->getPeer())
623 peerConnectFailed(p
);
625 Must(callback
!= NULL
);
626 CbDialer
*dialer
= dynamic_cast<CbDialer
*>(callback
->getDialer());
628 dialer
->answer().error
= error
;
631 // Our job is done. The callabck recepient will probably close the failed
632 // peer connection and try another peer or go direct (if possible). We
633 // can close the connection ourselves (our error notification would reach
634 // the recepient before the fd-closure notification), but we would rather
635 // minimize the number of fd-closure notifications and let the recepient
636 // manage the TCP state of the connection.
640 Ssl::PeerConnector::callBack()
642 AsyncCall::Pointer cb
= callback
;
643 // Do this now so that if we throw below, swanSong() assert that we _tried_
644 // to call back holds.
645 callback
= NULL
; // this should make done() true
647 // remove close handler
648 comm_remove_close_handler(serverConnection()->fd
, closeHandler
);
650 CbDialer
*dialer
= dynamic_cast<CbDialer
*>(cb
->getDialer());
652 dialer
->answer().conn
= serverConnection();
653 ScheduleCallHere(cb
);
657 Ssl::PeerConnector::swanSong()
659 // XXX: unregister fd-closure monitoring and CommSetSelect interest, if any
660 AsyncJob::swanSong();
661 assert(!callback
); // paranoid: we have not left the caller waiting
665 Ssl::PeerConnector::status() const
670 // TODO: redesign AsyncJob::status() API to avoid this
671 // id and stop reason reporting duplication.
673 if (stopReason
!= NULL
) {
674 buf
.Printf("Stopped, reason:");
675 buf
.Printf("%s",stopReason
);
677 if (serverConn
!= NULL
)
678 buf
.Printf(" FD %d", serverConn
->fd
);
679 buf
.Printf(" %s%u]", id
.Prefix
, id
.value
);
682 return buf
.content();
685 /* PeerConnectorAnswer */
687 Ssl::PeerConnectorAnswer::~PeerConnectorAnswer()
693 Ssl::operator <<(std::ostream
&os
, const Ssl::PeerConnectorAnswer
&answer
)
695 return os
<< answer
.conn
<< ", " << answer
.error
;