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