]> git.ipfire.org Git - thirdparty/squid.git/blame - src/FwdState.cc
Bug 4042: ext_kerberos_ldap_group: add -P principal option (#195)
[thirdparty/squid.git] / src / FwdState.cc
CommitLineData
41462d93 1/*
5b74111a 2 * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
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.
41462d93 7 */
8
bbc27441
AJ
9/* DEBUG: section 17 Request Forwarding */
10
582c2af2 11#include "squid.h"
4bf68cfa 12#include "AccessLogEntry.h"
289848ca 13#include "acl/Address.h"
c0941a6a
AR
14#include "acl/FilledChecklist.h"
15#include "acl/Gadgets.h"
5ae65581 16#include "anyp/PortCfg.h"
25b481e6 17#include "CacheManager.h"
602d9612 18#include "CachePeer.h"
582c2af2 19#include "client_side.h"
92ae4c86 20#include "clients/forward.h"
f9b72e0c 21#include "comm/Connection.h"
aed188fd 22#include "comm/ConnOpener.h"
d841c88d 23#include "comm/Loops.h"
582c2af2 24#include "CommCalls.h"
aa839030 25#include "errorpage.h"
582c2af2 26#include "event.h"
fc54b8d2 27#include "fd.h"
aa839030 28#include "fde.h"
eb13c21e 29#include "FwdState.h"
582c2af2 30#include "globals.h"
fc54b8d2 31#include "gopher.h"
bbaf2685 32#include "hier_code.h"
fc54b8d2 33#include "http.h"
d3dddfb5 34#include "http/Stream.h"
924f73bc 35#include "HttpReply.h"
aa839030 36#include "HttpRequest.h"
582c2af2 37#include "icmp/net_db.h"
fc54b8d2 38#include "internal.h"
582c2af2 39#include "ip/Intercept.h"
244da4ad 40#include "ip/NfMarkConfig.h"
425de4c8 41#include "ip/QosConfig.h"
582c2af2 42#include "ip/tools.h"
aa839030 43#include "MemObject.h"
582c2af2 44#include "mgr/Registration.h"
fc54b8d2 45#include "neighbors.h"
781ce8ff 46#include "pconn.h"
e8dca475 47#include "PeerPoolMgr.h"
a72b6e88 48#include "security/BlindPeerConnector.h"
4d5904f7 49#include "SquidConfig.h"
aa839030 50#include "SquidTime.h"
32f1ca3f 51#include "ssl/PeekingPeerConnector.h"
aa839030 52#include "Store.h"
e87137f1 53#include "StoreClient.h"
5eb529cb 54#include "urn.h"
fc54b8d2 55#include "whois.h"
cb4f4424 56#if USE_OPENSSL
2cef0ca6
AR
57#include "ssl/cert_validate_message.h"
58#include "ssl/Config.h"
4d16918e 59#include "ssl/ErrorDetail.h"
602d9612 60#include "ssl/helper.h"
fd4624d7 61#include "ssl/ServerBump.h"
602d9612 62#include "ssl/support.h"
fcfdf7f9
AJ
63#else
64#include "security/EncryptorAnswer.h"
4db984be 65#endif
1a30fdf5
AJ
66
67#include <cerrno>
cfd66529 68
575d05c4 69static CLCB fwdServerClosedWrapper;
b6b6f466 70static CNCB fwdConnectDoneWrapper;
71
8ddcc35d 72static OBJH fwdStats;
73
74#define MAX_FWD_STATS_IDX 9
955394ce 75static int FwdReplyCodes[MAX_FWD_STATS_IDX + 1][Http::scInvalidHeader + 1];
db1cd23c 76
d5430dc8 77static PconnPool *fwdPconnPool = new PconnPool("server-peers", NULL);
b6b6f466 78CBDATA_CLASS_INIT(FwdState);
781ce8ff 79
a72b6e88 80class FwdStatePeerAnswerDialer: public CallDialer, public Security::PeerConnector::CbDialer
a23223bf
CT
81{
82public:
fcfdf7f9 83 typedef void (FwdState::*Method)(Security::EncryptorAnswer &);
a23223bf
CT
84
85 FwdStatePeerAnswerDialer(Method method, FwdState *fwd):
f53969cc 86 method_(method), fwd_(fwd), answer_() {}
a23223bf
CT
87
88 /* CallDialer API */
89 virtual bool canDial(AsyncCall &call) { return fwd_.valid(); }
90 void dial(AsyncCall &call) { ((&(*fwd_))->*method_)(answer_); }
91 virtual void print(std::ostream &os) const {
e2849af8
A
92 os << '(' << fwd_.get() << ", " << answer_ << ')';
93 }
a23223bf 94
a72b6e88 95 /* Security::PeerConnector::CbDialer API */
fcfdf7f9 96 virtual Security::EncryptorAnswer &answer() { return answer_; }
a23223bf
CT
97
98private:
99 Method method_;
100 CbcPointer<FwdState> fwd_;
fcfdf7f9 101 Security::EncryptorAnswer answer_;
a23223bf 102};
a23223bf 103
429871db 104void
105FwdState::abort(void* d)
106{
107 FwdState* fwd = (FwdState*)d;
6ecaf21a 108 Pointer tmp = fwd; // Grab a temporary pointer to keep the object alive during our scope.
429871db 109
6b679a01 110 if (Comm::IsConnOpen(fwd->serverConnection())) {
e8dca475 111 fwd->closeServerConnection("store entry aborted");
6dd9a2e4
AJ
112 } else {
113 debugs(17, 7, HERE << "store entry aborted; no connection to close");
cff02fa6 114 }
c33a88ca 115 fwd->serverDestinations.clear();
6043e368 116 fwd->stopAndDestroy("store entry aborted");
429871db 117}
118
e8dca475
CT
119void
120FwdState::closeServerConnection(const char *reason)
121{
122 debugs(17, 3, "because " << reason << "; " << serverConn);
398bc066
CT
123 comm_remove_close_handler(serverConn->fd, closeHandler);
124 closeHandler = NULL;
e8dca475
CT
125 fwdPconnPool->noteUses(fd_table[serverConn->fd].pconn.uses);
126 serverConn->close();
127}
128
b6b6f466 129/**** PUBLIC INTERFACE ********************************************************/
c7f9eb6d 130
4bf68cfa 131FwdState::FwdState(const Comm::ConnectionPointer &client, StoreEntry * e, HttpRequest * r, const AccessLogEntryPointer &alp):
cc8c4af2
AJ
132 entry(e),
133 request(r),
134 al(alp),
135 err(NULL),
136 clientConn(client),
137 start_t(squid_curtime),
138 n_tries(0),
139 pconnRace(raceImpossible)
db1cd23c 140{
cc8c4af2 141 debugs(17, 2, "Forwarding client request " << client << ", url=" << e->url());
b248c2a3 142 HTTPMSGLOCK(request);
00ae51e4 143 serverDestinations.reserve(Config.forward_max_tries);
1bfe9ade 144 e->lock("FwdState");
b6b6f466 145 EBIT_SET(e->flags, ENTRY_FWD_HDR_WAIT);
cc8c4af2
AJ
146 flags.connected_okay = false;
147 flags.dont_retry = false;
148 flags.forward_completed = false;
149 debugs(17, 3, "FwdState constructed, this=" << this);
7a0fb323 150}
151
152// Called once, right after object creation, when it is safe to set self
fc68f6b1 153void FwdState::start(Pointer aSelf)
154{
7a0fb323 155 // Protect ourselves from being destroyed when the only Server pointing
156 // to us is gone (while we expect to talk to more Servers later).
157 // Once we set self, we are responsible for clearing it when we do not
158 // expect to talk to any servers.
159 self = aSelf; // refcounted
160
161 // We hope that either the store entry aborts or peer is selected.
162 // Otherwise we are going to leak our object.
34266cde 163
92cfc72f
CT
164 // Ftp::Relay needs to preserve control connection on data aborts
165 // so it registers its own abort handler that calls ours when needed.
166 if (!request->flags.ftpNative)
167 entry->registerAbort(FwdState::abort, this);
bfe4e2fe 168
32c32865 169#if STRICT_ORIGINAL_DST
bfe4e2fe
AJ
170 // Bug 3243: CVE 2009-0801
171 // Bypass of browser same-origin access control in intercepted communication
172 // To resolve this we must force DIRECT and only to the original client destination.
0d901ef4 173 const bool isIntercepted = request && !request->flags.redirected && (request->flags.intercepted || request->flags.interceptTproxy);
2962f8b8
AJ
174 const bool useOriginalDst = Config.onoff.client_dst_passthru || (request && !request->flags.hostVerified);
175 if (isIntercepted && useOriginalDst) {
d7ce0bcd 176 selectPeerForIntercepted();
7177edfb
AJ
177 // 3.2 does not suppro re-wrapping inside CONNECT.
178 // our only alternative is to fake destination "found" and continue with the forwarding.
bfe4e2fe 179 startConnectionOrFail();
7177edfb 180 return;
bfe4e2fe 181 }
32c32865
AJ
182#endif
183
7177edfb 184 // do full route options selection
6043e368
AR
185 startSelectingDestinations(request, al, entry);
186}
187
188/// ends forwarding; relies on refcounting so the effect may not be immediate
189void
190FwdState::stopAndDestroy(const char *reason)
191{
192 debugs(17, 3, "for " << reason);
193 PeerSelectionInitiator::subscribed = false; // may already be false
194 self = nullptr; // we hope refcounting destroys us soon; may already be nil
195 /* do not place any code here as this object may be gone by now */
db1cd23c 196}
197
32c32865 198#if STRICT_ORIGINAL_DST
d7ce0bcd
AR
199/// bypasses peerSelect() when dealing with intercepted requests
200void
201FwdState::selectPeerForIntercepted()
202{
203 // use pinned connection if available
204 Comm::ConnectionPointer p;
693cb033 205 if (ConnStateData *client = request->pinnedConnection()) {
d7ce0bcd 206 p = client->validatePinnedConnection(request, NULL);
693cb033
CT
207 if (Comm::IsConnOpen(p)) {
208 /* duplicate peerSelectPinned() effects */
209 p->peerType = PINNED;
210 entry->ping_status = PING_DONE; /* Skip ICP */
d7ce0bcd 211
693cb033
CT
212 debugs(17, 3, "reusing a pinned conn: " << *p);
213 serverDestinations.push_back(p);
214 } else {
215 debugs(17,2, "Pinned connection is not valid: " << p);
955394ce 216 ErrorState *anErr = new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, request);
693cb033
CT
217 fail(anErr);
218 }
219 // Either use the valid pinned connection or fail if it is invalid.
220 return;
d7ce0bcd
AR
221 }
222
7177edfb
AJ
223 // use client original destination as second preferred choice
224 p = new Comm::Connection();
225 p->peerType = ORIGINAL_DST;
226 p->remote = clientConn->local;
227 getOutgoingAddress(request, p);
228
229 debugs(17, 3, HERE << "using client original destination: " << *p);
d7ce0bcd
AR
230 serverDestinations.push_back(p);
231}
32c32865 232#endif
d7ce0bcd 233
802a8c1d 234void
235FwdState::completed()
41462d93 236{
e857372a 237 if (flags.forward_completed) {
e0236918 238 debugs(17, DBG_IMPORTANT, HERE << "FwdState::completed called on a completed request! Bad!");
fc68f6b1 239 return;
240 }
241
e857372a 242 flags.forward_completed = true;
802a8c1d 243
16b70e2a
CT
244 request->hier.stopPeerClock(false);
245
c4a88a3e
CT
246 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
247 debugs(17, 3, HERE << "entry aborted");
248 return ;
249 }
250
bc87dc25 251#if URL_CHECKSUM_DEBUG
62e76326 252
b6b6f466 253 entry->mem_obj->checkUrlChecksum();
225644d7 254#endif
62e76326 255
b6b6f466 256 if (entry->store_status == STORE_PENDING) {
257 if (entry->isEmpty()) {
a04b7e6e 258 if (!err) // we quit (e.g., fd closed) before an error or content
955394ce 259 fail(new ErrorState(ERR_READ_ERROR, Http::scBadGateway, request));
b6b6f466 260 assert(err);
261 errorAppendEntry(entry, err);
262 err = NULL;
cb4f4424 263#if USE_OPENSSL
2cef0ca6
AR
264 if (request->flags.sslPeek && request->clientConnectionManager.valid()) {
265 CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
801cfc26 266 ConnStateData::httpsPeeked, ConnStateData::PinnedIdleContext(Comm::ConnectionPointer(nullptr), request));
2cef0ca6
AR
267 }
268#endif
62e76326 269 } else {
b6b6f466 270 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
271 entry->complete();
d88e3c49 272 entry->releaseRequest();
62e76326 273 }
f563eea9 274 }
62e76326 275
b6b6f466 276 if (storePendingNClients(entry) > 0)
277 assert(!EBIT_TEST(entry->flags, ENTRY_FWD_HDR_WAIT));
62e76326 278
802a8c1d 279}
280
281FwdState::~FwdState()
282{
cc8c4af2 283 debugs(17, 3, "FwdState destructor start");
fc68f6b1 284
802a8c1d 285 if (! flags.forward_completed)
fc68f6b1 286 completed();
802a8c1d 287
9d2760b6
AR
288 doneWithRetries();
289
6dd9f4bd 290 HTTPMSGUNLOCK(request);
62e76326 291
913524f0 292 delete err;
62e76326 293
3900307b 294 entry->unregisterAbort();
429871db 295
1bfe9ade 296 entry->unlock("FwdState");
62e76326 297
b6b6f466 298 entry = NULL;
62e76326 299
e3a4aecc
AJ
300 if (calls.connector != NULL) {
301 calls.connector->cancel("FwdState destructed");
302 calls.connector = NULL;
303 }
304
e8dca475
CT
305 if (Comm::IsConnOpen(serverConn))
306 closeServerConnection("~FwdState");
fc68f6b1 307
c33a88ca 308 serverDestinations.clear();
cfd66529 309
cc8c4af2 310 debugs(17, 3, "FwdState destructed, this=" << this);
b6b6f466 311}
62e76326 312
38413773 313/**
b6b6f466 314 * This is the entry point for client-side to start forwarding
315 * a transaction. It is a static method that may or may not
316 * allocate a FwdState.
317 */
be0c6690 318void
4bf68cfa 319FwdState::Start(const Comm::ConnectionPointer &clientConn, StoreEntry *entry, HttpRequest *request, const AccessLogEntryPointer &al)
b6b6f466 320{
b50e327b 321 /** \note
b6b6f466 322 * client_addr == no_addr indicates this is an "internal" request
323 * from peer_digest.c, asn.c, netdb.c, etc and should always
324 * be allowed. yuck, I know.
325 */
62e76326 326
4dd643d5 327 if ( Config.accessList.miss && !request->client_addr.isNoAddr() &&
4e3f4dc7 328 !request->flags.internal && request->url.getScheme() != AnyP::PROTO_CACHE_OBJECT) {
b50e327b 329 /**
fbd3636b
NR
330 * Check if this host is allowed to fetch MISSES from us (miss_access).
331 * Intentionally replace the src_addr automatically selected by the checklist code
332 * we do NOT want the indirect client address to be tested here.
b6b6f466 333 */
c0941a6a 334 ACLFilledChecklist ch(Config.accessList.miss, request, NULL);
b6b6f466 335 ch.src_addr = request->client_addr;
06bf5384 336 if (ch.fastCheck().denied()) {
b6b6f466 337 err_type page_id;
9ce7856a 338 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, 1);
b6b6f466 339
340 if (page_id == ERR_NONE)
341 page_id = ERR_FORWARDING_DENIED;
342
955394ce 343 ErrorState *anErr = new ErrorState(page_id, Http::scForbidden, request);
f53969cc 344 errorAppendEntry(entry, anErr); // frees anErr
be0c6690 345 return;
b6b6f466 346 }
347 }
348
cfd66529 349 debugs(17, 3, HERE << "'" << entry->url() << "'");
f4ef658f 350 /*
351 * This seems like an odd place to bind mem_obj and request.
352 * Might want to assert that request is NULL at this point
353 */
b248c2a3 354 entry->mem_obj->request = request;
b6b6f466 355#if URL_CHECKSUM_DEBUG
356
357 entry->mem_obj->checkUrlChecksum();
358#endif
359
360 if (shutting_down) {
361 /* more yuck */
955394ce 362 ErrorState *anErr = new ErrorState(ERR_SHUTTING_DOWN, Http::scServiceUnavailable, request);
f53969cc 363 errorAppendEntry(entry, anErr); // frees anErr
be0c6690 364 return;
6801f8a8 365 }
62e76326 366
4e3f4dc7
AJ
367 if (request->flags.internal) {
368 debugs(17, 2, "calling internalStart() due to request flag");
e37bd29b 369 internalStart(clientConn, request, entry);
be0c6690 370 return;
4e3f4dc7
AJ
371 }
372
373 switch (request->url.getScheme()) {
b6b6f466 374
39a19cb7 375 case AnyP::PROTO_CACHE_OBJECT:
4e3f4dc7 376 debugs(17, 2, "calling CacheManager due to request scheme " << request->url.getScheme());
5c336a3b 377 CacheManager::GetInstance()->Start(clientConn, request, entry);
be0c6690 378 return;
b6b6f466 379
0c3d3f65 380 case AnyP::PROTO_URN:
b6b6f466 381 urnStart(request, entry);
be0c6690 382 return;
b6b6f466 383
384 default:
4bf68cfa 385 FwdState::Pointer fwd = new FwdState(clientConn, entry, request, al);
7a0fb323 386 fwd->start(fwd);
be0c6690 387 return;
b6b6f466 388 }
389
390 /* NOTREACHED */
41462d93 391}
392
4bf68cfa
AR
393void
394FwdState::fwdStart(const Comm::ConnectionPointer &clientConn, StoreEntry *entry, HttpRequest *request)
395{
396 // Hides AccessLogEntry.h from code that does not supply ALE anyway.
397 Start(clientConn, entry, request, NULL);
398}
399
0ce8e93b
EB
400/// subtracts time_t values, returning zero if smaller exceeds the larger value
401/// time_t might be unsigned so we need to be careful when subtracting times...
402static inline time_t
403diffOrZero(const time_t larger, const time_t smaller)
404{
405 return (larger > smaller) ? (larger - smaller) : 0;
406}
407
408/// time left to finish the whole forwarding process (which started at fwdStart)
409time_t
410FwdState::ForwardTimeout(const time_t fwdStart)
411{
412 // time already spent on forwarding (0 if clock went backwards)
413 const time_t timeSpent = diffOrZero(squid_curtime, fwdStart);
414 return diffOrZero(Config.Timeout.forward, timeSpent);
415}
416
417bool
418FwdState::EnoughTimeToReForward(const time_t fwdStart)
419{
420 return ForwardTimeout(fwdStart) > 0;
421}
422
cfd66529 423void
6b679a01 424FwdState::startConnectionOrFail()
cfd66529 425{
8652f8e7 426 debugs(17, 3, HERE << entry->url());
cfd66529 427
00ae51e4 428 if (serverDestinations.size() > 0) {
8652f8e7
AJ
429 // Ditch error page if it was created before.
430 // A new one will be created if there's another problem
913524f0
AJ
431 delete err;
432 err = NULL;
8652f8e7
AJ
433
434 // Update the logging information about this new server connection.
435 // Done here before anything else so the errors get logged for
436 // this server link regardless of what happens when connecting to it.
437 // IF sucessfuly connected this top destination will become the serverConnection().
7d1dac79 438 syncHierNote(serverDestinations[0], request->url.host());
129fe2a1 439 request->clearError();
8652f8e7 440
cfd66529
AJ
441 connectStart();
442 } else {
6043e368
AR
443 if (PeerSelectionInitiator::subscribed) {
444 debugs(17, 4, "wait for more destinations to try");
445 return; // expect a noteDestination*() call
446 }
447
95b83010
CT
448 debugs(17, 3, HERE << "Connection failed: " << entry->url());
449 if (!err) {
955394ce 450 ErrorState *anErr = new ErrorState(ERR_CANNOT_FORWARD, Http::scInternalServerError, request);
95b83010 451 fail(anErr);
8652f8e7 452 } // else use actual error from last connection attempt
6043e368
AR
453
454 stopAndDestroy("tried all destinations");
cfd66529
AJ
455 }
456}
457
b6b6f466 458void
459FwdState::fail(ErrorState * errorState)
460{
9b769c67 461 debugs(17, 3, err_type_str[errorState->type] << " \"" << Http::StatusCodeString(errorState->httpStatus) << "\"\n\t" << entry->url());
b6b6f466 462
913524f0 463 delete err;
b6b6f466 464 err = errorState;
465
e2cc8c07 466 if (!errorState->request)
b248c2a3 467 errorState->request = request;
64b66b76 468
693cb033
CT
469 if (err->type != ERR_ZERO_SIZE_OBJECT)
470 return;
471
472 if (pconnRace == racePossible) {
bc81ee68
AR
473 debugs(17, 5, HERE << "pconn race happened");
474 pconnRace = raceHappened;
475 }
693cb033
CT
476
477 if (ConnStateData *pinned_connection = request->pinnedConnection()) {
478 pinned_connection->pinning.zeroReply = true;
479 flags.dont_retry = true; // we want to propagate failure to the client
480 debugs(17, 4, "zero reply on pinned connection");
481 }
b6b6f466 482}
483
38413773 484/**
b6b6f466 485 * Frees fwdState without closing FD or generating an abort
486 */
487void
00ae51e4 488FwdState::unregister(Comm::ConnectionPointer &conn)
5229395c
AJ
489{
490 debugs(17, 3, HERE << entry->url() );
491 assert(serverConnection() == conn);
6b679a01 492 assert(Comm::IsConnOpen(conn));
398bc066
CT
493 comm_remove_close_handler(conn->fd, closeHandler);
494 closeHandler = NULL;
00ae51e4 495 serverConn = NULL;
5229395c
AJ
496}
497
d5430dc8 498// \deprecated use unregister(Comm::ConnectionPointer &conn) instead
5229395c 499void
b6b6f466 500FwdState::unregister(int fd)
501{
5229395c
AJ
502 debugs(17, 3, HERE << entry->url() );
503 assert(fd == serverConnection()->fd);
00ae51e4 504 unregister(serverConn);
b6b6f466 505}
506
38413773 507/**
d5430dc8 508 * FooClient modules call fwdComplete() when they are done
b6b6f466 509 * downloading an object. Then, we either 1) re-forward the
510 * request somewhere else if needed, or 2) call storeComplete()
511 * to finish it off
512 */
513void
514FwdState::complete()
515{
9b769c67 516 debugs(17, 3, HERE << entry->url() << "\n\tstatus " << entry->getReply()->sline.status());
b6b6f466 517#if URL_CHECKSUM_DEBUG
518
519 entry->mem_obj->checkUrlChecksum();
520#endif
521
9b769c67 522 logReplyStatus(n_tries, entry->getReply()->sline.status());
b6b6f466 523
524 if (reforward()) {
9b769c67 525 debugs(17, 3, HERE << "re-forwarding " << entry->getReply()->sline.status() << " " << entry->url());
b6b6f466 526
6b679a01 527 if (Comm::IsConnOpen(serverConn))
00ae51e4 528 unregister(serverConn);
b6b6f466 529
cfd66529
AJ
530 entry->reset();
531
8652f8e7 532 // drop the last path off the selection list. try the next one.
6043e368
AR
533 if (!serverDestinations.empty()) // paranoid
534 serverDestinations.erase(serverDestinations.begin());
8652f8e7
AJ
535 startConnectionOrFail();
536
b6b6f466 537 } else {
6b679a01 538 if (Comm::IsConnOpen(serverConn))
9b769c67 539 debugs(17, 3, HERE << "server FD " << serverConnection()->fd << " not re-forwarding status " << entry->getReply()->sline.status());
6b679a01 540 else
9b769c67 541 debugs(17, 3, HERE << "server (FD closed) not re-forwarding status " << entry->getReply()->sline.status());
9e5c22cf
AJ
542 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
543 entry->complete();
fc68f6b1 544
6b679a01 545 if (!Comm::IsConnOpen(serverConn))
fc68f6b1 546 completed();
547
6043e368 548 stopAndDestroy("forwarding completed");
b6b6f466 549 }
550}
551
6043e368
AR
552void
553FwdState::noteDestination(Comm::ConnectionPointer path)
554{
555 const bool wasBlocked = serverDestinations.empty();
556 serverDestinations.push_back(path);
557 if (wasBlocked)
558 startConnectionOrFail();
559 // else continue to use one of the previously noted destinations;
560 // if all of them fail, we may try this path
561}
b6b6f466 562
6043e368
AR
563void
564FwdState::noteDestinationsEnd(ErrorState *selectionError)
b6b6f466 565{
6043e368 566 PeerSelectionInitiator::subscribed = false;
7cbbf717 567 if (serverDestinations.empty()) { // was blocked, waiting for more paths
6043e368
AR
568
569 if (selectionError) {
570 debugs(17, 3, "Will abort forwarding because path selection has failed.");
571 Must(!err); // if we tried to connect, then path selection succeeded
572 fail(selectionError);
573 }
574 else if (err)
575 debugs(17, 3, "Will abort forwarding because all found paths have failed.");
576 else
577 debugs(17, 3, "Will abort forwarding because path selection found no paths.");
578
579 startConnectionOrFail(); // will choose the OrFail code path
580 return;
581 }
582 // else continue to use one of the previously noted destinations;
583 // if all of them fail, forwarding as whole will fail
584 Must(!selectionError); // finding at least one path means selection succeeded
b6b6f466 585}
586
6043e368
AR
587/**** CALLBACK WRAPPERS ************************************************************/
588
b6b6f466 589static void
575d05c4 590fwdServerClosedWrapper(const CommCloseCbParams &params)
b6b6f466 591{
575d05c4
AJ
592 FwdState *fwd = (FwdState *)params.data;
593 fwd->serverClosed(params.fd);
b6b6f466 594}
595
cfd66529 596void
c8407295 597fwdConnectDoneWrapper(const Comm::ConnectionPointer &conn, Comm::Flag status, int xerrno, void *data)
b6b6f466 598{
599 FwdState *fwd = (FwdState *) data;
aed188fd 600 fwd->connectDone(conn, status, xerrno);
b6b6f466 601}
602
603/**** PRIVATE *****************************************************************/
604
545782b8 605/*
606 * FwdState::checkRetry
26ac0430 607 *
545782b8 608 * Return TRUE if the request SHOULD be retried. This method is
609 * called when the HTTP connection fails, or when the connection
d5430dc8 610 * is closed before reading the end of HTTP headers from the server.
545782b8 611 */
b6b6f466 612bool
613FwdState::checkRetry()
68bd6892 614{
d8fd0f18 615 if (shutting_down)
b6b6f466 616 return false;
62e76326 617
9d2760b6
AR
618 if (!self) { // we have aborted before the server called us back
619 debugs(17, 5, HERE << "not retrying because of earlier abort");
620 // we will be destroyed when the server clears its Pointer to us
621 return false;
622 }
623
b6b6f466 624 if (entry->store_status != STORE_PENDING)
625 return false;
62e76326 626
b6b6f466 627 if (!entry->isEmpty())
628 return false;
62e76326 629
23a1e39c 630 if (n_tries > Config.forward_max_tries)
b6b6f466 631 return false;
4ed0e075 632
0ce8e93b 633 if (!EnoughTimeToReForward(start_t))
b6b6f466 634 return false;
62e76326 635
b6b6f466 636 if (flags.dont_retry)
637 return false;
62e76326 638
3b9899f7
AR
639 if (request->bodyNibbled())
640 return false;
641
0bbd5532
AJ
642 // NP: not yet actually connected anywhere. retry is safe.
643 if (!flags.connected_okay)
644 return true;
645
5d4989a8 646 if (!checkRetriable())
647 return false;
648
b6b6f466 649 return true;
68bd6892 650}
651
afc753f3 652/// Whether we may try sending this request again after a failure.
b6b6f466 653bool
654FwdState::checkRetriable()
cb928909 655{
ccbcff0e
AR
656 // Optimize: A compliant proxy may retry PUTs, but Squid lacks the [rather
657 // complicated] code required to protect the PUT request body from being
658 // nibbled during the first try. Thus, Squid cannot retry some PUTs today.
659 if (request->body_pipe != NULL)
660 return false;
661
c2a7cefd
AJ
662 // RFC2616 9.1 Safe and Idempotent Methods
663 return (request->method.isHttpSafe() || request->method.isIdempotent());
cb928909 664}
665
b6b6f466 666void
667FwdState::serverClosed(int fd)
910169e5 668{
8ed21336 669 // XXX: fd is often -1 here
e8dca475 670 debugs(17, 2, "FD " << fd << " " << entry->url() << " after " <<
8ed21336
AR
671 (fd >= 0 ? fd_table[fd].pconn.uses : -1) << " requests");
672 if (fd >= 0 && serverConnection()->fd == fd)
e8dca475 673 fwdPconnPool->noteUses(fd_table[fd].pconn.uses);
3e8c047e 674 retryOrBail();
675}
676
677void
26ac0430
AJ
678FwdState::retryOrBail()
679{
b6b6f466 680 if (checkRetry()) {
cfd66529 681 debugs(17, 3, HERE << "re-forwarding (" << n_tries << " tries, " << (squid_curtime - start_t) << " secs)");
bc81ee68
AR
682 // we should retry the same destination if it failed due to pconn race
683 if (pconnRace == raceHappened)
684 debugs(17, 4, HERE << "retrying the same destination");
685 else
b181c1df 686 serverDestinations.erase(serverDestinations.begin()); // last one failed. try another.
8652f8e7
AJ
687 startConnectionOrFail();
688 return;
d8fd0f18 689 }
62e76326 690
9d2760b6
AR
691 // TODO: should we call completed() here and move doneWithRetries there?
692 doneWithRetries();
693
16b70e2a
CT
694 request->hier.stopPeerClock(false);
695
8f01bdfb 696 if (self != NULL && !err && shutting_down && entry->isEmpty()) {
955394ce 697 ErrorState *anErr = new ErrorState(ERR_SHUTTING_DOWN, Http::scServiceUnavailable, request);
f01d4b80 698 errorAppendEntry(entry, anErr);
f563eea9 699 }
62e76326 700
6043e368 701 stopAndDestroy("cannot retry");
910169e5 702}
703
9d2760b6
AR
704// If the Server quits before nibbling at the request body, the body sender
705// will not know (so that we can retry). Call this if we will not retry. We
706// will notify the sender so that it does not get stuck waiting for space.
707void
708FwdState::doneWithRetries()
709{
710 if (request && request->body_pipe != NULL)
711 request->body_pipe->expectNoConsumption();
712}
713
3e8c047e 714// called by the server that failed after calling unregister()
715void
716FwdState::handleUnregisteredServerEnd()
717{
cfd66529 718 debugs(17, 2, HERE << "self=" << self << " err=" << err << ' ' << entry->url());
6b679a01 719 assert(!Comm::IsConnOpen(serverConn));
3e8c047e 720 retryOrBail();
721}
722
b6b6f466 723void
c8407295 724FwdState::connectDone(const Comm::ConnectionPointer &conn, Comm::Flag status, int xerrno)
41462d93 725{
c8407295 726 if (status != Comm::OK) {
9c8a6c3b 727 ErrorState *const anErr = makeConnectingError(ERR_CONNECT_FAIL);
b6b6f466 728 anErr->xerrno = xerrno;
b6b6f466 729 fail(anErr);
62e76326 730
2f538b78 731 /* it might have been a timeout with a partially open link */
00ae51e4
AJ
732 if (conn != NULL) {
733 if (conn->getPeer())
734 peerConnectFailed(conn->getPeer());
62e76326 735
80463bb4 736 conn->close();
2f538b78 737 }
aed188fd 738 retryOrBail();
2f538b78
AJ
739 return;
740 }
62e76326 741
00ae51e4 742 serverConn = conn;
f01d4b80 743 debugs(17, 3, HERE << serverConnection() << ": '" << entry->url() << "'" );
62e76326 744
398bc066 745 closeHandler = comm_add_close_handler(serverConnection()->fd, fwdServerClosedWrapper, this);
2f538b78 746
693cb033 747 if (!request->flags.pinned) {
d9a7bc71
CT
748 const CachePeer *p = serverConnection()->getPeer();
749 const bool peerWantsTls = p && p->secure.encryptTransport;
b53b9f8d 750 // userWillTlsToPeerForUs assumes CONNECT == HTTPS
d9a7bc71 751 const bool userWillTlsToPeerForUs = p && p->options.originserver &&
66c60bb0 752 request->method == Http::METHOD_CONNECT;
d9a7bc71
CT
753 const bool needTlsToPeer = peerWantsTls && !userWillTlsToPeerForUs;
754 const bool needTlsToOrigin = !p && request->url.getScheme() == AnyP::PROTO_HTTPS;
755 if (needTlsToPeer || needTlsToOrigin || request->flags.sslPeek) {
a23223bf
CT
756 HttpRequest::Pointer requestPointer = request;
757 AsyncCall::Pointer callback = asyncCall(17,4,
e2849af8
A
758 "FwdState::ConnectedToPeer",
759 FwdStatePeerAnswerDialer(&FwdState::connectedToPeer, this));
8aec3e1b 760 // Use positive timeout when less than one second is left.
0ce8e93b
EB
761 const time_t connTimeout = serverDestinations[0]->connectTimeout(start_t);
762 const time_t sslNegotiationTimeout = positiveTimeout(connTimeout);
a72b6e88
AJ
763 Security::PeerConnector *connector = nullptr;
764#if USE_OPENSSL
2e249337 765 if (request->flags.sslPeek)
d4ddb3e6 766 connector = new Ssl::PeekingPeerConnector(requestPointer, serverConnection(), clientConn, callback, al, sslNegotiationTimeout);
2e249337 767 else
a72b6e88
AJ
768#endif
769 connector = new Security::BlindPeerConnector(requestPointer, serverConnection(), callback, al, sslNegotiationTimeout);
a23223bf 770 AsyncJob::Start(connector); // will call our callback
39322eba
J
771 return;
772 }
41462d93 773 }
2f538b78 774
fcfdf7f9
AJ
775 // if not encrypting just run the post-connect actions
776 Security::EncryptorAnswer nil;
777 connectedToPeer(nil);
41462d93 778}
779
a23223bf 780void
fcfdf7f9 781FwdState::connectedToPeer(Security::EncryptorAnswer &answer)
a23223bf
CT
782{
783 if (ErrorState *error = answer.error.get()) {
784 fail(error);
785 answer.error.clear(); // preserve error for errorSendComplete()
89d7efa5
CT
786 if (CachePeer *p = serverConnection()->getPeer())
787 peerConnectFailed(p);
4e646287 788 serverConnection()->close();
a23223bf
CT
789 return;
790 }
791
56753478
CT
792 if (answer.tunneled) {
793 // TODO: When ConnStateData establishes tunnels, its state changes
794 // [in ways that may affect logging?]. Consider informing
795 // ConnStateData about our tunnel or otherwise unifying tunnel
796 // establishment [side effects].
797 unregister(serverConn); // async call owns it now
798 complete(); // destroys us
799 return;
800 }
801
fcfdf7f9
AJ
802 // should reach ConnStateData before the dispatched Client job starts
803 CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
804 ConnStateData::notePeerConnection, serverConnection());
805
89d7efa5
CT
806 if (serverConnection()->getPeer())
807 peerConnectSucceded(serverConnection()->getPeer());
808
809 flags.connected_okay = true;
a23223bf
CT
810 dispatch();
811}
a23223bf 812
b6b6f466 813void
814FwdState::connectTimeout(int fd)
41462d93 815{
bf8fe701 816 debugs(17, 2, "fwdConnectTimeout: FD " << fd << ": '" << entry->url() << "'" );
6b679a01
AJ
817 assert(serverDestinations[0] != NULL);
818 assert(fd == serverDestinations[0]->fd);
62e76326 819
528b2c61 820 if (entry->isEmpty()) {
f11c8e2f 821 ErrorState *anErr = new ErrorState(ERR_CONNECT_FAIL, Http::scGatewayTimeout, request);
b6b6f466 822 anErr->xerrno = ETIMEDOUT;
823 fail(anErr);
62e76326 824
cfd66529 825 /* This marks the peer DOWN ... */
6b679a01
AJ
826 if (serverDestinations[0]->getPeer())
827 peerConnectFailed(serverDestinations[0]->getPeer());
41462d93 828 }
62e76326 829
6b679a01
AJ
830 if (Comm::IsConnOpen(serverDestinations[0])) {
831 serverDestinations[0]->close();
746beefe 832 }
41462d93 833}
834
4b77ea6b
AR
835/// called when serverConn is set to an _open_ to-peer connection
836void
837FwdState::syncWithServerConn(const char *host)
838{
839 if (Ip::Qos::TheConfig.isAclTosActive())
840 Ip::Qos::setSockTos(serverConn, GetTosToServer(request));
841
842#if SO_MARK
843 if (Ip::Qos::TheConfig.isAclNfmarkActive())
844 Ip::Qos::setSockNfmark(serverConn, GetNfmarkToServer(request));
845#endif
846
7d1dac79
EB
847 syncHierNote(serverConn, host);
848}
849
850void
851FwdState::syncHierNote(const Comm::ConnectionPointer &server, const char *host)
852{
1ce66e29 853 if (request)
d8165775 854 request->hier.resetPeerNotes(server, host);
1ce66e29 855 if (al)
d8165775 856 al->hier.resetPeerNotes(server, host);
4b77ea6b
AR
857}
858
8aec3e1b
CT
859/**
860 * Called after forwarding path selection (via peer select) has taken place
861 * and whenever forwarding needs to attempt a new connection (routing failover).
862 * We have a vector of possible localIP->remoteIP paths now ready to start being connected.
863 */
864void
865FwdState::connectStart()
866{
867 assert(serverDestinations.size() > 0);
868
869 debugs(17, 3, "fwdConnectStart: " << entry->url());
870
16b70e2a 871 request->hier.startPeerClock();
777831e0 872
d9a7bc71
CT
873 // Do not fowrward bumped connections to parent proxy unless it is an
874 // origin server
875 if (serverDestinations[0]->getPeer() && !serverDestinations[0]->getPeer()->options.originserver && request->flags.sslBumped) {
2cf8425b 876 debugs(50, 4, "fwdConnectStart: Ssl bumped connections through parent proxy are not allowed");
955394ce 877 ErrorState *anErr = new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request);
b68415a1 878 fail(anErr);
6043e368 879 stopAndDestroy("SslBump misconfiguration");
b68415a1 880 return;
985bfe6b 881 }
d67acb4e 882
e857372a 883 request->flags.pinned = false; // XXX: what if the ConnStateData set this to flag existing credentials?
2efeb0b7
AJ
884 // XXX: answer: the peer selection *should* catch it and give us only the pinned peer. so we reverse the =0 step below.
885 // XXX: also, logs will now lie if pinning is broken and leads to an error message.
6b679a01 886 if (serverDestinations[0]->peerType == PINNED) {
26ac0430 887 ConnStateData *pinned_connection = request->pinnedConnection();
693cb033 888 debugs(17,7, "pinned peer connection: " << pinned_connection);
bc81ee68 889 // pinned_connection may become nil after a pconn race
60199bd7
CT
890 serverConn = pinned_connection ? pinned_connection->borrowPinnedConnection(request, serverDestinations[0]->getPeer()) : nullptr;
891 if (Comm::IsConnOpen(serverConn)) {
892 flags.connected_okay = true;
893 ++n_tries;
894 request->flags.pinned = true;
895
896 if (pinned_connection->pinnedAuth())
897 request->flags.auth = true;
898
899 closeHandler = comm_add_close_handler(serverConn->fd, fwdServerClosedWrapper, this);
900
901 syncWithServerConn(pinned_connection->pinning.host);
902
903 // the server may close the pinned connection before this request
904 pconnRace = racePossible;
905 dispatch();
906 return;
907 }
b410d563 908
693cb033 909 // Pinned connection failure.
85563fd9 910 debugs(17,2,HERE << "Pinned connection failed: " << pinned_connection);
955394ce 911 ErrorState *anErr = new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, request);
693cb033 912 fail(anErr);
6043e368 913 stopAndDestroy("pinned connection failure");
693cb033 914 return;
26ac0430 915 }
d67acb4e 916
642a305c 917 // Use pconn to avoid opening a new connection.
7a150c71
AJ
918 const char *host = NULL;
919 if (!serverDestinations[0]->getPeer())
5c51bffb 920 host = request->url.host();
bc81ee68
AR
921
922 Comm::ConnectionPointer temp;
923 // Avoid pconns after races so that the same client does not suffer twice.
924 // This does not increase the total number of connections because we just
925 // closed the connection that failed the race. And re-pinning assumes this.
926 if (pconnRace != raceHappened)
e8dca475 927 temp = pconnPop(serverDestinations[0], host);
bbf706ff 928
bc81ee68
AR
929 const bool openedPconn = Comm::IsConnOpen(temp);
930 pconnRace = openedPconn ? racePossible : raceImpossible;
cfd66529 931
6b679a01 932 // if we found an open persistent connection to use. use it.
bc81ee68 933 if (openedPconn) {
80463bb4 934 serverConn = temp;
3b9899f7 935 flags.connected_okay = true;
642a305c 936 debugs(17, 3, HERE << "reusing pconn " << serverConnection());
95dc7ff4 937 ++n_tries;
4ed0e075 938
398bc066 939 closeHandler = comm_add_close_handler(serverConnection()->fd, fwdServerClosedWrapper, this);
b5523edc 940
4b77ea6b 941 syncWithServerConn(request->url.host());
b5523edc 942
c8ceec27 943 dispatch();
c8ceec27 944 return;
41462d93 945 }
62e76326 946
bc81ee68
AR
947 // We will try to open a new connection, possibly to the same destination.
948 // We reset serverDestinations[0] in case we are using it again because
949 // ConnOpener modifies its destination argument.
4dd643d5 950 serverDestinations[0]->local.port(0);
bc81ee68
AR
951 serverConn = NULL;
952
bc87dc25 953#if URL_CHECKSUM_DEBUG
b6b6f466 954 entry->mem_obj->checkUrlChecksum();
bc87dc25 955#endif
62e76326 956
6ee88490 957 GetMarkingsToServer(request, *serverDestinations[0]);
b5523edc 958
e3a4aecc 959 calls.connector = commCbCall(17,3, "fwdConnectDoneWrapper", CommConnectCbPtrFun(fwdConnectDoneWrapper, this));
0ce8e93b
EB
960 const time_t connTimeout = serverDestinations[0]->connectTimeout(start_t);
961 Comm::ConnOpener *cs = new Comm::ConnOpener(serverDestinations[0], calls.connector, connTimeout);
7a150c71
AJ
962 if (host)
963 cs->setHost(host);
855150a4 964 AsyncJob::Start(cs);
41462d93 965}
966
b6b6f466 967void
968FwdState::dispatch()
41462d93 969{
7f06a3d8 970 debugs(17, 3, clientConn << ": Fetching " << request->method << ' ' << entry->url());
e0ebe27c 971 /*
972 * Assert that server_fd is set. This is to guarantee that fwdState
973 * is attached to something and will be deallocated when server_fd
974 * is closed.
975 */
6b679a01 976 assert(Comm::IsConnOpen(serverConn));
62e76326 977
5229395c 978 fd_note(serverConnection()->fd, entry->url());
62e76326 979
e8dca475 980 fd_table[serverConnection()->fd].noteUse();
62e76326 981
a7ad6e4e 982 /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
983 assert(entry->ping_status != PING_WAITING);
62e76326 984
1bfe9ade 985 assert(entry->locked());
62e76326 986
a7ad6e4e 987 EBIT_SET(entry->flags, ENTRY_DISPATCHED);
62e76326 988
5c51bffb 989 netdbPingSite(request->url.host());
62e76326 990
425de4c8 991 /* Retrieves remote server TOS or MARK value, and stores it as part of the
7172612f 992 * original client request FD object. It is later used to forward
425de4c8 993 * remote server's TOS/MARK in the response to the client in case of a MISS.
7172612f 994 */
425de4c8 995 if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
b5523edc
AJ
996 if (Comm::IsConnOpen(clientConn) && Comm::IsConnOpen(serverConnection())) {
997 fde * clientFde = &fd_table[clientConn->fd]; // XXX: move the fd_table access into Ip::Qos
653d9927 998 /* Get the netfilter CONNMARK */
244da4ad 999 clientFde->nfConnmarkFromServer = Ip::Qos::getNfConnmark(serverConnection(), Ip::Qos::dirOpened);
425de4c8
AJ
1000 }
1001 }
1002
1003#if _SQUID_LINUX_
1004 /* Bug 2537: The TOS forward part of QOS only applies to patched Linux kernels. */
1005 if (Ip::Qos::TheConfig.isHitTosActive()) {
b5523edc
AJ
1006 if (Comm::IsConnOpen(clientConn)) {
1007 fde * clientFde = &fd_table[clientConn->fd]; // XXX: move the fd_table access into Ip::Qos
425de4c8 1008 /* Get the TOS value for the packet */
b5523edc 1009 Ip::Qos::getTosFromServer(serverConnection(), clientFde);
7172612f 1010 }
26ac0430 1011 }
7172612f
AJ
1012#endif
1013
cb4f4424 1014#if USE_OPENSSL
2c065fc8 1015 if (request->flags.sslPeek) {
d7ce0bcd 1016 CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
801cfc26 1017 ConnStateData::httpsPeeked, ConnStateData::PinnedIdleContext(serverConnection(), request));
d7ce0bcd
AR
1018 unregister(serverConn); // async call owns it now
1019 complete(); // destroys us
1020 return;
061bbdec 1021 }
d7ce0bcd
AR
1022#endif
1023
5229395c 1024 if (serverConnection()->getPeer() != NULL) {
95dc7ff4 1025 ++ serverConnection()->getPeer()->stats.fetches;
5229395c
AJ
1026 request->peer_login = serverConnection()->getPeer()->login;
1027 request->peer_domain = serverConnection()->getPeer()->domain;
9825b398 1028 request->flags.auth_no_keytab = serverConnection()->getPeer()->options.auth_no_keytab;
b6b6f466 1029 httpStart(this);
41462d93 1030 } else {
2c065fc8 1031 assert(!request->flags.sslPeek);
b6b6f466 1032 request->peer_login = NULL;
1033 request->peer_domain = NULL;
9825b398 1034 request->flags.auth_no_keytab = 0;
62e76326 1035
4e3f4dc7 1036 switch (request->url.getScheme()) {
62e76326 1037
0c3d3f65 1038 case AnyP::PROTO_HTTPS:
b6b6f466 1039 httpStart(this);
62e76326 1040 break;
62e76326 1041
0c3d3f65 1042 case AnyP::PROTO_HTTP:
b6b6f466 1043 httpStart(this);
62e76326 1044 break;
1045
0c3d3f65 1046 case AnyP::PROTO_GOPHER:
b6b6f466 1047 gopherStart(this);
62e76326 1048 break;
1049
0c3d3f65 1050 case AnyP::PROTO_FTP:
92ae4c86 1051 if (request->flags.ftpNative)
5517260a 1052 Ftp::StartRelay(this);
434a79b0 1053 else
5517260a 1054 Ftp::StartGateway(this);
62e76326 1055 break;
1056
39a19cb7 1057 case AnyP::PROTO_CACHE_OBJECT:
62e76326 1058
0c3d3f65 1059 case AnyP::PROTO_URN:
62e76326 1060 fatal_dump("Should never get here");
1061 break;
1062
0c3d3f65 1063 case AnyP::PROTO_WHOIS:
b6b6f466 1064 whoisStart(this);
62e76326 1065 break;
1066
f53969cc 1067 case AnyP::PROTO_WAIS: /* Not implemented */
fc68f6b1 1068
62e76326 1069 default:
7be06178 1070 debugs(17, DBG_IMPORTANT, "WARNING: Cannot retrieve '" << entry->url() << "'.");
955394ce 1071 ErrorState *anErr = new ErrorState(ERR_UNSUP_REQ, Http::scBadRequest, request);
b6b6f466 1072 fail(anErr);
7be06178 1073 // Set the dont_retry flag because this is not a transient (network) error.
e857372a 1074 flags.dont_retry = true;
6b679a01 1075 if (Comm::IsConnOpen(serverConn)) {
00ae51e4 1076 serverConn->close();
746beefe 1077 }
62e76326 1078 break;
1079 }
41462d93 1080 }
1081}
1082
545782b8 1083/*
1084 * FwdState::reforward
1085 *
1086 * returns TRUE if the transaction SHOULD be re-forwarded to the
8bbb16e3 1087 * next choice in the serverDestinations list. This method is called when
d5430dc8 1088 * peer communication completes normally, or experiences
545782b8 1089 * some error after receiving the end of HTTP headers.
1090 */
b6b6f466 1091int
1092FwdState::reforward()
db1cd23c 1093{
b6b6f466 1094 StoreEntry *e = entry;
c4a88a3e
CT
1095
1096 if (EBIT_TEST(e->flags, ENTRY_ABORTED)) {
1097 debugs(17, 3, HERE << "entry aborted");
1098 return 0;
1099 }
1100
db1cd23c 1101 assert(e->store_status == STORE_PENDING);
1102 assert(e->mem_obj);
bc87dc25 1103#if URL_CHECKSUM_DEBUG
62e76326 1104
528b2c61 1105 e->mem_obj->checkUrlChecksum();
bc87dc25 1106#endif
62e76326 1107
cfd66529 1108 debugs(17, 3, HERE << e->url() << "?" );
62e76326 1109
d6eb18d6 1110 if (!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) {
cfd66529 1111 debugs(17, 3, HERE << "No, ENTRY_FWD_HDR_WAIT isn't set");
62e76326 1112 return 0;
d6eb18d6 1113 }
62e76326 1114
437823b4 1115 if (n_tries > Config.forward_max_tries)
62e76326 1116 return 0;
1117
58217e94 1118 if (request->bodyNibbled())
62e76326 1119 return 0;
1120
6043e368 1121 if (serverDestinations.size() <= 1 && !PeerSelectionInitiator::subscribed) {
8652f8e7 1122 // NP: <= 1 since total count includes the recently failed one.
cfd66529 1123 debugs(17, 3, HERE << "No alternative forwarding paths left");
62e76326 1124 return 0;
db1cd23c 1125 }
62e76326 1126
9b769c67 1127 const Http::StatusCode s = e->getReply()->sline.status();
cfd66529 1128 debugs(17, 3, HERE << "status " << s);
b6b6f466 1129 return reforwardableStatus(s);
db1cd23c 1130}
1131
2ac4f6b5
AR
1132/**
1133 * Create "503 Service Unavailable" or "504 Gateway Timeout" error depending
1134 * on whether this is a validation request. RFC 2616 says that we MUST reply
1135 * with "504 Gateway Timeout" if validation fails and cached reply has
1136 * proxy-revalidate, must-revalidate or s-maxage Cache-Control directive.
1137 */
1138ErrorState *
1139FwdState::makeConnectingError(const err_type type) const
1140{
450fe1cb 1141 return new ErrorState(type, request->flags.needValidation ?
f11c8e2f 1142 Http::scGatewayTimeout : Http::scServiceUnavailable, request);
2ac4f6b5
AR
1143}
1144
b6b6f466 1145static void
1146fwdStats(StoreEntry * s)
64d8034e 1147{
b6b6f466 1148 int i;
1149 int j;
1150 storeAppendPrintf(s, "Status");
62e76326 1151
95dc7ff4
FC
1152 for (j = 1; j < MAX_FWD_STATS_IDX; ++j) {
1153 storeAppendPrintf(s, "\ttry#%d", j);
64d8034e 1154 }
64d8034e 1155
b6b6f466 1156 storeAppendPrintf(s, "\n");
0185bd6f 1157
955394ce 1158 for (i = 0; i <= (int) Http::scInvalidHeader; ++i) {
b6b6f466 1159 if (FwdReplyCodes[0][i] == 0)
1160 continue;
0185bd6f 1161
b6b6f466 1162 storeAppendPrintf(s, "%3d", i);
0185bd6f 1163
95dc7ff4 1164 for (j = 0; j <= MAX_FWD_STATS_IDX; ++j) {
b6b6f466 1165 storeAppendPrintf(s, "\t%d", FwdReplyCodes[j][i]);
62e76326 1166 }
62e76326 1167
b6b6f466 1168 storeAppendPrintf(s, "\n");
e0ebe27c 1169 }
7197b20d 1170}
1171
b6b6f466 1172/**** STATIC MEMBER FUNCTIONS *************************************************/
db1cd23c 1173
b6b6f466 1174bool
955394ce 1175FwdState::reforwardableStatus(const Http::StatusCode s) const
db1cd23c 1176{
b6b6f466 1177 switch (s) {
62e76326 1178
955394ce 1179 case Http::scBadGateway:
62e76326 1180
f11c8e2f 1181 case Http::scGatewayTimeout:
b6b6f466 1182 return true;
62e76326 1183
955394ce 1184 case Http::scForbidden:
62e76326 1185
955394ce 1186 case Http::scInternalServerError:
62e76326 1187
955394ce 1188 case Http::scNotImplemented:
62e76326 1189
955394ce 1190 case Http::scServiceUnavailable:
b6b6f466 1191 return Config.retry.onerror;
62e76326 1192
b6b6f466 1193 default:
1194 return false;
db1cd23c 1195 }
b6b6f466 1196
1197 /* NOTREACHED */
db1cd23c 1198}
8ddcc35d 1199
06093389
AJ
1200/**
1201 * Decide where details need to be gathered to correctly describe a persistent connection.
1202 * What is needed:
642a305c
AJ
1203 * - the address/port details about this link
1204 * - domain name of server at other end of this link (either peer or requested host)
06093389 1205 */
781ce8ff 1206void
642a305c 1207FwdState::pconnPush(Comm::ConnectionPointer &conn, const char *domain)
781ce8ff 1208{
642a305c 1209 if (conn->getPeer()) {
7a150c71 1210 fwdPconnPool->push(conn, NULL);
06093389 1211 } else {
642a305c 1212 fwdPconnPool->push(conn, domain);
06093389 1213 }
781ce8ff 1214}
1215
e8dca475
CT
1216Comm::ConnectionPointer
1217FwdState::pconnPop(const Comm::ConnectionPointer &dest, const char *domain)
1218{
afc753f3
EB
1219 bool retriable = checkRetriable();
1220 if (!retriable && Config.accessList.serverPconnForNonretriable) {
1221 ACLFilledChecklist ch(Config.accessList.serverPconnForNonretriable, request, NULL);
06bf5384 1222 retriable = ch.fastCheck().allowed();
afc753f3 1223 }
e8dca475
CT
1224 // always call shared pool first because we need to close an idle
1225 // connection there if we have to use a standby connection.
afc753f3 1226 Comm::ConnectionPointer conn = fwdPconnPool->pop(dest, domain, retriable);
e8dca475
CT
1227 if (!Comm::IsConnOpen(conn)) {
1228 // either there was no pconn to pop or this is not a retriable xaction
1229 if (CachePeer *peer = dest->getPeer()) {
1230 if (peer->standby.pool)
1231 conn = peer->standby.pool->pop(dest, domain, true);
1232 }
1233 }
1234 return conn; // open, closed, or nil
1235}
1236
8ddcc35d 1237void
b6b6f466 1238FwdState::initModule()
8ddcc35d 1239{
6852be71 1240 RegisterWithCacheManager();
8ddcc35d 1241}
1242
62ee09ca 1243void
84f50787 1244FwdState::RegisterWithCacheManager(void)
62ee09ca 1245{
8822ebee 1246 Mgr::RegisterAction("forward", "Request Forwarding Statistics", fwdStats, 0, 1);
62ee09ca 1247}
1248
b6b6f466 1249void
955394ce 1250FwdState::logReplyStatus(int tries, const Http::StatusCode status)
8ddcc35d 1251{
955394ce 1252 if (status > Http::scInvalidHeader)
62e76326 1253 return;
1254
75eb730e 1255 assert(tries >= 0);
62e76326 1256
8ddcc35d 1257 if (tries > MAX_FWD_STATS_IDX)
62e76326 1258 tries = MAX_FWD_STATS_IDX;
1259
95dc7ff4 1260 ++ FwdReplyCodes[tries][status];
8ddcc35d 1261}
1262
b6b6f466 1263/**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
62e76326 1264
057f5854 1265/*
1266 * DPW 2007-05-19
1267 * Formerly static, but now used by client_side_request.cc
1268 */
425de4c8
AJ
1269/// Checks for a TOS value to apply depending on the ACL
1270tos_t
b6b6f466 1271aclMapTOS(acl_tos * head, ACLChecklist * ch)
b6a2f15e 1272{
60019fea 1273 for (acl_tos *l = head; l; l = l->next) {
06bf5384 1274 if (!l->aclList || ch->fastCheck(l->aclList).allowed())
b6b6f466 1275 return l->tos;
1276 }
5894ad28 1277
b6b6f466 1278 return 0;
1279}
5894ad28 1280
425de4c8 1281/// Checks for a netfilter mark value to apply depending on the ACL
244da4ad
AG
1282Ip::NfMarkConfig
1283aclFindNfMarkConfig(acl_nfmark * head, ACLChecklist * ch)
425de4c8 1284{
60019fea 1285 for (acl_nfmark *l = head; l; l = l->next) {
06bf5384 1286 if (!l->aclList || ch->fastCheck(l->aclList).allowed())
244da4ad 1287 return l->markConfig;
425de4c8
AJ
1288 }
1289
244da4ad 1290 return {};
425de4c8
AJ
1291}
1292
cfd66529 1293void
f9b72e0c 1294getOutgoingAddress(HttpRequest * request, Comm::ConnectionPointer conn)
b6b6f466 1295{
07f889c1 1296 // skip if an outgoing address is already set.
4dd643d5 1297 if (!conn->local.isAnyAddr()) return;
cfd66529 1298
07f889c1 1299 // ensure that at minimum the wildcard local matches remote protocol
4dd643d5
AJ
1300 if (conn->remote.isIPv4())
1301 conn->local.setIPv4();
07f889c1 1302
cfd66529 1303 // maybe use TPROXY client address
450fe1cb 1304 if (request && request->flags.spoofClientIp) {
739b352a 1305 if (!conn->getPeer() || !conn->getPeer()->options.no_tproxy) {
96d64448
AJ
1306#if FOLLOW_X_FORWARDED_FOR && LINUX_NETFILTER
1307 if (Config.onoff.tproxy_uses_indirect_client)
45906573 1308 conn->local = request->indirect_client_addr;
96d64448
AJ
1309 else
1310#endif
45906573 1311 conn->local = request->client_addr;
3b659606 1312 conn->local.port(0); // let OS pick the source port to prevent address clashes
cfd66529
AJ
1313 // some flags need setting on the socket to use this address
1314 conn->flags |= COMM_DOBIND;
1315 conn->flags |= COMM_TRANSPARENT;
1316 return;
1317 }
b0758e04
AJ
1318 // else no tproxy today ...
1319 }
c303f6e3 1320
b50e327b 1321 if (!Config.accessList.outgoing_address) {
cfd66529 1322 return; // anything will do.
b50e327b
AJ
1323 }
1324
c0941a6a 1325 ACLFilledChecklist ch(NULL, request, NULL);
1b091aec 1326 ch.dst_peer_name = conn->getPeer() ? conn->getPeer()->name : NULL;
cfd66529
AJ
1327 ch.dst_addr = conn->remote;
1328
1329 // TODO use the connection details in ACL.
1330 // needs a bit of rework in ACLFilledChecklist to use Comm::Connection instead of ConnStateData
6db78a1a 1331
289848ca 1332 for (Acl::Address *l = Config.accessList.outgoing_address; l; l = l->next) {
cfd66529
AJ
1333
1334 /* check if the outgoing address is usable to the destination */
4dd643d5 1335 if (conn->remote.isIPv4() != l->addr.isIPv4()) continue;
cfd66529
AJ
1336
1337 /* check ACLs for this outgoing address */
06bf5384 1338 if (!l->aclList || ch.fastCheck(l->aclList).allowed()) {
cfd66529
AJ
1339 conn->local = l->addr;
1340 return;
1341 }
1342 }
b6b6f466 1343}
62e76326 1344
425de4c8
AJ
1345tos_t
1346GetTosToServer(HttpRequest * request)
1347{
1348 ACLFilledChecklist ch(NULL, request, NULL);
425de4c8
AJ
1349 return aclMapTOS(Ip::Qos::TheConfig.tosToServer, &ch);
1350}
1351
1352nfmark_t
1353GetNfmarkToServer(HttpRequest * request)
b6b6f466 1354{
c0941a6a 1355 ACLFilledChecklist ch(NULL, request, NULL);
244da4ad
AG
1356 const auto mc = aclFindNfMarkConfig(Ip::Qos::TheConfig.nfmarkToServer, &ch);
1357 return mc.mark;
b6a2f15e 1358}
6ee88490
CT
1359
1360void
1361GetMarkingsToServer(HttpRequest * request, Comm::Connection &conn)
1362{
1363 // Get the server side TOS and Netfilter mark to be set on the connection.
1364 if (Ip::Qos::TheConfig.isAclTosActive()) {
1365 conn.tos = GetTosToServer(request);
1366 debugs(17, 3, "from " << conn.local << " tos " << int(conn.tos));
1367 }
1368
1369#if SO_MARK && USE_LIBCAP
1370 conn.nfmark = GetNfmarkToServer(request);
1371 debugs(17, 3, "from " << conn.local << " netfilter mark " << conn.nfmark);
1372#else
1373 conn.nfmark = 0;
1374#endif
1375}
f53969cc 1376