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/AclAddress.h"
36 #include "acl/FilledChecklist.h"
37 #include "acl/Gadgets.h"
38 #include "anyp/PortCfg.h"
39 #include "CachePeer.h"
40 #include "CacheManager.h"
41 #include "client_side.h"
42 #include "comm/Connection.h"
43 #include "comm/ConnOpener.h"
44 #include "comm/Loops.h"
45 #include "CommCalls.h"
46 #include "errorpage.h"
51 #include "FtpGatewayServer.h"
55 #include "hier_code.h"
57 #include "HttpReply.h"
58 #include "HttpRequest.h"
59 #include "icmp/net_db.h"
61 #include "ip/Intercept.h"
62 #include "ip/QosConfig.h"
64 #include "MemObject.h"
65 #include "mgr/Registration.h"
66 #include "neighbors.h"
68 #include "PeerSelectState.h"
69 #include "SquidConfig.h"
70 #include "SquidTime.h"
72 #include "StoreClient.h"
76 #include "ssl/cert_validate_message.h"
77 #include "ssl/Config.h"
78 #include "ssl/helper.h"
79 #include "ssl/support.h"
80 #include "ssl/ErrorDetail.h"
81 #include "ssl/ServerBump.h"
87 static PSC fwdPeerSelectionCompleteWrapper
;
88 static CLCB fwdServerClosedWrapper
;
90 static PF fwdNegotiateSSLWrapper
;
92 static CNCB fwdConnectDoneWrapper
;
96 #define MAX_FWD_STATS_IDX 9
97 static int FwdReplyCodes
[MAX_FWD_STATS_IDX
+ 1][Http::scInvalidHeader
+ 1];
99 static PconnPool
*fwdPconnPool
= new PconnPool("server-side");
100 CBDATA_CLASS_INIT(FwdState
);
103 FwdState::abort(void* d
)
105 FwdState
* fwd
= (FwdState
*)d
;
106 Pointer tmp
= fwd
; // Grab a temporary pointer to keep the object alive during our scope.
108 if (Comm::IsConnOpen(fwd
->serverConnection())) {
109 comm_remove_close_handler(fwd
->serverConnection()->fd
, fwdServerClosedWrapper
, fwd
);
110 debugs(17, 3, HERE
<< "store entry aborted; closing " <<
111 fwd
->serverConnection());
112 fwd
->serverConnection()->close();
114 debugs(17, 7, HERE
<< "store entry aborted; no connection to close");
116 fwd
->serverDestinations
.clean();
120 /**** PUBLIC INTERFACE ********************************************************/
122 FwdState::FwdState(const Comm::ConnectionPointer
&client
, StoreEntry
* e
, HttpRequest
* r
, const AccessLogEntryPointer
&alp
):
125 debugs(17, 2, HERE
<< "Forwarding client request " << client
<< ", url=" << e
->url() );
129 HTTPMSGLOCK(request
);
130 pconnRace
= raceImpossible
;
131 start_t
= squid_curtime
;
132 serverDestinations
.reserve(Config
.forward_max_tries
);
134 EBIT_SET(e
->flags
, ENTRY_FWD_HDR_WAIT
);
137 // Called once, right after object creation, when it is safe to set self
138 void FwdState::start(Pointer aSelf
)
140 // Protect ourselves from being destroyed when the only Server pointing
141 // to us is gone (while we expect to talk to more Servers later).
142 // Once we set self, we are responsible for clearing it when we do not
143 // expect to talk to any servers.
144 self
= aSelf
; // refcounted
146 // We hope that either the store entry aborts or peer is selected.
147 // Otherwise we are going to leak our object.
149 entry
->registerAbort(FwdState::abort
, this);
151 #if STRICT_ORIGINAL_DST
152 // Bug 3243: CVE 2009-0801
153 // Bypass of browser same-origin access control in intercepted communication
154 // To resolve this we must force DIRECT and only to the original client destination.
155 const bool isIntercepted
= request
&& !request
->flags
.redirected
&& (request
->flags
.intercepted
|| request
->flags
.interceptTproxy
);
156 const bool useOriginalDst
= Config
.onoff
.client_dst_passthru
|| (request
&& !request
->flags
.hostVerified
);
157 if (isIntercepted
&& useOriginalDst
) {
158 selectPeerForIntercepted();
159 // 3.2 does not suppro re-wrapping inside CONNECT.
160 // our only alternative is to fake destination "found" and continue with the forwarding.
161 startConnectionOrFail();
166 // do full route options selection
167 peerSelect(&serverDestinations
, request
, entry
, fwdPeerSelectionCompleteWrapper
, this);
170 #if STRICT_ORIGINAL_DST
171 /// bypasses peerSelect() when dealing with intercepted requests
173 FwdState::selectPeerForIntercepted()
175 // use pinned connection if available
176 Comm::ConnectionPointer p
;
177 if (ConnStateData
*client
= request
->pinnedConnection()) {
178 p
= client
->validatePinnedConnection(request
, NULL
);
179 if (Comm::IsConnOpen(p
)) {
180 /* duplicate peerSelectPinned() effects */
181 p
->peerType
= PINNED
;
182 entry
->ping_status
= PING_DONE
; /* Skip ICP */
184 debugs(17, 3, "reusing a pinned conn: " << *p
);
185 serverDestinations
.push_back(p
);
187 debugs(17,2, "Pinned connection is not valid: " << p
);
188 ErrorState
*anErr
= new ErrorState(ERR_ZERO_SIZE_OBJECT
, Http::scServiceUnavailable
, request
);
191 // Either use the valid pinned connection or fail if it is invalid.
195 // use client original destination as second preferred choice
196 p
= new Comm::Connection();
197 p
->peerType
= ORIGINAL_DST
;
198 p
->remote
= clientConn
->local
;
199 getOutgoingAddress(request
, p
);
201 debugs(17, 3, HERE
<< "using client original destination: " << *p
);
202 serverDestinations
.push_back(p
);
207 FwdState::completed()
209 if (flags
.forward_completed
) {
210 debugs(17, DBG_IMPORTANT
, HERE
<< "FwdState::completed called on a completed request! Bad!");
214 flags
.forward_completed
= true;
216 if (EBIT_TEST(entry
->flags
, ENTRY_ABORTED
)) {
217 debugs(17, 3, HERE
<< "entry aborted");
221 #if URL_CHECKSUM_DEBUG
223 entry
->mem_obj
->checkUrlChecksum();
226 if (entry
->store_status
== STORE_PENDING
) {
227 if (entry
->isEmpty()) {
228 if (!err
) // we quit (e.g., fd closed) before an error or content
229 fail(new ErrorState(ERR_READ_ERROR
, Http::scBadGateway
, request
));
231 errorAppendEntry(entry
, err
);
234 if (request
->flags
.sslPeek
&& request
->clientConnectionManager
.valid()) {
235 CallJobHere1(17, 4, request
->clientConnectionManager
, ConnStateData
,
236 ConnStateData::httpsPeeked
, Comm::ConnectionPointer(NULL
));
240 EBIT_CLR(entry
->flags
, ENTRY_FWD_HDR_WAIT
);
242 entry
->releaseRequest();
246 if (storePendingNClients(entry
) > 0)
247 assert(!EBIT_TEST(entry
->flags
, ENTRY_FWD_HDR_WAIT
));
251 FwdState::~FwdState()
253 debugs(17, 3, HERE
<< "FwdState destructor starting");
255 if (! flags
.forward_completed
)
260 HTTPMSGUNLOCK(request
);
264 entry
->unregisterAbort();
270 if (calls
.connector
!= NULL
) {
271 calls
.connector
->cancel("FwdState destructed");
272 calls
.connector
= NULL
;
275 if (Comm::IsConnOpen(serverConn
)) {
276 comm_remove_close_handler(serverConnection()->fd
, fwdServerClosedWrapper
, this);
277 debugs(17, 3, HERE
<< "closing FD " << serverConnection()->fd
);
281 serverDestinations
.clean();
283 debugs(17, 3, HERE
<< "FwdState destructor done");
287 * This is the entry point for client-side to start forwarding
288 * a transaction. It is a static method that may or may not
289 * allocate a FwdState.
292 FwdState::Start(const Comm::ConnectionPointer
&clientConn
, StoreEntry
*entry
, HttpRequest
*request
, const AccessLogEntryPointer
&al
)
295 * client_addr == no_addr indicates this is an "internal" request
296 * from peer_digest.c, asn.c, netdb.c, etc and should always
297 * be allowed. yuck, I know.
300 if ( Config
.accessList
.miss
&& !request
->client_addr
.isNoAddr() &&
301 request
->protocol
!= AnyP::PROTO_INTERNAL
&& request
->protocol
!= AnyP::PROTO_CACHE_OBJECT
) {
303 * Check if this host is allowed to fetch MISSES from us (miss_access).
304 * Intentionally replace the src_addr automatically selected by the checklist code
305 * we do NOT want the indirect client address to be tested here.
307 ACLFilledChecklist
ch(Config
.accessList
.miss
, request
, NULL
);
308 ch
.src_addr
= request
->client_addr
;
309 if (ch
.fastCheck() == ACCESS_DENIED
) {
311 page_id
= aclGetDenyInfoPage(&Config
.denyInfoList
, AclMatchedName
, 1);
313 if (page_id
== ERR_NONE
)
314 page_id
= ERR_FORWARDING_DENIED
;
316 ErrorState
*anErr
= new ErrorState(page_id
, Http::scForbidden
, request
);
317 errorAppendEntry(entry
, anErr
); // frees anErr
322 debugs(17, 3, HERE
<< "'" << entry
->url() << "'");
324 * This seems like an odd place to bind mem_obj and request.
325 * Might want to assert that request is NULL at this point
327 entry
->mem_obj
->request
= request
;
328 HTTPMSGLOCK(entry
->mem_obj
->request
);
329 #if URL_CHECKSUM_DEBUG
331 entry
->mem_obj
->checkUrlChecksum();
336 ErrorState
*anErr
= new ErrorState(ERR_SHUTTING_DOWN
, Http::scServiceUnavailable
, request
);
337 errorAppendEntry(entry
, anErr
); // frees anErr
341 switch (request
->protocol
) {
343 case AnyP::PROTO_INTERNAL
:
344 internalStart(clientConn
, request
, entry
);
347 case AnyP::PROTO_CACHE_OBJECT
:
348 CacheManager::GetInstance()->Start(clientConn
, request
, entry
);
351 case AnyP::PROTO_URN
:
352 urnStart(request
, entry
);
356 FwdState::Pointer fwd
= new FwdState(clientConn
, entry
, request
, al
);
365 FwdState::fwdStart(const Comm::ConnectionPointer
&clientConn
, StoreEntry
*entry
, HttpRequest
*request
)
367 // Hides AccessLogEntry.h from code that does not supply ALE anyway.
368 Start(clientConn
, entry
, request
, NULL
);
372 FwdState::startConnectionOrFail()
374 debugs(17, 3, HERE
<< entry
->url());
376 if (serverDestinations
.size() > 0) {
377 // Ditch error page if it was created before.
378 // A new one will be created if there's another problem
382 // Update the logging information about this new server connection.
383 // Done here before anything else so the errors get logged for
384 // this server link regardless of what happens when connecting to it.
385 // IF sucessfuly connected this top destination will become the serverConnection().
386 request
->hier
.note(serverDestinations
[0], request
->GetHost());
387 request
->clearError();
391 debugs(17, 3, HERE
<< "Connection failed: " << entry
->url());
393 ErrorState
*anErr
= new ErrorState(ERR_CANNOT_FORWARD
, Http::scInternalServerError
, request
);
395 } // else use actual error from last connection attempt
396 self
= NULL
; // refcounted
401 FwdState::fail(ErrorState
* errorState
)
403 debugs(17, 3, err_type_str
[errorState
->type
] << " \"" << Http::StatusCodeString(errorState
->httpStatus
) << "\"\n\t" << entry
->url());
408 if (!errorState
->request
) {
409 errorState
->request
= request
;
410 HTTPMSGLOCK(errorState
->request
);
413 if (err
->type
!= ERR_ZERO_SIZE_OBJECT
)
416 if (pconnRace
== racePossible
) {
417 debugs(17, 5, HERE
<< "pconn race happened");
418 pconnRace
= raceHappened
;
421 if (ConnStateData
*pinned_connection
= request
->pinnedConnection()) {
422 pinned_connection
->pinning
.zeroReply
= true;
423 flags
.dont_retry
= true; // we want to propagate failure to the client
424 debugs(17, 4, "zero reply on pinned connection");
429 * Frees fwdState without closing FD or generating an abort
432 FwdState::unregister(Comm::ConnectionPointer
&conn
)
434 debugs(17, 3, HERE
<< entry
->url() );
435 assert(serverConnection() == conn
);
436 assert(Comm::IsConnOpen(conn
));
437 comm_remove_close_handler(conn
->fd
, fwdServerClosedWrapper
, this);
441 // Legacy method to be removed in favor of the above as soon as possible
443 FwdState::unregister(int fd
)
445 debugs(17, 3, HERE
<< entry
->url() );
446 assert(fd
== serverConnection()->fd
);
447 unregister(serverConn
);
451 * server-side modules call fwdComplete() when they are done
452 * downloading an object. Then, we either 1) re-forward the
453 * request somewhere else if needed, or 2) call storeComplete()
459 debugs(17, 3, HERE
<< entry
->url() << "\n\tstatus " << entry
->getReply()->sline
.status());
460 #if URL_CHECKSUM_DEBUG
462 entry
->mem_obj
->checkUrlChecksum();
465 logReplyStatus(n_tries
, entry
->getReply()->sline
.status());
468 debugs(17, 3, HERE
<< "re-forwarding " << entry
->getReply()->sline
.status() << " " << entry
->url());
470 if (Comm::IsConnOpen(serverConn
))
471 unregister(serverConn
);
475 // drop the last path off the selection list. try the next one.
476 serverDestinations
.shift();
477 startConnectionOrFail();
480 if (Comm::IsConnOpen(serverConn
))
481 debugs(17, 3, HERE
<< "server FD " << serverConnection()->fd
<< " not re-forwarding status " << entry
->getReply()->sline
.status());
483 debugs(17, 3, HERE
<< "server (FD closed) not re-forwarding status " << entry
->getReply()->sline
.status());
484 EBIT_CLR(entry
->flags
, ENTRY_FWD_HDR_WAIT
);
487 if (!Comm::IsConnOpen(serverConn
))
490 self
= NULL
; // refcounted
494 /**** CALLBACK WRAPPERS ************************************************************/
497 fwdPeerSelectionCompleteWrapper(Comm::ConnectionList
* unused
, ErrorState
*err
, void *data
)
499 FwdState
*fwd
= (FwdState
*) data
;
502 fwd
->startConnectionOrFail();
506 fwdServerClosedWrapper(const CommCloseCbParams
¶ms
)
508 FwdState
*fwd
= (FwdState
*)params
.data
;
509 fwd
->serverClosed(params
.fd
);
514 fwdNegotiateSSLWrapper(int fd
, void *data
)
516 FwdState
*fwd
= (FwdState
*) data
;
517 fwd
->negotiateSSL(fd
);
523 fwdConnectDoneWrapper(const Comm::ConnectionPointer
&conn
, comm_err_t status
, int xerrno
, void *data
)
525 FwdState
*fwd
= (FwdState
*) data
;
526 fwd
->connectDone(conn
, status
, xerrno
);
529 /**** PRIVATE *****************************************************************/
532 * FwdState::checkRetry
534 * Return TRUE if the request SHOULD be retried. This method is
535 * called when the HTTP connection fails, or when the connection
536 * is closed before server-side read the end of HTTP headers.
539 FwdState::checkRetry()
544 if (!self
) { // we have aborted before the server called us back
545 debugs(17, 5, HERE
<< "not retrying because of earlier abort");
546 // we will be destroyed when the server clears its Pointer to us
550 if (entry
->store_status
!= STORE_PENDING
)
553 if (!entry
->isEmpty())
556 if (n_tries
> Config
.forward_max_tries
)
559 if (squid_curtime
- start_t
> Config
.Timeout
.forward
)
562 if (flags
.dont_retry
)
565 if (request
->bodyNibbled())
568 // NP: not yet actually connected anywhere. retry is safe.
569 if (!flags
.connected_okay
)
572 if (!checkRetriable())
579 * FwdState::checkRetriable
581 * Return TRUE if this is the kind of request that can be retried
582 * after a failure. If the request is not retriable then we don't
583 * want to risk sending it on a persistent connection. Instead we'll
584 * force it to go on a new HTTP connection.
587 FwdState::checkRetriable()
589 // Optimize: A compliant proxy may retry PUTs, but Squid lacks the [rather
590 // complicated] code required to protect the PUT request body from being
591 // nibbled during the first try. Thus, Squid cannot retry some PUTs today.
592 if (request
->body_pipe
!= NULL
)
595 // RFC2616 9.1 Safe and Idempotent Methods
596 return (request
->method
.isHttpSafe() || request
->method
.isIdempotent());
600 FwdState::serverClosed(int fd
)
602 debugs(17, 2, HERE
<< "FD " << fd
<< " " << entry
->url());
607 FwdState::retryOrBail()
610 debugs(17, 3, HERE
<< "re-forwarding (" << n_tries
<< " tries, " << (squid_curtime
- start_t
) << " secs)");
611 // we should retry the same destination if it failed due to pconn race
612 if (pconnRace
== raceHappened
)
613 debugs(17, 4, HERE
<< "retrying the same destination");
615 serverDestinations
.shift(); // last one failed. try another.
616 startConnectionOrFail();
620 // TODO: should we call completed() here and move doneWithRetries there?
623 if (self
!= NULL
&& !err
&& shutting_down
) {
624 ErrorState
*anErr
= new ErrorState(ERR_SHUTTING_DOWN
, Http::scServiceUnavailable
, request
);
625 errorAppendEntry(entry
, anErr
);
628 self
= NULL
; // refcounted
631 // If the Server quits before nibbling at the request body, the body sender
632 // will not know (so that we can retry). Call this if we will not retry. We
633 // will notify the sender so that it does not get stuck waiting for space.
635 FwdState::doneWithRetries()
637 if (request
&& request
->body_pipe
!= NULL
)
638 request
->body_pipe
->expectNoConsumption();
641 // called by the server that failed after calling unregister()
643 FwdState::handleUnregisteredServerEnd()
645 debugs(17, 2, HERE
<< "self=" << self
<< " err=" << err
<< ' ' << entry
->url());
646 assert(!Comm::IsConnOpen(serverConn
));
652 FwdState::negotiateSSL(int fd
)
654 unsigned long ssl_lib_error
= SSL_ERROR_NONE
;
655 SSL
*ssl
= fd_table
[fd
].ssl
;
658 if ((ret
= SSL_connect(ssl
)) <= 0) {
659 int ssl_error
= SSL_get_error(ssl
, ret
);
661 int sysErrNo
= EPROTO
;
663 int sysErrNo
= EACCES
;
668 case SSL_ERROR_WANT_READ
:
669 Comm::SetSelect(fd
, COMM_SELECT_READ
, fwdNegotiateSSLWrapper
, this, 0);
672 case SSL_ERROR_WANT_WRITE
:
673 Comm::SetSelect(fd
, COMM_SELECT_WRITE
, fwdNegotiateSSLWrapper
, this, 0);
677 case SSL_ERROR_SYSCALL
:
678 ssl_lib_error
= ERR_get_error();
679 debugs(81, DBG_IMPORTANT
, "fwdNegotiateSSL: Error negotiating SSL connection on FD " << fd
<<
680 ": " << ERR_error_string(ssl_lib_error
, NULL
) << " (" << ssl_error
<<
681 "/" << ret
<< "/" << errno
<< ")");
683 // store/report errno when ssl_error is SSL_ERROR_SYSCALL, ssl_lib_error is 0, and ret is -1
684 if (ssl_error
== SSL_ERROR_SYSCALL
&& ret
== -1 && ssl_lib_error
== 0)
687 // falling through to complete error handling
690 // TODO: move into a method before merge
691 Ssl::ErrorDetail
*errDetails
;
692 Ssl::ErrorDetail
*errFromFailure
= (Ssl::ErrorDetail
*)SSL_get_ex_data(ssl
, ssl_ex_index_ssl_error_detail
);
693 if (errFromFailure
!= NULL
) {
694 // The errFromFailure is attached to the ssl object
695 // and will be released when ssl object destroyed.
696 // Copy errFromFailure to a new Ssl::ErrorDetail object.
697 errDetails
= new Ssl::ErrorDetail(*errFromFailure
);
699 // server_cert can be NULL here
700 X509
*server_cert
= SSL_get_peer_certificate(ssl
);
701 errDetails
= new Ssl::ErrorDetail(SQUID_ERR_SSL_HANDSHAKE
, server_cert
, NULL
);
702 X509_free(server_cert
);
705 if (ssl_lib_error
!= SSL_ERROR_NONE
)
706 errDetails
->setLibError(ssl_lib_error
);
708 if (request
->clientConnectionManager
.valid()) {
709 // remember the server certificate from the ErrorDetail object
710 if (Ssl::ServerBump
*serverBump
= request
->clientConnectionManager
->serverBump()) {
711 serverBump
->serverCert
.resetAndLock(errDetails
->peerCert());
713 // remember validation errors, if any
714 if (Ssl::CertErrors
*errs
= static_cast<Ssl::CertErrors
*>(SSL_get_ex_data(ssl
, ssl_ex_index_ssl_errors
)))
715 serverBump
->sslErrors
= cbdataReference(errs
);
719 // For intercepted connections, set the host name to the server
720 // certificate CN. Otherwise, we just hope that CONNECT is using
721 // a user-entered address (a host name or a user-entered IP).
722 const bool isConnectRequest
= !request
->clientConnectionManager
->port
->flags
.isIntercepted();
723 if (request
->flags
.sslPeek
&& !isConnectRequest
) {
724 if (X509
*srvX509
= errDetails
->peerCert()) {
725 if (const char *name
= Ssl::CommonHostName(srvX509
)) {
726 request
->SetHost(name
);
727 debugs(83, 3, HERE
<< "reset request host: " << name
);
732 ErrorState
*const anErr
= makeConnectingError(ERR_SECURE_CONNECT_FAIL
);
733 anErr
->xerrno
= sysErrNo
;
734 anErr
->detail
= errDetails
;
737 if (serverConnection()->getPeer()) {
738 peerConnectFailed(serverConnection()->getPeer());
746 if (request
->clientConnectionManager
.valid()) {
747 // remember the server certificate from the ErrorDetail object
748 if (Ssl::ServerBump
*serverBump
= request
->clientConnectionManager
->serverBump()) {
749 serverBump
->serverCert
.reset(SSL_get_peer_certificate(ssl
));
751 // remember validation errors, if any
752 if (Ssl::CertErrors
*errs
= static_cast<Ssl::CertErrors
*>(SSL_get_ex_data(ssl
, ssl_ex_index_ssl_errors
)))
753 serverBump
->sslErrors
= cbdataReference(errs
);
757 if (serverConnection()->getPeer() && !SSL_session_reused(ssl
)) {
758 if (serverConnection()->getPeer()->sslSession
)
759 SSL_SESSION_free(serverConnection()->getPeer()->sslSession
);
761 serverConnection()->getPeer()->sslSession
= SSL_get1_session(ssl
);
764 if (Ssl::TheConfig
.ssl_crt_validator
) {
765 Ssl::CertValidationRequest validationRequest
;
766 // WARNING: Currently we do not use any locking for any of the
767 // members of the Ssl::CertValidationRequest class. In this code the
768 // Ssl::CertValidationRequest object used only to pass data to
769 // Ssl::CertValidationHelper::submit method.
770 validationRequest
.ssl
= ssl
;
771 validationRequest
.domainName
= request
->GetHost();
772 if (Ssl::CertErrors
*errs
= static_cast<Ssl::CertErrors
*>(SSL_get_ex_data(ssl
, ssl_ex_index_ssl_errors
)))
773 // validationRequest disappears on return so no need to cbdataReference
774 validationRequest
.errors
= errs
;
776 validationRequest
.errors
= NULL
;
778 debugs(83, 5, "Sending SSL certificate for validation to ssl_crtvd.");
779 Ssl::CertValidationHelper::GetInstance()->sslSubmit(validationRequest
, sslCrtvdHandleReplyWrapper
, this);
781 } catch (const std::exception
&e
) {
782 debugs(33, DBG_IMPORTANT
, "ERROR: Failed to compose ssl_crtvd " <<
783 "request for " << validationRequest
.domainName
<<
784 " certificate: " << e
.what() << "; will now block to " <<
785 "validate that certificate.");
786 // fall through to do blocking in-process generation.
787 ErrorState
*anErr
= new ErrorState(ERR_GATEWAY_FAILURE
, Http::scInternalServerError
, request
);
789 if (serverConnection()->getPeer()) {
790 peerConnectFailed(serverConnection()->getPeer());
802 FwdState::sslCrtvdHandleReplyWrapper(void *data
, Ssl::CertValidationResponse
const &validationResponse
)
804 FwdState
* fwd
= (FwdState
*)(data
);
805 fwd
->sslCrtvdHandleReply(validationResponse
);
809 FwdState::sslCrtvdHandleReply(Ssl::CertValidationResponse
const &validationResponse
)
811 Ssl::CertErrors
*errs
= NULL
;
812 Ssl::ErrorDetail
*errDetails
= NULL
;
813 bool validatorFailed
= false;
814 if (!Comm::IsConnOpen(serverConnection())) {
818 debugs(83,5, request
->GetHost() << " cert validation result: " << validationResponse
.resultCode
);
820 if (validationResponse
.resultCode
== HelperReply::Error
)
821 errs
= sslCrtvdCheckForErrors(validationResponse
, errDetails
);
822 else if (validationResponse
.resultCode
!= HelperReply::Okay
)
823 validatorFailed
= true;
825 if (!errDetails
&& !validatorFailed
) {
830 ErrorState
*anErr
= NULL
;
831 if (validatorFailed
) {
832 anErr
= new ErrorState(ERR_GATEWAY_FAILURE
, Http::scInternalServerError
, request
);
835 // Check the list error with
836 if (errDetails
&& request
->clientConnectionManager
.valid()) {
837 // remember the server certificate from the ErrorDetail object
838 if (Ssl::ServerBump
*serverBump
= request
->clientConnectionManager
->serverBump()) {
839 // remember validation errors, if any
841 if (serverBump
->sslErrors
)
842 cbdataReferenceDone(serverBump
->sslErrors
);
843 serverBump
->sslErrors
= cbdataReference(errs
);
848 anErr
= makeConnectingError(ERR_SECURE_CONNECT_FAIL
);
849 anErr
->detail
= errDetails
;
850 /*anErr->xerrno= Should preserved*/
854 if (serverConnection()->getPeer()) {
855 peerConnectFailed(serverConnection()->getPeer());
862 /// Checks errors in the cert. validator response against sslproxy_cert_error.
863 /// The first honored error, if any, is returned via errDetails parameter.
864 /// The method returns all seen errors except SSL_ERROR_NONE as Ssl::CertErrors.
866 FwdState::sslCrtvdCheckForErrors(Ssl::CertValidationResponse
const &resp
, Ssl::ErrorDetail
*& errDetails
)
868 Ssl::CertErrors
*errs
= NULL
;
870 ACLFilledChecklist
*check
= NULL
;
871 if (acl_access
*acl
= Config
.ssl_client
.cert_error
)
872 check
= new ACLFilledChecklist(acl
, request
, dash_str
);
874 SSL
*ssl
= fd_table
[serverConnection()->fd
].ssl
;
875 typedef Ssl::CertValidationResponse::RecvdErrors::const_iterator SVCRECI
;
876 for (SVCRECI i
= resp
.errors
.begin(); i
!= resp
.errors
.end(); ++i
) {
877 debugs(83, 7, "Error item: " << i
->error_no
<< " " << i
->error_reason
);
879 assert(i
->error_no
!= SSL_ERROR_NONE
);
882 bool allowed
= false;
884 check
->sslErrors
= new Ssl::CertErrors(Ssl::CertError(i
->error_no
, i
->cert
.get()));
885 if (check
->fastCheck() == ACCESS_ALLOWED
)
888 // else the Config.ssl_client.cert_error access list is not defined
889 // and the first error will cause the error page
892 debugs(83, 3, "bypassing SSL error " << i
->error_no
<< " in " << "buffer");
894 debugs(83, 5, "confirming SSL error " << i
->error_no
);
895 X509
*brokenCert
= i
->cert
.get();
896 Ssl::X509_Pointer
peerCert(SSL_get_peer_certificate(ssl
));
897 const char *aReason
= i
->error_reason
.empty() ? NULL
: i
->error_reason
.c_str();
898 errDetails
= new Ssl::ErrorDetail(i
->error_no
, peerCert
.get(), brokenCert
, aReason
);
901 delete check
->sslErrors
;
902 check
->sslErrors
= NULL
;
907 errs
= new Ssl::CertErrors(Ssl::CertError(i
->error_no
, i
->cert
.get()));
909 errs
->push_back_unique(Ssl::CertError(i
->error_no
, i
->cert
.get()));
918 FwdState::initiateSSL()
921 SSL_CTX
*sslContext
= NULL
;
922 const CachePeer
*peer
= serverConnection()->getPeer();
923 int fd
= serverConnection()->fd
;
926 assert(peer
->use_ssl
);
927 sslContext
= peer
->sslContext
;
929 sslContext
= Config
.ssl_client
.sslContext
;
934 if ((ssl
= SSL_new(sslContext
)) == NULL
) {
935 debugs(83, DBG_IMPORTANT
, "fwdInitiateSSL: Error allocating handle: " << ERR_error_string(ERR_get_error(), NULL
) );
936 ErrorState
*anErr
= new ErrorState(ERR_SOCKET_FAILURE
, Http::scInternalServerError
, request
);
937 // TODO: create Ssl::ErrorDetail with OpenSSL-supplied error code
939 self
= NULL
; // refcounted
947 SSL_set_ex_data(ssl
, ssl_ex_index_server
, peer
->ssldomain
);
952 SSL_set_ex_data(ssl
, ssl_ex_index_server
, peer
->name
);
957 SSL_set_ex_data(ssl
, ssl_ex_index_server
, peer
->host
);
959 if (peer
->sslSession
)
960 SSL_set_session(ssl
, peer
->sslSession
);
963 // While we are peeking at the certificate, we may not know the server
964 // name that the client will request (after interception or CONNECT)
965 // unless it was the CONNECT request with a user-typed address.
966 const char *hostname
= request
->GetHost();
967 const bool hostnameIsIp
= request
->GetHostIsNumeric();
968 const bool isConnectRequest
= !request
->clientConnectionManager
->port
->flags
.isIntercepted();
969 if (!request
->flags
.sslPeek
|| isConnectRequest
)
970 SSL_set_ex_data(ssl
, ssl_ex_index_server
, (void*)hostname
);
972 // Use SNI TLS extension only when we connect directly
973 // to the origin server and we know the server host name.
975 Ssl::setClientSNI(ssl
, hostname
);
978 // If CertValidation Helper used do not lookup checklist for errors,
979 // but keep a list of errors to send it to CertValidator
980 if (!Ssl::TheConfig
.ssl_crt_validator
) {
981 // Create the ACL check list now, while we have access to more info.
982 // The list is used in ssl_verify_cb() and is freed in ssl_free().
983 if (acl_access
*acl
= Config
.ssl_client
.cert_error
) {
984 ACLFilledChecklist
*check
= new ACLFilledChecklist(acl
, request
, dash_str
);
985 SSL_set_ex_data(ssl
, ssl_ex_index_cert_error_check
, check
);
989 // store peeked cert to check SQUID_X509_V_ERR_CERT_CHANGE
991 if (request
->clientConnectionManager
.valid() &&
992 request
->clientConnectionManager
->serverBump() &&
993 (peeked_cert
= request
->clientConnectionManager
->serverBump()->serverCert
.get())) {
994 CRYPTO_add(&(peeked_cert
->references
),1,CRYPTO_LOCK_X509
);
995 SSL_set_ex_data(ssl
, ssl_ex_index_ssl_peeked_cert
, peeked_cert
);
998 fd_table
[fd
].ssl
= ssl
;
999 fd_table
[fd
].read_method
= &ssl_read_method
;
1000 fd_table
[fd
].write_method
= &ssl_write_method
;
1007 FwdState::connectDone(const Comm::ConnectionPointer
&conn
, comm_err_t status
, int xerrno
)
1009 if (status
!= COMM_OK
) {
1010 ErrorState
*const anErr
= makeConnectingError(ERR_CONNECT_FAIL
);
1011 anErr
->xerrno
= xerrno
;
1014 /* it might have been a timeout with a partially open link */
1016 if (conn
->getPeer())
1017 peerConnectFailed(conn
->getPeer());
1026 flags
.connected_okay
= true;
1028 debugs(17, 3, HERE
<< serverConnection() << ": '" << entry
->url() << "'" );
1030 comm_add_close_handler(serverConnection()->fd
, fwdServerClosedWrapper
, this);
1032 if (serverConnection()->getPeer())
1033 peerConnectSucceded(serverConnection()->getPeer());
1036 if (!request
->flags
.pinned
) {
1037 if ((serverConnection()->getPeer() && serverConnection()->getPeer()->use_ssl
) ||
1038 (!serverConnection()->getPeer() && request
->protocol
== AnyP::PROTO_HTTPS
) ||
1039 request
->flags
.sslPeek
) {
1046 const CbcPointer
<ConnStateData
> &clientConnState
=
1047 request
->clientConnectionManager
;
1048 if (clientConnState
->isFtp
) {
1049 clientConnState
->pinConnection(serverConnection(), request
,
1050 serverConnection()->getPeer(), false);
1057 FwdState::connectTimeout(int fd
)
1059 debugs(17, 2, "fwdConnectTimeout: FD " << fd
<< ": '" << entry
->url() << "'" );
1060 assert(serverDestinations
[0] != NULL
);
1061 assert(fd
== serverDestinations
[0]->fd
);
1063 if (entry
->isEmpty()) {
1064 ErrorState
*anErr
= new ErrorState(ERR_CONNECT_FAIL
, Http::scGateway_Timeout
, request
);
1065 anErr
->xerrno
= ETIMEDOUT
;
1068 /* This marks the peer DOWN ... */
1069 if (serverDestinations
[0]->getPeer())
1070 peerConnectFailed(serverDestinations
[0]->getPeer());
1073 if (Comm::IsConnOpen(serverDestinations
[0])) {
1074 serverDestinations
[0]->close();
1079 * Called after Forwarding path selection (via peer select) has taken place.
1080 * And whenever forwarding needs to attempt a new connection (routing failover)
1081 * We have a vector of possible localIP->remoteIP paths now ready to start being connected.
1084 FwdState::connectStart()
1086 assert(serverDestinations
.size() > 0);
1088 debugs(17, 3, "fwdConnectStart: " << entry
->url());
1090 if (!request
->hier
.first_conn_start
.tv_sec
) // first attempt
1091 request
->hier
.first_conn_start
= current_time
;
1093 /* connection timeout */
1095 if (serverDestinations
[0]->getPeer()) {
1096 ctimeout
= serverDestinations
[0]->getPeer()->connect_timeout
> 0 ?
1097 serverDestinations
[0]->getPeer()->connect_timeout
: Config
.Timeout
.peer_connect
;
1099 ctimeout
= Config
.Timeout
.connect
;
1102 /* calculate total forwarding timeout ??? */
1103 int ftimeout
= Config
.Timeout
.forward
- (squid_curtime
- start_t
);
1107 if (ftimeout
< ctimeout
)
1108 ctimeout
= ftimeout
;
1110 if (serverDestinations
[0]->getPeer() && request
->flags
.sslBumped
) {
1111 debugs(50, 4, "fwdConnectStart: Ssl bumped connections through parrent proxy are not allowed");
1112 ErrorState
*anErr
= new ErrorState(ERR_CANNOT_FORWARD
, Http::scServiceUnavailable
, request
);
1114 self
= NULL
; // refcounted
1118 request
->flags
.pinned
= false; // XXX: what if the ConnStateData set this to flag existing credentials?
1119 // XXX: answer: the peer selection *should* catch it and give us only the pinned peer. so we reverse the =0 step below.
1120 // XXX: also, logs will now lie if pinning is broken and leads to an error message.
1121 if (serverDestinations
[0]->peerType
== PINNED
) {
1122 ConnStateData
*pinned_connection
= request
->pinnedConnection();
1123 debugs(17,7, "pinned peer connection: " << pinned_connection
);
1124 // pinned_connection may become nil after a pconn race
1125 if (pinned_connection
)
1126 serverConn
= pinned_connection
->validatePinnedConnection(request
, serverDestinations
[0]->getPeer());
1129 if (Comm::IsConnOpen(serverConn
)) {
1130 flags
.connected_okay
= true;
1132 request
->hier
.note(serverConn
, request
->GetHost());
1133 request
->flags
.pinned
= true;
1134 if (pinned_connection
->pinnedAuth())
1135 request
->flags
.auth
= true;
1136 comm_add_close_handler(serverConn
->fd
, fwdServerClosedWrapper
, this);
1137 // the server may close the pinned connection before this request
1138 pconnRace
= racePossible
;
1142 // Pinned connection failure.
1143 debugs(17,2,HERE
<< "Pinned connection failed: " << pinned_connection
);
1144 ErrorState
*anErr
= new ErrorState(ERR_ZERO_SIZE_OBJECT
, Http::scServiceUnavailable
, request
);
1146 self
= NULL
; // refcounted
1150 // Use pconn to avoid opening a new connection.
1151 const char *host
= NULL
;
1152 if (!serverDestinations
[0]->getPeer())
1153 host
= request
->GetHost();
1155 Comm::ConnectionPointer temp
;
1156 // Avoid pconns after races so that the same client does not suffer twice.
1157 // This does not increase the total number of connections because we just
1158 // closed the connection that failed the race. And re-pinning assumes this.
1159 if (pconnRace
!= raceHappened
)
1160 temp
= fwdPconnPool
->pop(serverDestinations
[0], host
, checkRetriable());
1162 const bool openedPconn
= Comm::IsConnOpen(temp
);
1163 pconnRace
= openedPconn
? racePossible
: raceImpossible
;
1165 // if we found an open persistent connection to use. use it.
1168 flags
.connected_okay
= true;
1169 debugs(17, 3, HERE
<< "reusing pconn " << serverConnection());
1172 comm_add_close_handler(serverConnection()->fd
, fwdServerClosedWrapper
, this);
1174 /* Update server side TOS and Netfilter mark on the connection. */
1175 if (Ip::Qos::TheConfig
.isAclTosActive()) {
1176 const tos_t tos
= GetTosToServer(request
);
1177 Ip::Qos::setSockTos(temp
, tos
);
1180 if (Ip::Qos::TheConfig
.isAclNfmarkActive()) {
1181 const nfmark_t nfmark
= GetNfmarkToServer(request
);
1182 Ip::Qos::setSockNfmark(temp
, nfmark
);
1190 // We will try to open a new connection, possibly to the same destination.
1191 // We reset serverDestinations[0] in case we are using it again because
1192 // ConnOpener modifies its destination argument.
1193 serverDestinations
[0]->local
.port(0);
1196 #if URL_CHECKSUM_DEBUG
1197 entry
->mem_obj
->checkUrlChecksum();
1200 /* Get the server side TOS and Netfilter mark to be set on the connection. */
1201 if (Ip::Qos::TheConfig
.isAclTosActive()) {
1202 serverDestinations
[0]->tos
= GetTosToServer(request
);
1204 #if SO_MARK && USE_LIBCAP
1205 serverDestinations
[0]->nfmark
= GetNfmarkToServer(request
);
1206 debugs(17, 3, "fwdConnectStart: got outgoing addr " << serverDestinations
[0]->local
<< ", tos " << int(serverDestinations
[0]->tos
)
1207 << ", netfilter mark " << serverDestinations
[0]->nfmark
);
1209 serverDestinations
[0]->nfmark
= 0;
1210 debugs(17, 3, "fwdConnectStart: got outgoing addr " << serverDestinations
[0]->local
<< ", tos " << int(serverDestinations
[0]->tos
));
1213 calls
.connector
= commCbCall(17,3, "fwdConnectDoneWrapper", CommConnectCbPtrFun(fwdConnectDoneWrapper
, this));
1214 Comm::ConnOpener
*cs
= new Comm::ConnOpener(serverDestinations
[0], calls
.connector
, ctimeout
);
1217 AsyncJob::Start(cs
);
1221 FwdState::dispatch()
1223 debugs(17, 3, HERE
<< clientConn
<< ": Fetching '" << RequestMethodStr(request
->method
) << " " << entry
->url() << "'");
1225 * Assert that server_fd is set. This is to guarantee that fwdState
1226 * is attached to something and will be deallocated when server_fd
1229 assert(Comm::IsConnOpen(serverConn
));
1231 fd_note(serverConnection()->fd
, entry
->url());
1233 fd_table
[serverConnection()->fd
].noteUse(fwdPconnPool
);
1235 /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
1236 assert(entry
->ping_status
!= PING_WAITING
);
1238 assert(entry
->lock_count
);
1240 EBIT_SET(entry
->flags
, ENTRY_DISPATCHED
);
1242 netdbPingSite(request
->GetHost());
1244 /* Retrieves remote server TOS or MARK value, and stores it as part of the
1245 * original client request FD object. It is later used to forward
1246 * remote server's TOS/MARK in the response to the client in case of a MISS.
1248 if (Ip::Qos::TheConfig
.isHitNfmarkActive()) {
1249 if (Comm::IsConnOpen(clientConn
) && Comm::IsConnOpen(serverConnection())) {
1250 fde
* clientFde
= &fd_table
[clientConn
->fd
]; // XXX: move the fd_table access into Ip::Qos
1251 /* Get the netfilter mark for the connection */
1252 Ip::Qos::getNfmarkFromServer(serverConnection(), clientFde
);
1257 /* Bug 2537: The TOS forward part of QOS only applies to patched Linux kernels. */
1258 if (Ip::Qos::TheConfig
.isHitTosActive()) {
1259 if (Comm::IsConnOpen(clientConn
)) {
1260 fde
* clientFde
= &fd_table
[clientConn
->fd
]; // XXX: move the fd_table access into Ip::Qos
1261 /* Get the TOS value for the packet */
1262 Ip::Qos::getTosFromServer(serverConnection(), clientFde
);
1268 if (request
->flags
.sslPeek
) {
1269 CallJobHere1(17, 4, request
->clientConnectionManager
, ConnStateData
,
1270 ConnStateData::httpsPeeked
, serverConnection());
1271 unregister(serverConn
); // async call owns it now
1272 complete(); // destroys us
1277 if (serverConnection()->getPeer() != NULL
) {
1278 ++ serverConnection()->getPeer()->stats
.fetches
;
1279 request
->peer_login
= serverConnection()->getPeer()->login
;
1280 request
->peer_domain
= serverConnection()->getPeer()->domain
;
1283 assert(!request
->flags
.sslPeek
);
1284 request
->peer_login
= NULL
;
1285 request
->peer_domain
= NULL
;
1287 switch (request
->protocol
) {
1290 case AnyP::PROTO_HTTPS
:
1295 case AnyP::PROTO_HTTP
:
1299 case AnyP::PROTO_GOPHER
:
1303 case AnyP::PROTO_FTP
:
1304 if (request
->clientConnectionManager
->isFtp
)
1305 ftpGatewayServerStart(this);
1310 case AnyP::PROTO_CACHE_OBJECT
:
1312 case AnyP::PROTO_INTERNAL
:
1314 case AnyP::PROTO_URN
:
1315 fatal_dump("Should never get here");
1318 case AnyP::PROTO_WHOIS
:
1322 case AnyP::PROTO_WAIS
: /* Not implemented */
1325 debugs(17, DBG_IMPORTANT
, "WARNING: Cannot retrieve '" << entry
->url() << "'.");
1326 ErrorState
*anErr
= new ErrorState(ERR_UNSUP_REQ
, Http::scBadRequest
, request
);
1328 // Set the dont_retry flag because this is not a transient (network) error.
1329 flags
.dont_retry
= true;
1330 if (Comm::IsConnOpen(serverConn
)) {
1331 serverConn
->close();
1339 * FwdState::reforward
1341 * returns TRUE if the transaction SHOULD be re-forwarded to the
1342 * next choice in the serverDestinations list. This method is called when
1343 * server-side communication completes normally, or experiences
1344 * some error after receiving the end of HTTP headers.
1347 FwdState::reforward()
1349 StoreEntry
*e
= entry
;
1351 if (EBIT_TEST(e
->flags
, ENTRY_ABORTED
)) {
1352 debugs(17, 3, HERE
<< "entry aborted");
1356 assert(e
->store_status
== STORE_PENDING
);
1358 #if URL_CHECKSUM_DEBUG
1360 e
->mem_obj
->checkUrlChecksum();
1363 debugs(17, 3, HERE
<< e
->url() << "?" );
1365 if (!EBIT_TEST(e
->flags
, ENTRY_FWD_HDR_WAIT
)) {
1366 debugs(17, 3, HERE
<< "No, ENTRY_FWD_HDR_WAIT isn't set");
1370 if (n_tries
> Config
.forward_max_tries
)
1373 if (request
->bodyNibbled())
1376 if (serverDestinations
.size() <= 1) {
1377 // NP: <= 1 since total count includes the recently failed one.
1378 debugs(17, 3, HERE
<< "No alternative forwarding paths left");
1382 const Http::StatusCode s
= e
->getReply()->sline
.status();
1383 debugs(17, 3, HERE
<< "status " << s
);
1384 return reforwardableStatus(s
);
1388 * Create "503 Service Unavailable" or "504 Gateway Timeout" error depending
1389 * on whether this is a validation request. RFC 2616 says that we MUST reply
1390 * with "504 Gateway Timeout" if validation fails and cached reply has
1391 * proxy-revalidate, must-revalidate or s-maxage Cache-Control directive.
1394 FwdState::makeConnectingError(const err_type type
) const
1396 return new ErrorState(type
, request
->flags
.needValidation
?
1397 Http::scGateway_Timeout
: Http::scServiceUnavailable
, request
);
1401 fwdStats(StoreEntry
* s
)
1405 storeAppendPrintf(s
, "Status");
1407 for (j
= 1; j
< MAX_FWD_STATS_IDX
; ++j
) {
1408 storeAppendPrintf(s
, "\ttry#%d", j
);
1411 storeAppendPrintf(s
, "\n");
1413 for (i
= 0; i
<= (int) Http::scInvalidHeader
; ++i
) {
1414 if (FwdReplyCodes
[0][i
] == 0)
1417 storeAppendPrintf(s
, "%3d", i
);
1419 for (j
= 0; j
<= MAX_FWD_STATS_IDX
; ++j
) {
1420 storeAppendPrintf(s
, "\t%d", FwdReplyCodes
[j
][i
]);
1423 storeAppendPrintf(s
, "\n");
1427 /**** STATIC MEMBER FUNCTIONS *************************************************/
1430 FwdState::reforwardableStatus(const Http::StatusCode s
) const
1434 case Http::scBadGateway
:
1436 case Http::scGateway_Timeout
:
1439 case Http::scForbidden
:
1441 case Http::scInternalServerError
:
1443 case Http::scNotImplemented
:
1445 case Http::scServiceUnavailable
:
1446 return Config
.retry
.onerror
;
1456 * Decide where details need to be gathered to correctly describe a persistent connection.
1458 * - the address/port details about this link
1459 * - domain name of server at other end of this link (either peer or requested host)
1462 FwdState::pconnPush(Comm::ConnectionPointer
&conn
, const char *domain
)
1464 if (conn
->getPeer()) {
1465 fwdPconnPool
->push(conn
, NULL
);
1467 fwdPconnPool
->push(conn
, domain
);
1472 FwdState::initModule()
1474 RegisterWithCacheManager();
1478 FwdState::RegisterWithCacheManager(void)
1480 Mgr::RegisterAction("forward", "Request Forwarding Statistics", fwdStats
, 0, 1);
1484 FwdState::logReplyStatus(int tries
, const Http::StatusCode status
)
1486 if (status
> Http::scInvalidHeader
)
1491 if (tries
> MAX_FWD_STATS_IDX
)
1492 tries
= MAX_FWD_STATS_IDX
;
1494 ++ FwdReplyCodes
[tries
][status
];
1497 /**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
1501 * Formerly static, but now used by client_side_request.cc
1503 /// Checks for a TOS value to apply depending on the ACL
1505 aclMapTOS(acl_tos
* head
, ACLChecklist
* ch
)
1509 for (l
= head
; l
; l
= l
->next
) {
1510 if (!l
->aclList
|| ch
->fastCheck(l
->aclList
) == ACCESS_ALLOWED
)
1517 /// Checks for a netfilter mark value to apply depending on the ACL
1519 aclMapNfmark(acl_nfmark
* head
, ACLChecklist
* ch
)
1523 for (l
= head
; l
; l
= l
->next
) {
1524 if (!l
->aclList
|| ch
->fastCheck(l
->aclList
) == ACCESS_ALLOWED
)
1532 getOutgoingAddress(HttpRequest
* request
, Comm::ConnectionPointer conn
)
1534 // skip if an outgoing address is already set.
1535 if (!conn
->local
.isAnyAddr()) return;
1537 // ensure that at minimum the wildcard local matches remote protocol
1538 if (conn
->remote
.isIPv4())
1539 conn
->local
.setIPv4();
1541 // maybe use TPROXY client address
1542 if (request
&& request
->flags
.spoofClientIp
) {
1543 if (!conn
->getPeer() || !conn
->getPeer()->options
.no_tproxy
) {
1544 #if FOLLOW_X_FORWARDED_FOR && LINUX_NETFILTER
1545 if (Config
.onoff
.tproxy_uses_indirect_client
)
1546 conn
->local
= request
->indirect_client_addr
;
1549 conn
->local
= request
->client_addr
;
1550 // some flags need setting on the socket to use this address
1551 conn
->flags
|= COMM_DOBIND
;
1552 conn
->flags
|= COMM_TRANSPARENT
;
1555 // else no tproxy today ...
1558 if (!Config
.accessList
.outgoing_address
) {
1559 return; // anything will do.
1562 ACLFilledChecklist
ch(NULL
, request
, NULL
);
1563 ch
.dst_peer
= conn
->getPeer();
1564 ch
.dst_addr
= conn
->remote
;
1566 // TODO use the connection details in ACL.
1567 // needs a bit of rework in ACLFilledChecklist to use Comm::Connection instead of ConnStateData
1570 for (l
= Config
.accessList
.outgoing_address
; l
; l
= l
->next
) {
1572 /* check if the outgoing address is usable to the destination */
1573 if (conn
->remote
.isIPv4() != l
->addr
.isIPv4()) continue;
1575 /* check ACLs for this outgoing address */
1576 if (!l
->aclList
|| ch
.fastCheck(l
->aclList
) == ACCESS_ALLOWED
) {
1577 conn
->local
= l
->addr
;
1584 GetTosToServer(HttpRequest
* request
)
1586 ACLFilledChecklist
ch(NULL
, request
, NULL
);
1587 return aclMapTOS(Ip::Qos::TheConfig
.tosToServer
, &ch
);
1591 GetNfmarkToServer(HttpRequest
* request
)
1593 ACLFilledChecklist
ch(NULL
, request
, NULL
);
1594 return aclMapNfmark(Ip::Qos::TheConfig
.nfmarkToServer
, &ch
);