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