2 * DEBUG: section 17 Request Forwarding
3 * AUTHOR: Duane Wessels
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
34 #include "AccessLogEntry.h"
35 #include "acl/FilledChecklist.h"
36 #include "acl/Gadgets.h"
37 #include "anyp/PortCfg.h"
38 #include "CacheManager.h"
39 #include "client_side.h"
40 #include "comm/Connection.h"
41 #include "comm/ConnOpener.h"
42 #include "comm/Loops.h"
43 #include "CommCalls.h"
44 #include "errorpage.h"
49 #include "hier_code.h"
50 #include "HttpReply.h"
51 #include "HttpRequest.h"
52 #include "icmp/net_db.h"
53 #include "ip/Intercept.h"
54 #include "ip/QosConfig.h"
56 #include "MemObject.h"
57 #include "mgr/Registration.h"
59 #include "PeerSelectState.h"
61 #include "SquidTime.h"
64 #include "ssl/support.h"
65 #include "ssl/ErrorDetail.h"
66 #include "ssl/ServerBump.h"
72 static PSC fwdPeerSelectionCompleteWrapper
;
73 static CLCB fwdServerClosedWrapper
;
75 static PF fwdNegotiateSSLWrapper
;
77 static CNCB fwdConnectDoneWrapper
;
81 #define MAX_FWD_STATS_IDX 9
82 static int FwdReplyCodes
[MAX_FWD_STATS_IDX
+ 1][HTTP_INVALID_HEADER
+ 1];
84 static PconnPool
*fwdPconnPool
= new PconnPool("server-side");
85 CBDATA_CLASS_INIT(FwdState
);
88 FwdState::abort(void* d
)
90 FwdState
* fwd
= (FwdState
*)d
;
91 Pointer tmp
= fwd
; // Grab a temporary pointer to keep the object alive during our scope.
93 if (Comm::IsConnOpen(fwd
->serverConnection())) {
94 comm_remove_close_handler(fwd
->serverConnection()->fd
, fwdServerClosedWrapper
, fwd
);
95 debugs(17, 3, HERE
<< "store entry aborted; closing " <<
96 fwd
->serverConnection());
97 fwd
->serverConnection()->close();
99 debugs(17, 7, HERE
<< "store entry aborted; no connection to close");
101 fwd
->serverDestinations
.clean();
105 /**** PUBLIC INTERFACE ********************************************************/
107 FwdState::FwdState(const Comm::ConnectionPointer
&client
, StoreEntry
* e
, HttpRequest
* r
, const AccessLogEntryPointer
&alp
):
110 debugs(17, 2, HERE
<< "Forwarding client request " << client
<< ", url=" << e
->url() );
113 request
= HTTPMSGLOCK(r
);
114 pconnRace
= raceImpossible
;
115 start_t
= squid_curtime
;
116 serverDestinations
.reserve(Config
.forward_max_tries
);
118 EBIT_SET(e
->flags
, ENTRY_FWD_HDR_WAIT
);
121 // Called once, right after object creation, when it is safe to set self
122 void FwdState::start(Pointer aSelf
)
124 // Protect ourselves from being destroyed when the only Server pointing
125 // to us is gone (while we expect to talk to more Servers later).
126 // Once we set self, we are responsible for clearing it when we do not
127 // expect to talk to any servers.
128 self
= aSelf
; // refcounted
130 // We hope that either the store entry aborts or peer is selected.
131 // Otherwise we are going to leak our object.
133 entry
->registerAbort(FwdState::abort
, this);
135 #if STRICT_ORIGINAL_DST
136 // Bug 3243: CVE 2009-0801
137 // Bypass of browser same-origin access control in intercepted communication
138 // To resolve this we must force DIRECT and only to the original client destination.
139 const bool isIntercepted
= request
&& !request
->flags
.redirected
&& (request
->flags
.intercepted
|| request
->flags
.spoof_client_ip
);
140 const bool useOriginalDst
= Config
.onoff
.client_dst_passthru
|| (request
&& !request
->flags
.hostVerified
);
141 if (isIntercepted
&& useOriginalDst
) {
142 selectPeerForIntercepted();
143 // 3.2 does not suppro re-wrapping inside CONNECT.
144 // our only alternative is to fake destination "found" and continue with the forwarding.
145 startConnectionOrFail();
150 // do full route options selection
151 peerSelect(&serverDestinations
, request
, entry
, fwdPeerSelectionCompleteWrapper
, this);
154 #if STRICT_ORIGINAL_DST
155 /// bypasses peerSelect() when dealing with intercepted requests
157 FwdState::selectPeerForIntercepted()
159 // use pinned connection if available
160 Comm::ConnectionPointer p
;
161 if (ConnStateData
*client
= request
->pinnedConnection())
162 p
= client
->validatePinnedConnection(request
, NULL
);
164 if (Comm::IsConnOpen(p
)) {
165 /* duplicate peerSelectPinned() effects */
166 p
->peerType
= PINNED
;
167 entry
->ping_status
= PING_DONE
; /* Skip ICP */
169 debugs(17, 3, HERE
<< "reusing a pinned conn: " << *p
);
170 serverDestinations
.push_back(p
);
173 // use client original destination as second preferred choice
174 p
= new Comm::Connection();
175 p
->peerType
= ORIGINAL_DST
;
176 p
->remote
= clientConn
->local
;
177 getOutgoingAddress(request
, p
);
179 debugs(17, 3, HERE
<< "using client original destination: " << *p
);
180 serverDestinations
.push_back(p
);
185 FwdState::completed()
187 if (flags
.forward_completed
== 1) {
188 debugs(17, DBG_IMPORTANT
, HERE
<< "FwdState::completed called on a completed request! Bad!");
192 flags
.forward_completed
= 1;
194 if (EBIT_TEST(entry
->flags
, ENTRY_ABORTED
)) {
195 debugs(17, 3, HERE
<< "entry aborted");
199 #if URL_CHECKSUM_DEBUG
201 entry
->mem_obj
->checkUrlChecksum();
204 if (entry
->store_status
== STORE_PENDING
) {
205 if (entry
->isEmpty()) {
207 errorAppendEntry(entry
, err
);
210 EBIT_CLR(entry
->flags
, ENTRY_FWD_HDR_WAIT
);
212 entry
->releaseRequest();
216 if (storePendingNClients(entry
) > 0)
217 assert(!EBIT_TEST(entry
->flags
, ENTRY_FWD_HDR_WAIT
));
221 FwdState::~FwdState()
223 debugs(17, 3, HERE
<< "FwdState destructor starting");
225 if (! flags
.forward_completed
)
230 HTTPMSGUNLOCK(request
);
234 entry
->unregisterAbort();
240 if (calls
.connector
!= NULL
) {
241 calls
.connector
->cancel("FwdState destructed");
242 calls
.connector
= NULL
;
245 if (Comm::IsConnOpen(serverConn
)) {
246 comm_remove_close_handler(serverConnection()->fd
, fwdServerClosedWrapper
, this);
247 debugs(17, 3, HERE
<< "closing FD " << serverConnection()->fd
);
251 serverDestinations
.clean();
253 debugs(17, 3, HERE
<< "FwdState destructor done");
257 * This is the entry point for client-side to start forwarding
258 * a transaction. It is a static method that may or may not
259 * allocate a FwdState.
262 FwdState::Start(const Comm::ConnectionPointer
&clientConn
, StoreEntry
*entry
, HttpRequest
*request
, const AccessLogEntryPointer
&al
)
265 * client_addr == no_addr indicates this is an "internal" request
266 * from peer_digest.c, asn.c, netdb.c, etc and should always
267 * be allowed. yuck, I know.
270 if ( Config
.accessList
.miss
&& !request
->client_addr
.IsNoAddr() &&
271 request
->protocol
!= AnyP::PROTO_INTERNAL
&& request
->protocol
!= AnyP::PROTO_CACHE_OBJECT
) {
273 * Check if this host is allowed to fetch MISSES from us (miss_access)
275 ACLFilledChecklist
ch(Config
.accessList
.miss
, request
, NULL
);
276 ch
.src_addr
= request
->client_addr
;
277 ch
.my_addr
= request
->my_addr
;
278 if (ch
.fastCheck() == ACCESS_DENIED
) {
280 page_id
= aclGetDenyInfoPage(&Config
.denyInfoList
, AclMatchedName
, 1);
282 if (page_id
== ERR_NONE
)
283 page_id
= ERR_FORWARDING_DENIED
;
285 ErrorState
*anErr
= new ErrorState(page_id
, HTTP_FORBIDDEN
, request
);
286 errorAppendEntry(entry
, anErr
); // frees anErr
291 debugs(17, 3, HERE
<< "'" << entry
->url() << "'");
293 * This seems like an odd place to bind mem_obj and request.
294 * Might want to assert that request is NULL at this point
296 entry
->mem_obj
->request
= HTTPMSGLOCK(request
);
297 #if URL_CHECKSUM_DEBUG
299 entry
->mem_obj
->checkUrlChecksum();
304 ErrorState
*anErr
= new ErrorState(ERR_SHUTTING_DOWN
, HTTP_SERVICE_UNAVAILABLE
, request
);
305 errorAppendEntry(entry
, anErr
); // frees anErr
309 switch (request
->protocol
) {
311 case AnyP::PROTO_INTERNAL
:
312 internalStart(clientConn
, request
, entry
);
315 case AnyP::PROTO_CACHE_OBJECT
:
316 CacheManager::GetInstance()->Start(clientConn
, request
, entry
);
319 case AnyP::PROTO_URN
:
320 urnStart(request
, entry
);
324 FwdState::Pointer fwd
= new FwdState(clientConn
, entry
, request
, al
);
333 FwdState::fwdStart(const Comm::ConnectionPointer
&clientConn
, StoreEntry
*entry
, HttpRequest
*request
)
335 // Hides AccessLogEntry.h from code that does not supply ALE anyway.
336 Start(clientConn
, entry
, request
, NULL
);
340 FwdState::startConnectionOrFail()
342 debugs(17, 3, HERE
<< entry
->url());
344 if (serverDestinations
.size() > 0) {
345 // Ditch error page if it was created before.
346 // A new one will be created if there's another problem
350 // Update the logging information about this new server connection.
351 // Done here before anything else so the errors get logged for
352 // this server link regardless of what happens when connecting to it.
353 // IF sucessfuly connected this top destination will become the serverConnection().
354 request
->hier
.note(serverDestinations
[0], request
->GetHost());
355 request
->clearError();
359 debugs(17, 3, HERE
<< "Connection failed: " << entry
->url());
361 ErrorState
*anErr
= new ErrorState(ERR_CANNOT_FORWARD
, HTTP_INTERNAL_SERVER_ERROR
, request
);
363 } // else use actual error from last connection attempt
365 if (request
->flags
.sslPeek
&& request
->clientConnectionManager
.valid()) {
366 errorAppendEntry(entry
, err
); // will free err
368 CallJobHere1(17, 4, request
->clientConnectionManager
, ConnStateData
,
369 ConnStateData::httpsPeeked
, Comm::ConnectionPointer(NULL
));
372 self
= NULL
; // refcounted
377 FwdState::fail(ErrorState
* errorState
)
379 debugs(17, 3, HERE
<< err_type_str
[errorState
->type
] << " \"" << httpStatusString(errorState
->httpStatus
) << "\"\n\t" << entry
->url() );
384 if (!errorState
->request
)
385 errorState
->request
= HTTPMSGLOCK(request
);
387 if (pconnRace
== racePossible
&& err
->type
== ERR_ZERO_SIZE_OBJECT
) {
388 debugs(17, 5, HERE
<< "pconn race happened");
389 pconnRace
= raceHappened
;
394 * Frees fwdState without closing FD or generating an abort
397 FwdState::unregister(Comm::ConnectionPointer
&conn
)
399 debugs(17, 3, HERE
<< entry
->url() );
400 assert(serverConnection() == conn
);
401 assert(Comm::IsConnOpen(conn
));
402 comm_remove_close_handler(conn
->fd
, fwdServerClosedWrapper
, this);
406 // Legacy method to be removed in favor of the above as soon as possible
408 FwdState::unregister(int fd
)
410 debugs(17, 3, HERE
<< entry
->url() );
411 assert(fd
== serverConnection()->fd
);
412 unregister(serverConn
);
416 * server-side modules call fwdComplete() when they are done
417 * downloading an object. Then, we either 1) re-forward the
418 * request somewhere else if needed, or 2) call storeComplete()
424 debugs(17, 3, HERE
<< entry
->url() << "\n\tstatus " << entry
->getReply()->sline
.status
);
425 #if URL_CHECKSUM_DEBUG
427 entry
->mem_obj
->checkUrlChecksum();
430 logReplyStatus(n_tries
, entry
->getReply()->sline
.status
);
433 debugs(17, 3, HERE
<< "re-forwarding " << entry
->getReply()->sline
.status
<< " " << entry
->url());
435 if (Comm::IsConnOpen(serverConn
))
436 unregister(serverConn
);
440 // drop the last path off the selection list. try the next one.
441 serverDestinations
.shift();
442 startConnectionOrFail();
445 if (Comm::IsConnOpen(serverConn
))
446 debugs(17, 3, HERE
<< "server FD " << serverConnection()->fd
<< " not re-forwarding status " << entry
->getReply()->sline
.status
);
448 debugs(17, 3, HERE
<< "server (FD closed) not re-forwarding status " << entry
->getReply()->sline
.status
);
449 EBIT_CLR(entry
->flags
, ENTRY_FWD_HDR_WAIT
);
452 if (!Comm::IsConnOpen(serverConn
))
455 self
= NULL
; // refcounted
459 /**** CALLBACK WRAPPERS ************************************************************/
462 fwdPeerSelectionCompleteWrapper(Comm::ConnectionList
* unused
, ErrorState
*err
, void *data
)
464 FwdState
*fwd
= (FwdState
*) data
;
467 fwd
->startConnectionOrFail();
471 fwdServerClosedWrapper(const CommCloseCbParams
¶ms
)
473 FwdState
*fwd
= (FwdState
*)params
.data
;
474 fwd
->serverClosed(params
.fd
);
479 fwdNegotiateSSLWrapper(int fd
, void *data
)
481 FwdState
*fwd
= (FwdState
*) data
;
482 fwd
->negotiateSSL(fd
);
488 fwdConnectDoneWrapper(const Comm::ConnectionPointer
&conn
, comm_err_t status
, int xerrno
, void *data
)
490 FwdState
*fwd
= (FwdState
*) data
;
491 fwd
->connectDone(conn
, status
, xerrno
);
494 /**** PRIVATE *****************************************************************/
497 * FwdState::checkRetry
499 * Return TRUE if the request SHOULD be retried. This method is
500 * called when the HTTP connection fails, or when the connection
501 * is closed before server-side read the end of HTTP headers.
504 FwdState::checkRetry()
509 if (!self
) { // we have aborted before the server called us back
510 debugs(17, 5, HERE
<< "not retrying because of earlier abort");
511 // we will be destroyed when the server clears its Pointer to us
515 if (entry
->store_status
!= STORE_PENDING
)
518 if (!entry
->isEmpty())
524 if (origin_tries
> 2)
527 if (squid_curtime
- start_t
> Config
.Timeout
.forward
)
530 if (flags
.dont_retry
)
533 if (request
->bodyNibbled())
536 // NP: not yet actually connected anywhere. retry is safe.
537 if (!flags
.connected_okay
)
540 if (!checkRetriable())
547 * FwdState::checkRetriable
549 * Return TRUE if this is the kind of request that can be retried
550 * after a failure. If the request is not retriable then we don't
551 * want to risk sending it on a persistent connection. Instead we'll
552 * force it to go on a new HTTP connection.
555 FwdState::checkRetriable()
557 /* RFC2616 9.1 Safe and Idempotent Methods */
558 switch (request
->method
.id()) {
559 /* 9.1.1 Safe Methods */
564 /* 9.1.2 Idempotent Methods */
583 FwdState::serverClosed(int fd
)
585 debugs(17, 2, HERE
<< "FD " << fd
<< " " << entry
->url());
590 FwdState::retryOrBail()
593 debugs(17, 3, HERE
<< "re-forwarding (" << n_tries
<< " tries, " << (squid_curtime
- start_t
) << " secs)");
594 // we should retry the same destination if it failed due to pconn race
595 if (pconnRace
== raceHappened
)
596 debugs(17, 4, HERE
<< "retrying the same destination");
598 serverDestinations
.shift(); // last one failed. try another.
599 startConnectionOrFail();
603 // TODO: should we call completed() here and move doneWithRetries there?
606 if (self
!= NULL
&& !err
&& shutting_down
) {
607 ErrorState
*anErr
= new ErrorState(ERR_SHUTTING_DOWN
, HTTP_SERVICE_UNAVAILABLE
, request
);
608 errorAppendEntry(entry
, anErr
);
611 self
= NULL
; // refcounted
614 // If the Server quits before nibbling at the request body, the body sender
615 // will not know (so that we can retry). Call this if we will not retry. We
616 // will notify the sender so that it does not get stuck waiting for space.
618 FwdState::doneWithRetries()
620 if (request
&& request
->body_pipe
!= NULL
)
621 request
->body_pipe
->expectNoConsumption();
624 // called by the server that failed after calling unregister()
626 FwdState::handleUnregisteredServerEnd()
628 debugs(17, 2, HERE
<< "self=" << self
<< " err=" << err
<< ' ' << entry
->url());
629 assert(!Comm::IsConnOpen(serverConn
));
635 FwdState::negotiateSSL(int fd
)
637 unsigned long ssl_lib_error
= SSL_ERROR_NONE
;
638 SSL
*ssl
= fd_table
[fd
].ssl
;
641 if ((ret
= SSL_connect(ssl
)) <= 0) {
642 int ssl_error
= SSL_get_error(ssl
, ret
);
644 int sysErrNo
= EPROTO
;
646 int sysErrNo
= EACCES
;
651 case SSL_ERROR_WANT_READ
:
652 Comm::SetSelect(fd
, COMM_SELECT_READ
, fwdNegotiateSSLWrapper
, this, 0);
655 case SSL_ERROR_WANT_WRITE
:
656 Comm::SetSelect(fd
, COMM_SELECT_WRITE
, fwdNegotiateSSLWrapper
, this, 0);
660 case SSL_ERROR_SYSCALL
:
661 ssl_lib_error
= ERR_get_error();
662 debugs(81, DBG_IMPORTANT
, "fwdNegotiateSSL: Error negotiating SSL connection on FD " << fd
<<
663 ": " << ERR_error_string(ssl_lib_error
, NULL
) << " (" << ssl_error
<<
664 "/" << ret
<< "/" << errno
<< ")");
666 // store/report errno when ssl_error is SSL_ERROR_SYSCALL, ssl_lib_error is 0, and ret is -1
667 if (ssl_error
== SSL_ERROR_SYSCALL
&& ret
== -1 && ssl_lib_error
== 0)
670 // falling through to complete error handling
673 // TODO: move into a method before merge
674 Ssl::ErrorDetail
*errDetails
;
675 Ssl::ErrorDetail
*errFromFailure
= (Ssl::ErrorDetail
*)SSL_get_ex_data(ssl
, ssl_ex_index_ssl_error_detail
);
676 if (errFromFailure
!= NULL
) {
677 // The errFromFailure is attached to the ssl object
678 // and will be released when ssl object destroyed.
679 // Copy errFromFailure to a new Ssl::ErrorDetail object.
680 errDetails
= new Ssl::ErrorDetail(*errFromFailure
);
682 // server_cert can be NULL here
683 X509
*server_cert
= SSL_get_peer_certificate(ssl
);
684 errDetails
= new Ssl::ErrorDetail(SQUID_ERR_SSL_HANDSHAKE
, server_cert
, NULL
);
685 X509_free(server_cert
);
688 if (ssl_lib_error
!= SSL_ERROR_NONE
)
689 errDetails
->setLibError(ssl_lib_error
);
691 if (request
->clientConnectionManager
.valid()) {
692 // remember the server certificate from the ErrorDetail object
693 if (Ssl::ServerBump
*serverBump
= request
->clientConnectionManager
->serverBump()) {
694 serverBump
->serverCert
.resetAndLock(errDetails
->peerCert());
696 // remember validation errors, if any
697 if (Ssl::Errors
*errs
= static_cast<Ssl::Errors
*>(SSL_get_ex_data(ssl
, ssl_ex_index_ssl_errors
)))
698 serverBump
->sslErrors
= cbdataReference(errs
);
702 // For intercepted connections, set the host name to the server
703 // certificate CN. Otherwise, we just hope that CONNECT is using
704 // a user-entered address (a host name or a user-entered IP).
705 const bool isConnectRequest
= !request
->clientConnectionManager
->port
->spoof_client_ip
&&
706 !request
->clientConnectionManager
->port
->intercepted
;
707 if (request
->flags
.sslPeek
&& !isConnectRequest
) {
708 if (X509
*srvX509
= errDetails
->peerCert()) {
709 if (const char *name
= Ssl::CommonHostName(srvX509
)) {
710 request
->SetHost(name
);
711 debugs(83, 3, HERE
<< "reset request host: " << name
);
716 ErrorState
*const anErr
= makeConnectingError(ERR_SECURE_CONNECT_FAIL
);
717 anErr
->xerrno
= sysErrNo
;
718 anErr
->detail
= errDetails
;
721 if (serverConnection()->getPeer()) {
722 peerConnectFailed(serverConnection()->getPeer());
730 if (request
->clientConnectionManager
.valid()) {
731 // remember the server certificate from the ErrorDetail object
732 if (Ssl::ServerBump
*serverBump
= request
->clientConnectionManager
->serverBump()) {
733 serverBump
->serverCert
.reset(SSL_get_peer_certificate(ssl
));
735 // remember validation errors, if any
736 if (Ssl::Errors
*errs
= static_cast<Ssl::Errors
*>(SSL_get_ex_data(ssl
, ssl_ex_index_ssl_errors
)))
737 serverBump
->sslErrors
= cbdataReference(errs
);
741 if (serverConnection()->getPeer() && !SSL_session_reused(ssl
)) {
742 if (serverConnection()->getPeer()->sslSession
)
743 SSL_SESSION_free(serverConnection()->getPeer()->sslSession
);
745 serverConnection()->getPeer()->sslSession
= SSL_get1_session(ssl
);
752 FwdState::initiateSSL()
755 SSL_CTX
*sslContext
= NULL
;
756 const peer
*peer
= serverConnection()->getPeer();
757 int fd
= serverConnection()->fd
;
760 assert(peer
->use_ssl
);
761 sslContext
= peer
->sslContext
;
763 sslContext
= Config
.ssl_client
.sslContext
;
768 if ((ssl
= SSL_new(sslContext
)) == NULL
) {
769 debugs(83, DBG_IMPORTANT
, "fwdInitiateSSL: Error allocating handle: " << ERR_error_string(ERR_get_error(), NULL
) );
770 ErrorState
*anErr
= new ErrorState(ERR_SOCKET_FAILURE
, HTTP_INTERNAL_SERVER_ERROR
, request
);
771 // TODO: create Ssl::ErrorDetail with OpenSSL-supplied error code
773 self
= NULL
; // refcounted
781 SSL_set_ex_data(ssl
, ssl_ex_index_server
, peer
->ssldomain
);
786 SSL_set_ex_data(ssl
, ssl_ex_index_server
, peer
->name
);
791 SSL_set_ex_data(ssl
, ssl_ex_index_server
, peer
->host
);
793 if (peer
->sslSession
)
794 SSL_set_session(ssl
, peer
->sslSession
);
797 // While we are peeking at the certificate, we may not know the server
798 // name that the client will request (after interception or CONNECT)
799 // unless it was the CONNECT request with a user-typed address.
800 const char *hostname
= request
->GetHost();
801 const bool hostnameIsIp
= request
->GetHostIsNumeric();
802 const bool isConnectRequest
= !request
->clientConnectionManager
->port
->spoof_client_ip
&&
803 !request
->clientConnectionManager
->port
->intercepted
;
804 if (!request
->flags
.sslPeek
|| isConnectRequest
)
805 SSL_set_ex_data(ssl
, ssl_ex_index_server
, (void*)hostname
);
807 // Use SNI TLS extension only when we connect directly
808 // to the origin server and we know the server host name.
810 Ssl::setClientSNI(ssl
, hostname
);
813 // Create the ACL check list now, while we have access to more info.
814 // The list is used in ssl_verify_cb() and is freed in ssl_free().
815 if (acl_access
*acl
= Config
.ssl_client
.cert_error
) {
816 ACLFilledChecklist
*check
= new ACLFilledChecklist(acl
, request
, dash_str
);
818 SSL_set_ex_data(ssl
, ssl_ex_index_cert_error_check
, check
);
821 // store peeked cert to check SQUID_X509_V_ERR_CERT_CHANGE
823 if (request
->clientConnectionManager
.valid() &&
824 request
->clientConnectionManager
->serverBump() &&
825 (peeked_cert
= request
->clientConnectionManager
->serverBump()->serverCert
.get())) {
826 CRYPTO_add(&(peeked_cert
->references
),1,CRYPTO_LOCK_X509
);
827 SSL_set_ex_data(ssl
, ssl_ex_index_ssl_peeked_cert
, peeked_cert
);
830 fd_table
[fd
].ssl
= ssl
;
831 fd_table
[fd
].read_method
= &ssl_read_method
;
832 fd_table
[fd
].write_method
= &ssl_write_method
;
839 FwdState::connectDone(const Comm::ConnectionPointer
&conn
, comm_err_t status
, int xerrno
)
841 if (status
!= COMM_OK
) {
842 ErrorState
*const anErr
= makeConnectingError(ERR_CONNECT_FAIL
);
843 anErr
->xerrno
= xerrno
;
846 /* it might have been a timeout with a partially open link */
849 peerConnectFailed(conn
->getPeer());
858 flags
.connected_okay
= true;
860 debugs(17, 3, HERE
<< serverConnection() << ": '" << entry
->url() << "'" );
862 comm_add_close_handler(serverConnection()->fd
, fwdServerClosedWrapper
, this);
864 if (serverConnection()->getPeer())
865 peerConnectSucceded(serverConnection()->getPeer());
867 // some requests benefit from pinning but do not require it and can "repin"
868 const bool rePin
= request
->flags
.canRePin
&&
869 request
->clientConnectionManager
.valid();
871 debugs(17, 3, HERE
<< "repinning " << serverConn
);
872 request
->clientConnectionManager
->pinConnection(serverConn
,
873 request
, serverConn
->getPeer(), request
->flags
.auth
);
874 request
->flags
.pinned
= 1;
878 if (!request
->flags
.pinned
|| rePin
) {
879 if ((serverConnection()->getPeer() && serverConnection()->getPeer()->use_ssl
) ||
880 (!serverConnection()->getPeer() && request
->protocol
== AnyP::PROTO_HTTPS
) ||
881 request
->flags
.sslPeek
) {
892 FwdState::connectTimeout(int fd
)
894 debugs(17, 2, "fwdConnectTimeout: FD " << fd
<< ": '" << entry
->url() << "'" );
895 assert(serverDestinations
[0] != NULL
);
896 assert(fd
== serverDestinations
[0]->fd
);
898 if (entry
->isEmpty()) {
899 ErrorState
*anErr
= new ErrorState(ERR_CONNECT_FAIL
, HTTP_GATEWAY_TIMEOUT
, request
);
900 anErr
->xerrno
= ETIMEDOUT
;
903 /* This marks the peer DOWN ... */
904 if (serverDestinations
[0]->getPeer())
905 peerConnectFailed(serverDestinations
[0]->getPeer());
908 if (Comm::IsConnOpen(serverDestinations
[0])) {
909 serverDestinations
[0]->close();
914 * Called after Forwarding path selection (via peer select) has taken place.
915 * And whenever forwarding needs to attempt a new connection (routing failover)
916 * We have a vector of possible localIP->remoteIP paths now ready to start being connected.
919 FwdState::connectStart()
921 assert(serverDestinations
.size() > 0);
923 debugs(17, 3, "fwdConnectStart: " << entry
->url());
925 if (n_tries
== 0) // first attempt
926 request
->hier
.first_conn_start
= current_time
;
928 /* connection timeout */
930 if (serverDestinations
[0]->getPeer()) {
931 ctimeout
= serverDestinations
[0]->getPeer()->connect_timeout
> 0 ?
932 serverDestinations
[0]->getPeer()->connect_timeout
: Config
.Timeout
.peer_connect
;
934 ctimeout
= Config
.Timeout
.connect
;
937 /* calculate total forwarding timeout ??? */
938 int ftimeout
= Config
.Timeout
.forward
- (squid_curtime
- start_t
);
942 if (ftimeout
< ctimeout
)
945 if (serverDestinations
[0]->getPeer() && request
->flags
.sslBumped
== true) {
946 debugs(50, 4, "fwdConnectStart: Ssl bumped connections through parrent proxy are not allowed");
947 ErrorState
*anErr
= new ErrorState(ERR_CANNOT_FORWARD
, HTTP_SERVICE_UNAVAILABLE
, request
);
949 self
= NULL
; // refcounted
953 request
->flags
.pinned
= 0; // XXX: what if the ConnStateData set this to flag existing credentials?
954 // XXX: answer: the peer selection *should* catch it and give us only the pinned peer. so we reverse the =0 step below.
955 // XXX: also, logs will now lie if pinning is broken and leads to an error message.
956 if (serverDestinations
[0]->peerType
== PINNED
) {
957 ConnStateData
*pinned_connection
= request
->pinnedConnection();
958 // pinned_connection may become nil after a pconn race
959 if (pinned_connection
)
960 serverConn
= pinned_connection
->validatePinnedConnection(request
, serverDestinations
[0]->getPeer());
963 if (Comm::IsConnOpen(serverConn
)) {
964 flags
.connected_okay
= true;
966 if (!serverConn
->getPeer())
967 serverConn
->peerType
= HIER_DIRECT
;
970 request
->flags
.pinned
= 1;
971 if (pinned_connection
->pinnedAuth())
972 request
->flags
.auth
= 1;
973 comm_add_close_handler(serverConn
->fd
, fwdServerClosedWrapper
, this);
974 // the server may close the pinned connection before this request
975 pconnRace
= racePossible
;
979 /* Failure. Fall back on next path unless we can re-pin */
980 debugs(17,2,HERE
<< "Pinned connection failed: " << pinned_connection
);
981 if (pconnRace
!= raceHappened
|| !request
->flags
.canRePin
) {
982 serverDestinations
.shift();
983 pconnRace
= raceImpossible
;
984 startConnectionOrFail();
987 debugs(17,3, HERE
<< "There was a pconn race. Will try to repin.");
988 // and fall through to regular handling
991 // Use pconn to avoid opening a new connection.
993 if (serverDestinations
[0]->getPeer()) {
994 host
= serverDestinations
[0]->getPeer()->host
;
996 host
= request
->GetHost();
999 Comm::ConnectionPointer temp
;
1000 // Avoid pconns after races so that the same client does not suffer twice.
1001 // This does not increase the total number of connections because we just
1002 // closed the connection that failed the race. And re-pinning assumes this.
1003 if (pconnRace
!= raceHappened
)
1004 temp
= fwdPconnPool
->pop(serverDestinations
[0], host
, checkRetriable());
1006 const bool openedPconn
= Comm::IsConnOpen(temp
);
1007 pconnRace
= openedPconn
? racePossible
: raceImpossible
;
1009 // if we found an open persistent connection to use. use it.
1012 flags
.connected_okay
= true;
1013 debugs(17, 3, HERE
<< "reusing pconn " << serverConnection());
1016 if (!serverConnection()->getPeer())
1019 comm_add_close_handler(serverConnection()->fd
, fwdServerClosedWrapper
, this);
1021 /* Update server side TOS and Netfilter mark on the connection. */
1022 if (Ip::Qos::TheConfig
.isAclTosActive()) {
1023 temp
->tos
= GetTosToServer(request
);
1024 Ip::Qos::setSockTos(temp
, temp
->tos
);
1027 if (Ip::Qos::TheConfig
.isAclNfmarkActive()) {
1028 temp
->nfmark
= GetNfmarkToServer(request
);
1029 Ip::Qos::setSockNfmark(temp
, temp
->nfmark
);
1037 // We will try to open a new connection, possibly to the same destination.
1038 // We reset serverDestinations[0] in case we are using it again because
1039 // ConnOpener modifies its destination argument.
1040 serverDestinations
[0]->local
.SetPort(0);
1043 #if URL_CHECKSUM_DEBUG
1044 entry
->mem_obj
->checkUrlChecksum();
1047 /* Get the server side TOS and Netfilter mark to be set on the connection. */
1048 if (Ip::Qos::TheConfig
.isAclTosActive()) {
1049 serverDestinations
[0]->tos
= GetTosToServer(request
);
1051 #if SO_MARK && USE_LIBCAP
1052 serverDestinations
[0]->nfmark
= GetNfmarkToServer(request
);
1053 debugs(17, 3, "fwdConnectStart: got outgoing addr " << serverDestinations
[0]->local
<< ", tos " << int(serverDestinations
[0]->tos
)
1054 << ", netfilter mark " << serverDestinations
[0]->nfmark
);
1056 serverDestinations
[0]->nfmark
= 0;
1057 debugs(17, 3, "fwdConnectStart: got outgoing addr " << serverDestinations
[0]->local
<< ", tos " << int(serverDestinations
[0]->tos
));
1060 calls
.connector
= commCbCall(17,3, "fwdConnectDoneWrapper", CommConnectCbPtrFun(fwdConnectDoneWrapper
, this));
1061 Comm::ConnOpener
*cs
= new Comm::ConnOpener(serverDestinations
[0], calls
.connector
, ctimeout
);
1063 AsyncJob::Start(cs
);
1067 FwdState::dispatch()
1069 debugs(17, 3, HERE
<< clientConn
<< ": Fetching '" << RequestMethodStr(request
->method
) << " " << entry
->url() << "'");
1071 * Assert that server_fd is set. This is to guarantee that fwdState
1072 * is attached to something and will be deallocated when server_fd
1075 assert(Comm::IsConnOpen(serverConn
));
1077 fd_note(serverConnection()->fd
, entry
->url());
1079 fd_table
[serverConnection()->fd
].noteUse(fwdPconnPool
);
1081 /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
1082 assert(entry
->ping_status
!= PING_WAITING
);
1084 assert(entry
->lock_count
);
1086 EBIT_SET(entry
->flags
, ENTRY_DISPATCHED
);
1088 netdbPingSite(request
->GetHost());
1090 /* Retrieves remote server TOS or MARK value, and stores it as part of the
1091 * original client request FD object. It is later used to forward
1092 * remote server's TOS/MARK in the response to the client in case of a MISS.
1094 if (Ip::Qos::TheConfig
.isHitNfmarkActive()) {
1095 if (Comm::IsConnOpen(clientConn
) && Comm::IsConnOpen(serverConnection())) {
1096 fde
* clientFde
= &fd_table
[clientConn
->fd
]; // XXX: move the fd_table access into Ip::Qos
1097 /* Get the netfilter mark for the connection */
1098 Ip::Qos::getNfmarkFromServer(serverConnection(), clientFde
);
1103 /* Bug 2537: The TOS forward part of QOS only applies to patched Linux kernels. */
1104 if (Ip::Qos::TheConfig
.isHitTosActive()) {
1105 if (Comm::IsConnOpen(clientConn
)) {
1106 fde
* clientFde
= &fd_table
[clientConn
->fd
]; // XXX: move the fd_table access into Ip::Qos
1107 /* Get the TOS value for the packet */
1108 Ip::Qos::getTosFromServer(serverConnection(), clientFde
);
1114 if (request
->flags
.sslPeek
) {
1115 CallJobHere1(17, 4, request
->clientConnectionManager
, ConnStateData
,
1116 ConnStateData::httpsPeeked
, serverConnection());
1117 unregister(serverConn
); // async call owns it now
1118 complete(); // destroys us
1123 if (serverConnection()->getPeer() != NULL
) {
1124 ++ serverConnection()->getPeer()->stats
.fetches
;
1125 request
->peer_login
= serverConnection()->getPeer()->login
;
1126 request
->peer_domain
= serverConnection()->getPeer()->domain
;
1129 assert(!request
->flags
.sslPeek
);
1130 request
->peer_login
= NULL
;
1131 request
->peer_domain
= NULL
;
1133 switch (request
->protocol
) {
1136 case AnyP::PROTO_HTTPS
:
1141 case AnyP::PROTO_HTTP
:
1145 case AnyP::PROTO_GOPHER
:
1149 case AnyP::PROTO_FTP
:
1153 case AnyP::PROTO_CACHE_OBJECT
:
1155 case AnyP::PROTO_INTERNAL
:
1157 case AnyP::PROTO_URN
:
1158 fatal_dump("Should never get here");
1161 case AnyP::PROTO_WHOIS
:
1165 case AnyP::PROTO_WAIS
: /* Not implemented */
1168 debugs(17, DBG_IMPORTANT
, "WARNING: Cannot retrieve '" << entry
->url() << "'.");
1169 ErrorState
*anErr
= new ErrorState(ERR_UNSUP_REQ
, HTTP_BAD_REQUEST
, request
);
1171 // Set the dont_retry flag because this is not a transient (network) error.
1172 flags
.dont_retry
= 1;
1173 if (Comm::IsConnOpen(serverConn
)) {
1174 serverConn
->close();
1182 * FwdState::reforward
1184 * returns TRUE if the transaction SHOULD be re-forwarded to the
1185 * next choice in the serverDestinations list. This method is called when
1186 * server-side communication completes normally, or experiences
1187 * some error after receiving the end of HTTP headers.
1190 FwdState::reforward()
1192 StoreEntry
*e
= entry
;
1195 if (EBIT_TEST(e
->flags
, ENTRY_ABORTED
)) {
1196 debugs(17, 3, HERE
<< "entry aborted");
1200 assert(e
->store_status
== STORE_PENDING
);
1202 #if URL_CHECKSUM_DEBUG
1204 e
->mem_obj
->checkUrlChecksum();
1207 debugs(17, 3, HERE
<< e
->url() << "?" );
1209 if (!EBIT_TEST(e
->flags
, ENTRY_FWD_HDR_WAIT
)) {
1210 debugs(17, 3, HERE
<< "No, ENTRY_FWD_HDR_WAIT isn't set");
1214 if (n_tries
> Config
.forward_max_tries
)
1217 if (origin_tries
> 1)
1220 if (request
->bodyNibbled())
1223 if (serverDestinations
.size() <= 1) {
1224 // NP: <= 1 since total count includes the recently failed one.
1225 debugs(17, 3, HERE
<< "No alternative forwarding paths left");
1229 s
= e
->getReply()->sline
.status
;
1230 debugs(17, 3, HERE
<< "status " << s
);
1231 return reforwardableStatus(s
);
1235 * Create "503 Service Unavailable" or "504 Gateway Timeout" error depending
1236 * on whether this is a validation request. RFC 2616 says that we MUST reply
1237 * with "504 Gateway Timeout" if validation fails and cached reply has
1238 * proxy-revalidate, must-revalidate or s-maxage Cache-Control directive.
1241 FwdState::makeConnectingError(const err_type type
) const
1243 return new ErrorState(type
, request
->flags
.need_validation
?
1244 HTTP_GATEWAY_TIMEOUT
: HTTP_SERVICE_UNAVAILABLE
, request
);
1248 fwdStats(StoreEntry
* s
)
1252 storeAppendPrintf(s
, "Status");
1254 for (j
= 1; j
< MAX_FWD_STATS_IDX
; ++j
) {
1255 storeAppendPrintf(s
, "\ttry#%d", j
);
1258 storeAppendPrintf(s
, "\n");
1260 for (i
= 0; i
<= (int) HTTP_INVALID_HEADER
; ++i
) {
1261 if (FwdReplyCodes
[0][i
] == 0)
1264 storeAppendPrintf(s
, "%3d", i
);
1266 for (j
= 0; j
<= MAX_FWD_STATS_IDX
; ++j
) {
1267 storeAppendPrintf(s
, "\t%d", FwdReplyCodes
[j
][i
]);
1270 storeAppendPrintf(s
, "\n");
1274 /**** STATIC MEMBER FUNCTIONS *************************************************/
1277 FwdState::reforwardableStatus(http_status s
)
1281 case HTTP_BAD_GATEWAY
:
1283 case HTTP_GATEWAY_TIMEOUT
:
1286 case HTTP_FORBIDDEN
:
1288 case HTTP_INTERNAL_SERVER_ERROR
:
1290 case HTTP_NOT_IMPLEMENTED
:
1292 case HTTP_SERVICE_UNAVAILABLE
:
1293 return Config
.retry
.onerror
;
1303 * Decide where details need to be gathered to correctly describe a persistent connection.
1305 * - the address/port details about this link
1306 * - domain name of server at other end of this link (either peer or requested host)
1309 FwdState::pconnPush(Comm::ConnectionPointer
&conn
, const char *domain
)
1311 if (conn
->getPeer()) {
1312 fwdPconnPool
->push(conn
, conn
->getPeer()->name
);
1314 fwdPconnPool
->push(conn
, domain
);
1319 FwdState::initModule()
1321 RegisterWithCacheManager();
1325 FwdState::RegisterWithCacheManager(void)
1327 Mgr::RegisterAction("forward", "Request Forwarding Statistics", fwdStats
, 0, 1);
1331 FwdState::logReplyStatus(int tries
, http_status status
)
1333 if (status
> HTTP_INVALID_HEADER
)
1338 if (tries
> MAX_FWD_STATS_IDX
)
1339 tries
= MAX_FWD_STATS_IDX
;
1341 ++ FwdReplyCodes
[tries
][status
];
1344 /**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
1348 * Formerly static, but now used by client_side_request.cc
1350 /// Checks for a TOS value to apply depending on the ACL
1352 aclMapTOS(acl_tos
* head
, ACLChecklist
* ch
)
1356 for (l
= head
; l
; l
= l
->next
) {
1357 if (!l
->aclList
|| ch
->fastCheck(l
->aclList
) == ACCESS_ALLOWED
)
1364 /// Checks for a netfilter mark value to apply depending on the ACL
1366 aclMapNfmark(acl_nfmark
* head
, ACLChecklist
* ch
)
1370 for (l
= head
; l
; l
= l
->next
) {
1371 if (!l
->aclList
|| ch
->fastCheck(l
->aclList
) == ACCESS_ALLOWED
)
1379 getOutgoingAddress(HttpRequest
* request
, Comm::ConnectionPointer conn
)
1381 // skip if an outgoing address is already set.
1382 if (!conn
->local
.IsAnyAddr()) return;
1384 // ensure that at minimum the wildcard local matches remote protocol
1385 if (conn
->remote
.IsIPv4())
1386 conn
->local
.SetIPv4();
1388 // maybe use TPROXY client address
1389 if (request
&& request
->flags
.spoof_client_ip
) {
1390 if (!conn
->getPeer() || !conn
->getPeer()->options
.no_tproxy
) {
1391 #if FOLLOW_X_FORWARDED_FOR && LINUX_NETFILTER
1392 if (Config
.onoff
.tproxy_uses_indirect_client
)
1393 conn
->local
= request
->indirect_client_addr
;
1396 conn
->local
= request
->client_addr
;
1397 // some flags need setting on the socket to use this address
1398 conn
->flags
|= COMM_DOBIND
;
1399 conn
->flags
|= COMM_TRANSPARENT
;
1402 // else no tproxy today ...
1405 if (!Config
.accessList
.outgoing_address
) {
1406 return; // anything will do.
1409 ACLFilledChecklist
ch(NULL
, request
, NULL
);
1410 ch
.dst_peer
= conn
->getPeer();
1411 ch
.dst_addr
= conn
->remote
;
1413 // TODO use the connection details in ACL.
1414 // needs a bit of rework in ACLFilledChecklist to use Comm::Connection instead of ConnStateData
1417 for (l
= Config
.accessList
.outgoing_address
; l
; l
= l
->next
) {
1419 /* check if the outgoing address is usable to the destination */
1420 if (conn
->remote
.IsIPv4() != l
->addr
.IsIPv4()) continue;
1422 /* check ACLs for this outgoing address */
1423 if (!l
->aclList
|| ch
.fastCheck(l
->aclList
) == ACCESS_ALLOWED
) {
1424 conn
->local
= l
->addr
;
1431 GetTosToServer(HttpRequest
* request
)
1433 ACLFilledChecklist
ch(NULL
, request
, NULL
);
1436 ch
.src_addr
= request
->client_addr
;
1437 ch
.my_addr
= request
->my_addr
;
1440 return aclMapTOS(Ip::Qos::TheConfig
.tosToServer
, &ch
);
1444 GetNfmarkToServer(HttpRequest
* request
)
1446 ACLFilledChecklist
ch(NULL
, request
, NULL
);
1449 ch
.src_addr
= request
->client_addr
;
1450 ch
.my_addr
= request
->my_addr
;
1453 return aclMapNfmark(Ip::Qos::TheConfig
.nfmarkToServer
, &ch
);