]> git.ipfire.org Git - thirdparty/squid.git/blame - src/FwdState.cc
Add flexible RFC 3986 URI encoder (#617)
[thirdparty/squid.git] / src / FwdState.cc
CommitLineData
41462d93 1/*
77b1029d 2 * Copyright (C) 1996-2020 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{
66d51f4f
AR
514 const auto replyStatus = entry->mem().baseReply().sline.status();
515 debugs(17, 3, *entry << " status " << replyStatus << ' ' << entry->url());
b6b6f466 516#if URL_CHECKSUM_DEBUG
517
518 entry->mem_obj->checkUrlChecksum();
519#endif
520
66d51f4f 521 logReplyStatus(n_tries, replyStatus);
b6b6f466 522
523 if (reforward()) {
66d51f4f 524 debugs(17, 3, "re-forwarding " << replyStatus << " " << entry->url());
b6b6f466 525
6b679a01 526 if (Comm::IsConnOpen(serverConn))
00ae51e4 527 unregister(serverConn);
b6b6f466 528
cfd66529
AJ
529 entry->reset();
530
3dde9e52 531 useDestinations();
8652f8e7 532
b6b6f466 533 } else {
6b679a01 534 if (Comm::IsConnOpen(serverConn))
66d51f4f 535 debugs(17, 3, "server FD " << serverConnection()->fd << " not re-forwarding status " << replyStatus);
6b679a01 536 else
66d51f4f 537 debugs(17, 3, "server (FD closed) not re-forwarding status " << replyStatus);
9e5c22cf 538 entry->complete();
fc68f6b1 539
6b679a01 540 if (!Comm::IsConnOpen(serverConn))
fc68f6b1 541 completed();
542
6043e368 543 stopAndDestroy("forwarding completed");
b6b6f466 544 }
545}
546
6043e368
AR
547void
548FwdState::noteDestination(Comm::ConnectionPointer path)
549{
55622953
CT
550 flags.destinationsFound = true;
551
552 if (!path) {
553 // We can call usePinned() without fear of clashing with an earlier
554 // forwarding attempt because PINNED must be the first destination.
555 assert(destinations->empty());
556 usePinned();
557 return;
558 }
559
560 debugs(17, 3, path);
561
55622953
CT
562 destinations->addPath(path);
563
564 if (Comm::IsConnOpen(serverConn)) {
565 // We are already using a previously opened connection, so we cannot be
566 // waiting for connOpener. We still receive destinations for backup.
567 Must(!opening());
568 return;
569 }
570
571 if (opening()) {
572 notifyConnOpener();
573 return; // and continue to wait for FwdState::noteConnection() callback
574 }
575
576 // This is the first path candidate we have seen. Create connOpener.
577 useDestinations();
6043e368 578}
b6b6f466 579
6043e368
AR
580void
581FwdState::noteDestinationsEnd(ErrorState *selectionError)
b6b6f466 582{
6043e368 583 PeerSelectionInitiator::subscribed = false;
55622953 584 destinations->destinationsFinalized = true;
6043e368 585
55622953 586 if (!flags.destinationsFound) {
6043e368
AR
587 if (selectionError) {
588 debugs(17, 3, "Will abort forwarding because path selection has failed.");
589 Must(!err); // if we tried to connect, then path selection succeeded
590 fail(selectionError);
591 }
592 else if (err)
593 debugs(17, 3, "Will abort forwarding because all found paths have failed.");
594 else
595 debugs(17, 3, "Will abort forwarding because path selection found no paths.");
596
3dde9e52 597 useDestinations(); // will detect and handle the lack of paths
6043e368
AR
598 return;
599 }
600 // else continue to use one of the previously noted destinations;
601 // if all of them fail, forwarding as whole will fail
602 Must(!selectionError); // finding at least one path means selection succeeded
55622953
CT
603
604 if (Comm::IsConnOpen(serverConn)) {
605 // We are already using a previously opened connection, so we cannot be
606 // waiting for connOpener. We were receiving destinations for backup.
607 Must(!opening());
608 return;
609 }
610
611 Must(opening()); // or we would be stuck with nothing to do or wait for
612 notifyConnOpener();
613 // and continue to wait for FwdState::noteConnection() callback
614}
615
616/// makes sure connOpener knows that destinations have changed
617void
618FwdState::notifyConnOpener()
619{
620 if (destinations->notificationPending) {
621 debugs(17, 7, "reusing pending notification about " << *destinations);
622 } else {
623 debugs(17, 7, "notifying about " << *destinations);
624 destinations->notificationPending = true;
625 CallJobHere(17, 5, connOpener, HappyConnOpener, noteCandidatesChange);
626 }
b6b6f466 627}
628
6043e368
AR
629/**** CALLBACK WRAPPERS ************************************************************/
630
b6b6f466 631static void
575d05c4 632fwdServerClosedWrapper(const CommCloseCbParams &params)
b6b6f466 633{
575d05c4
AJ
634 FwdState *fwd = (FwdState *)params.data;
635 fwd->serverClosed(params.fd);
b6b6f466 636}
637
b6b6f466 638/**** PRIVATE *****************************************************************/
639
545782b8 640/*
641 * FwdState::checkRetry
26ac0430 642 *
545782b8 643 * Return TRUE if the request SHOULD be retried. This method is
644 * called when the HTTP connection fails, or when the connection
d5430dc8 645 * is closed before reading the end of HTTP headers from the server.
545782b8 646 */
b6b6f466 647bool
648FwdState::checkRetry()
68bd6892 649{
d8fd0f18 650 if (shutting_down)
b6b6f466 651 return false;
62e76326 652
9d2760b6
AR
653 if (!self) { // we have aborted before the server called us back
654 debugs(17, 5, HERE << "not retrying because of earlier abort");
655 // we will be destroyed when the server clears its Pointer to us
656 return false;
657 }
658
b6b6f466 659 if (entry->store_status != STORE_PENDING)
660 return false;
62e76326 661
b6b6f466 662 if (!entry->isEmpty())
663 return false;
62e76326 664
3eebd267 665 if (exhaustedTries())
b6b6f466 666 return false;
4ed0e075 667
3dde9e52
CT
668 if (request->flags.pinned && !pinnedCanRetry())
669 return false;
670
0ce8e93b 671 if (!EnoughTimeToReForward(start_t))
b6b6f466 672 return false;
62e76326 673
b6b6f466 674 if (flags.dont_retry)
675 return false;
62e76326 676
3b9899f7
AR
677 if (request->bodyNibbled())
678 return false;
679
0bbd5532
AJ
680 // NP: not yet actually connected anywhere. retry is safe.
681 if (!flags.connected_okay)
682 return true;
683
5d4989a8 684 if (!checkRetriable())
685 return false;
686
b6b6f466 687 return true;
68bd6892 688}
689
afc753f3 690/// Whether we may try sending this request again after a failure.
b6b6f466 691bool
692FwdState::checkRetriable()
cb928909 693{
ccbcff0e
AR
694 // Optimize: A compliant proxy may retry PUTs, but Squid lacks the [rather
695 // complicated] code required to protect the PUT request body from being
696 // nibbled during the first try. Thus, Squid cannot retry some PUTs today.
697 if (request->body_pipe != NULL)
698 return false;
699
c2a7cefd
AJ
700 // RFC2616 9.1 Safe and Idempotent Methods
701 return (request->method.isHttpSafe() || request->method.isIdempotent());
cb928909 702}
703
b6b6f466 704void
705FwdState::serverClosed(int fd)
910169e5 706{
8ed21336 707 // XXX: fd is often -1 here
e8dca475 708 debugs(17, 2, "FD " << fd << " " << entry->url() << " after " <<
8ed21336
AR
709 (fd >= 0 ? fd_table[fd].pconn.uses : -1) << " requests");
710 if (fd >= 0 && serverConnection()->fd == fd)
e8dca475 711 fwdPconnPool->noteUses(fd_table[fd].pconn.uses);
3e8c047e 712 retryOrBail();
713}
714
715void
26ac0430
AJ
716FwdState::retryOrBail()
717{
b6b6f466 718 if (checkRetry()) {
cfd66529 719 debugs(17, 3, HERE << "re-forwarding (" << n_tries << " tries, " << (squid_curtime - start_t) << " secs)");
3dde9e52 720 useDestinations();
8652f8e7 721 return;
d8fd0f18 722 }
62e76326 723
9d2760b6
AR
724 // TODO: should we call completed() here and move doneWithRetries there?
725 doneWithRetries();
726
16b70e2a
CT
727 request->hier.stopPeerClock(false);
728
8f01bdfb 729 if (self != NULL && !err && shutting_down && entry->isEmpty()) {
7e6eabbc 730 const auto anErr = new ErrorState(ERR_SHUTTING_DOWN, Http::scServiceUnavailable, request, al);
f01d4b80 731 errorAppendEntry(entry, anErr);
f563eea9 732 }
62e76326 733
6043e368 734 stopAndDestroy("cannot retry");
910169e5 735}
736
9d2760b6
AR
737// If the Server quits before nibbling at the request body, the body sender
738// will not know (so that we can retry). Call this if we will not retry. We
739// will notify the sender so that it does not get stuck waiting for space.
740void
741FwdState::doneWithRetries()
742{
743 if (request && request->body_pipe != NULL)
744 request->body_pipe->expectNoConsumption();
745}
746
3e8c047e 747// called by the server that failed after calling unregister()
748void
749FwdState::handleUnregisteredServerEnd()
750{
cfd66529 751 debugs(17, 2, HERE << "self=" << self << " err=" << err << ' ' << entry->url());
6b679a01 752 assert(!Comm::IsConnOpen(serverConn));
3e8c047e 753 retryOrBail();
754}
755
55622953
CT
756/// called when a to-peer connection has been successfully obtained or
757/// when all candidate destinations have been tried and all have failed
b6b6f466 758void
55622953 759FwdState::noteConnection(HappyConnOpener::Answer &answer)
41462d93 760{
55622953
CT
761 calls.connector = nullptr;
762 connOpener.clear();
62e76326 763
55622953
CT
764 Must(n_tries <= answer.n_tries); // n_tries cannot decrease
765 n_tries = answer.n_tries;
62e76326 766
55622953
CT
767 if (const auto error = answer.error.get()) {
768 flags.dont_retry = true; // or HappyConnOpener would not have given up
769 syncHierNote(answer.conn, request->url.host());
770 fail(error);
771 answer.error.clear(); // preserve error for errorSendComplete()
772 retryOrBail(); // will notice flags.dont_retry and bail
2f538b78
AJ
773 return;
774 }
62e76326 775
55622953 776 syncWithServerConn(answer.conn, request->url.host(), answer.reused);
2f538b78 777
55622953
CT
778 if (answer.reused)
779 return dispatch();
3dde9e52 780
55622953 781 // Check if we need to TLS before use
f5e17947
CT
782 if (const CachePeer *peer = serverConnection()->getPeer()) {
783 // Assume that it is only possible for the client-first from the
784 // bumping modes to try connect to a remote server. The bumped
785 // requests with other modes are using pinned connections or fails.
786 const bool clientFirstBump = request->flags.sslBumped;
787 // We need a CONNECT tunnel to send encrypted traffic through a proxy,
788 // but we do not support TLS inside TLS, so we exclude HTTPS proxies.
789 const bool originWantsEncryptedTraffic =
790 request->method == Http::METHOD_CONNECT ||
791 request->flags.sslPeek ||
792 clientFirstBump;
793 if (originWantsEncryptedTraffic && // the "encrypted traffic" part
794 !peer->options.originserver && // the "through a proxy" part
795 !peer->secure.encryptTransport) // the "exclude HTTPS proxies" part
796 return establishTunnelThruProxy();
797 }
798
799 secureConnectionToPeerIfNeeded();
800}
801
802void
803FwdState::establishTunnelThruProxy()
804{
805 AsyncCall::Pointer callback = asyncCall(17,4,
806 "FwdState::tunnelEstablishmentDone",
807 Http::Tunneler::CbDialer<FwdState>(&FwdState::tunnelEstablishmentDone, this));
808 HttpRequest::Pointer requestPointer = request;
809 const auto tunneler = new Http::Tunneler(serverConnection(), requestPointer, callback, connectingTimeout(serverConnection()), al);
810#if USE_DELAY_POOLS
811 Must(serverConnection()->getPeer());
812 if (!serverConnection()->getPeer()->options.no_delay)
813 tunneler->setDelayId(entry->mem_obj->mostBytesAllowed());
814#endif
815 AsyncJob::Start(tunneler);
816 // and wait for the tunnelEstablishmentDone() call
817}
818
819/// resumes operations after the (possibly failed) HTTP CONNECT exchange
820void
821FwdState::tunnelEstablishmentDone(Http::TunnelerAnswer &answer)
822{
823 if (answer.positive()) {
824 if (answer.leftovers.isEmpty()) {
825 secureConnectionToPeerIfNeeded();
826 return;
827 }
828 // This should not happen because TLS servers do not speak first. If we
829 // have to handle this, then pass answer.leftovers via a PeerConnector
830 // to ServerBio. See ClientBio::setReadBufData().
831 static int occurrences = 0;
832 const auto level = (occurrences++ < 100) ? DBG_IMPORTANT : 2;
833 debugs(17, level, "ERROR: Early data after CONNECT response. " <<
834 "Found " << answer.leftovers.length() << " bytes. " <<
835 "Closing " << serverConnection());
836 fail(new ErrorState(ERR_CONNECT_FAIL, Http::scBadGateway, request, al));
837 closeServerConnection("found early data after CONNECT response");
838 retryOrBail();
839 return;
840 }
841
842 // TODO: Reuse to-peer connections after a CONNECT error response.
843
844 if (const auto peer = serverConnection()->getPeer())
845 peerConnectFailed(peer);
846
847 const auto error = answer.squidError.get();
848 Must(error);
849 answer.squidError.clear(); // preserve error for fail()
850 fail(error);
851 closeServerConnection("Squid-generated CONNECT error");
852 retryOrBail();
853}
854
855/// handles an established TCP connection to peer (including origin servers)
856void
857FwdState::secureConnectionToPeerIfNeeded()
858{
859 assert(!request->flags.pinned);
860
3dde9e52
CT
861 const CachePeer *p = serverConnection()->getPeer();
862 const bool peerWantsTls = p && p->secure.encryptTransport;
863 // userWillTlsToPeerForUs assumes CONNECT == HTTPS
864 const bool userWillTlsToPeerForUs = p && p->options.originserver &&
865 request->method == Http::METHOD_CONNECT;
866 const bool needTlsToPeer = peerWantsTls && !userWillTlsToPeerForUs;
f5e17947
CT
867 const bool clientFirstBump = request->flags.sslBumped; // client-first (already) bumped connection
868 const bool needsBump = request->flags.sslPeek || clientFirstBump;
869
870 // 'GET https://...' requests. If a peer is used the request is forwarded
871 // as is
872 const bool needTlsToOrigin = !p && request->url.getScheme() == AnyP::PROTO_HTTPS && !clientFirstBump;
873
874 if (needTlsToPeer || needTlsToOrigin || needsBump) {
3dde9e52
CT
875 HttpRequest::Pointer requestPointer = request;
876 AsyncCall::Pointer callback = asyncCall(17,4,
877 "FwdState::ConnectedToPeer",
878 FwdStatePeerAnswerDialer(&FwdState::connectedToPeer, this));
55622953 879 const auto sslNegotiationTimeout = connectingTimeout(serverConnection());
3dde9e52 880 Security::PeerConnector *connector = nullptr;
a72b6e88 881#if USE_OPENSSL
3dde9e52
CT
882 if (request->flags.sslPeek)
883 connector = new Ssl::PeekingPeerConnector(requestPointer, serverConnection(), clientConn, callback, al, sslNegotiationTimeout);
884 else
a72b6e88 885#endif
3dde9e52
CT
886 connector = new Security::BlindPeerConnector(requestPointer, serverConnection(), callback, al, sslNegotiationTimeout);
887 AsyncJob::Start(connector); // will call our callback
888 return;
41462d93 889 }
2f538b78 890
fcfdf7f9 891 // if not encrypting just run the post-connect actions
f5e17947 892 successfullyConnectedToPeer();
41462d93 893}
894
f5e17947 895/// called when all negotiations with the TLS-speaking peer have been completed
a23223bf 896void
fcfdf7f9 897FwdState::connectedToPeer(Security::EncryptorAnswer &answer)
a23223bf
CT
898{
899 if (ErrorState *error = answer.error.get()) {
900 fail(error);
901 answer.error.clear(); // preserve error for errorSendComplete()
89d7efa5
CT
902 if (CachePeer *p = serverConnection()->getPeer())
903 peerConnectFailed(p);
4e646287 904 serverConnection()->close();
a23223bf
CT
905 return;
906 }
907
56753478
CT
908 if (answer.tunneled) {
909 // TODO: When ConnStateData establishes tunnels, its state changes
910 // [in ways that may affect logging?]. Consider informing
911 // ConnStateData about our tunnel or otherwise unifying tunnel
912 // establishment [side effects].
913 unregister(serverConn); // async call owns it now
914 complete(); // destroys us
915 return;
916 }
917
f5e17947
CT
918 successfullyConnectedToPeer();
919}
920
921/// called when all negotiations with the peer have been completed
922void
923FwdState::successfullyConnectedToPeer()
924{
fcfdf7f9
AJ
925 // should reach ConnStateData before the dispatched Client job starts
926 CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
927 ConnStateData::notePeerConnection, serverConnection());
928
89d7efa5
CT
929 if (serverConnection()->getPeer())
930 peerConnectSucceded(serverConnection()->getPeer());
931
a23223bf
CT
932 dispatch();
933}
a23223bf 934
55622953 935/// commits to using the given open to-peer connection
b6b6f466 936void
55622953 937FwdState::syncWithServerConn(const Comm::ConnectionPointer &conn, const char *host, const bool reused)
41462d93 938{
55622953
CT
939 Must(IsConnOpen(conn));
940 serverConn = conn;
62e76326 941
55622953 942 closeHandler = comm_add_close_handler(serverConn->fd, fwdServerClosedWrapper, this);
62e76326 943
55622953
CT
944 if (reused) {
945 pconnRace = racePossible;
946 ResetMarkingsToServer(request, *serverConn);
947 } else {
948 pconnRace = raceImpossible;
949 // Comm::ConnOpener already applied proper/current markings
746beefe 950 }
4b77ea6b 951
7d1dac79
EB
952 syncHierNote(serverConn, host);
953}
954
955void
956FwdState::syncHierNote(const Comm::ConnectionPointer &server, const char *host)
957{
1ce66e29 958 if (request)
d8165775 959 request->hier.resetPeerNotes(server, host);
1ce66e29 960 if (al)
d8165775 961 al->hier.resetPeerNotes(server, host);
4b77ea6b
AR
962}
963
8aec3e1b
CT
964/**
965 * Called after forwarding path selection (via peer select) has taken place
966 * and whenever forwarding needs to attempt a new connection (routing failover).
967 * We have a vector of possible localIP->remoteIP paths now ready to start being connected.
968 */
969void
970FwdState::connectStart()
971{
55622953 972 debugs(17, 3, *destinations << " to " << entry->url());
8aec3e1b 973
daf80700
CT
974 Must(!request->pinnedConnection());
975
55622953
CT
976 assert(!destinations->empty());
977 assert(!opening());
8aec3e1b 978
55622953
CT
979 // Ditch error page if it was created before.
980 // A new one will be created if there's another problem
3dde9e52
CT
981 delete err;
982 err = nullptr;
983 request->clearError();
55622953 984 serverConn = nullptr;
3dde9e52 985
16b70e2a 986 request->hier.startPeerClock();
777831e0 987
55622953 988 calls.connector = asyncCall(17, 5, "FwdState::noteConnection", HappyConnOpener::CbDialer<FwdState>(&FwdState::noteConnection, this));
bc81ee68 989
55622953
CT
990 HttpRequest::Pointer cause = request;
991 const auto cs = new HappyConnOpener(destinations, calls.connector, cause, start_t, n_tries, al);
992 cs->setHost(request->url.host());
993 bool retriable = checkRetriable();
994 if (!retriable && Config.accessList.serverPconnForNonretriable) {
995 ACLFilledChecklist ch(Config.accessList.serverPconnForNonretriable, request, nullptr);
996 ch.al = al;
997 ch.syncAle(request, nullptr);
998 retriable = ch.fastCheck().allowed();
41462d93 999 }
55622953
CT
1000 cs->setRetriable(retriable);
1001 cs->allowPersistent(pconnRace != raceHappened);
1002 destinations->notificationPending = true; // start() is async
1003 connOpener = cs;
855150a4 1004 AsyncJob::Start(cs);
41462d93 1005}
1006
3dde9e52
CT
1007/// send request on an existing connection dedicated to the requesting client
1008void
1009FwdState::usePinned()
1010{
3dde9e52
CT
1011 const auto connManager = request->pinnedConnection();
1012 debugs(17, 7, "connection manager: " << connManager);
1013
daf80700
CT
1014 try {
1015 serverConn = ConnStateData::BorrowPinnedConnection(request, al);
1016 debugs(17, 5, "connection: " << serverConn);
1017 } catch (ErrorState * const anErr) {
1018 syncHierNote(nullptr, connManager ? connManager->pinning.host : request->url.host());
3dde9e52 1019 serverConn = nullptr;
3dde9e52
CT
1020 fail(anErr);
1021 // Connection managers monitor their idle pinned to-server
1022 // connections and close from-client connections upon seeing
1023 // a to-server connection closure. Retrying here is futile.
1024 stopAndDestroy("pinned connection failure");
1025 return;
1026 }
1027
3dde9e52
CT
1028 ++n_tries;
1029 request->flags.pinned = true;
1030
e7d3e24e 1031 assert(connManager);
3dde9e52
CT
1032 if (connManager->pinnedAuth())
1033 request->flags.auth = true;
1034
3dde9e52 1035 // the server may close the pinned connection before this request
55622953 1036 const auto reused = true;
daf80700 1037 syncWithServerConn(serverConn, connManager->pinning.host, reused);
55622953 1038
3dde9e52
CT
1039 dispatch();
1040}
1041
b6b6f466 1042void
1043FwdState::dispatch()
41462d93 1044{
7f06a3d8 1045 debugs(17, 3, clientConn << ": Fetching " << request->method << ' ' << entry->url());
e0ebe27c 1046 /*
1047 * Assert that server_fd is set. This is to guarantee that fwdState
1048 * is attached to something and will be deallocated when server_fd
1049 * is closed.
1050 */
6b679a01 1051 assert(Comm::IsConnOpen(serverConn));
62e76326 1052
5229395c 1053 fd_note(serverConnection()->fd, entry->url());
62e76326 1054
e8dca475 1055 fd_table[serverConnection()->fd].noteUse();
62e76326 1056
a7ad6e4e 1057 /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
1058 assert(entry->ping_status != PING_WAITING);
62e76326 1059
1bfe9ade 1060 assert(entry->locked());
62e76326 1061
a7ad6e4e 1062 EBIT_SET(entry->flags, ENTRY_DISPATCHED);
62e76326 1063
55622953
CT
1064 flags.connected_okay = true;
1065
5c51bffb 1066 netdbPingSite(request->url.host());
62e76326 1067
425de4c8 1068 /* Retrieves remote server TOS or MARK value, and stores it as part of the
7172612f 1069 * original client request FD object. It is later used to forward
425de4c8 1070 * remote server's TOS/MARK in the response to the client in case of a MISS.
7172612f 1071 */
425de4c8 1072 if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
b5523edc
AJ
1073 if (Comm::IsConnOpen(clientConn) && Comm::IsConnOpen(serverConnection())) {
1074 fde * clientFde = &fd_table[clientConn->fd]; // XXX: move the fd_table access into Ip::Qos
653d9927 1075 /* Get the netfilter CONNMARK */
244da4ad 1076 clientFde->nfConnmarkFromServer = Ip::Qos::getNfConnmark(serverConnection(), Ip::Qos::dirOpened);
425de4c8
AJ
1077 }
1078 }
1079
1080#if _SQUID_LINUX_
1081 /* Bug 2537: The TOS forward part of QOS only applies to patched Linux kernels. */
1082 if (Ip::Qos::TheConfig.isHitTosActive()) {
b5523edc
AJ
1083 if (Comm::IsConnOpen(clientConn)) {
1084 fde * clientFde = &fd_table[clientConn->fd]; // XXX: move the fd_table access into Ip::Qos
425de4c8 1085 /* Get the TOS value for the packet */
b5523edc 1086 Ip::Qos::getTosFromServer(serverConnection(), clientFde);
7172612f 1087 }
26ac0430 1088 }
7172612f
AJ
1089#endif
1090
cb4f4424 1091#if USE_OPENSSL
2c065fc8 1092 if (request->flags.sslPeek) {
d7ce0bcd 1093 CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
801cfc26 1094 ConnStateData::httpsPeeked, ConnStateData::PinnedIdleContext(serverConnection(), request));
d7ce0bcd
AR
1095 unregister(serverConn); // async call owns it now
1096 complete(); // destroys us
1097 return;
061bbdec 1098 }
d7ce0bcd
AR
1099#endif
1100
f5e17947
CT
1101 if (const auto peer = serverConnection()->getPeer()) {
1102 ++peer->stats.fetches;
1103 request->prepForPeering(*peer);
b6b6f466 1104 httpStart(this);
41462d93 1105 } else {
2c065fc8 1106 assert(!request->flags.sslPeek);
f5e17947 1107 request->prepForDirect();
62e76326 1108
4e3f4dc7 1109 switch (request->url.getScheme()) {
62e76326 1110
0c3d3f65 1111 case AnyP::PROTO_HTTPS:
b6b6f466 1112 httpStart(this);
62e76326 1113 break;
62e76326 1114
0c3d3f65 1115 case AnyP::PROTO_HTTP:
b6b6f466 1116 httpStart(this);
62e76326 1117 break;
1118
0c3d3f65 1119 case AnyP::PROTO_GOPHER:
b6b6f466 1120 gopherStart(this);
62e76326 1121 break;
1122
0c3d3f65 1123 case AnyP::PROTO_FTP:
92ae4c86 1124 if (request->flags.ftpNative)
5517260a 1125 Ftp::StartRelay(this);
434a79b0 1126 else
5517260a 1127 Ftp::StartGateway(this);
62e76326 1128 break;
1129
39a19cb7 1130 case AnyP::PROTO_CACHE_OBJECT:
62e76326 1131
0c3d3f65 1132 case AnyP::PROTO_URN:
62e76326 1133 fatal_dump("Should never get here");
1134 break;
1135
0c3d3f65 1136 case AnyP::PROTO_WHOIS:
b6b6f466 1137 whoisStart(this);
62e76326 1138 break;
1139
f53969cc 1140 case AnyP::PROTO_WAIS: /* Not implemented */
fc68f6b1 1141
62e76326 1142 default:
7be06178 1143 debugs(17, DBG_IMPORTANT, "WARNING: Cannot retrieve '" << entry->url() << "'.");
7e6eabbc 1144 const auto anErr = new ErrorState(ERR_UNSUP_REQ, Http::scBadRequest, request, al);
b6b6f466 1145 fail(anErr);
7be06178 1146 // Set the dont_retry flag because this is not a transient (network) error.
e857372a 1147 flags.dont_retry = true;
6b679a01 1148 if (Comm::IsConnOpen(serverConn)) {
00ae51e4 1149 serverConn->close();
746beefe 1150 }
62e76326 1151 break;
1152 }
41462d93 1153 }
1154}
1155
545782b8 1156/*
1157 * FwdState::reforward
1158 *
1159 * returns TRUE if the transaction SHOULD be re-forwarded to the
8bbb16e3 1160 * next choice in the serverDestinations list. This method is called when
d5430dc8 1161 * peer communication completes normally, or experiences
545782b8 1162 * some error after receiving the end of HTTP headers.
1163 */
b6b6f466 1164int
1165FwdState::reforward()
db1cd23c 1166{
b6b6f466 1167 StoreEntry *e = entry;
c4a88a3e
CT
1168
1169 if (EBIT_TEST(e->flags, ENTRY_ABORTED)) {
1170 debugs(17, 3, HERE << "entry aborted");
1171 return 0;
1172 }
1173
db1cd23c 1174 assert(e->store_status == STORE_PENDING);
1175 assert(e->mem_obj);
bc87dc25 1176#if URL_CHECKSUM_DEBUG
62e76326 1177
528b2c61 1178 e->mem_obj->checkUrlChecksum();
bc87dc25 1179#endif
62e76326 1180
cfd66529 1181 debugs(17, 3, HERE << e->url() << "?" );
62e76326 1182
3dde9e52
CT
1183 if (request->flags.pinned && !pinnedCanRetry()) {
1184 debugs(17, 3, "pinned connection; cannot retry");
1185 return 0;
1186 }
1187
d6eb18d6 1188 if (!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) {
cfd66529 1189 debugs(17, 3, HERE << "No, ENTRY_FWD_HDR_WAIT isn't set");
62e76326 1190 return 0;
d6eb18d6 1191 }
62e76326 1192
3eebd267 1193 if (exhaustedTries())
62e76326 1194 return 0;
1195
58217e94 1196 if (request->bodyNibbled())
62e76326 1197 return 0;
1198
55622953 1199 if (destinations->empty() && !PeerSelectionInitiator::subscribed) {
cfd66529 1200 debugs(17, 3, HERE << "No alternative forwarding paths left");
62e76326 1201 return 0;
db1cd23c 1202 }
62e76326 1203
66d51f4f 1204 const auto s = entry->mem().baseReply().sline.status();
cfd66529 1205 debugs(17, 3, HERE << "status " << s);
b6b6f466 1206 return reforwardableStatus(s);
db1cd23c 1207}
1208
b6b6f466 1209static void
1210fwdStats(StoreEntry * s)
64d8034e 1211{
b6b6f466 1212 int i;
1213 int j;
1214 storeAppendPrintf(s, "Status");
62e76326 1215
95dc7ff4
FC
1216 for (j = 1; j < MAX_FWD_STATS_IDX; ++j) {
1217 storeAppendPrintf(s, "\ttry#%d", j);
64d8034e 1218 }
64d8034e 1219
b6b6f466 1220 storeAppendPrintf(s, "\n");
0185bd6f 1221
955394ce 1222 for (i = 0; i <= (int) Http::scInvalidHeader; ++i) {
b6b6f466 1223 if (FwdReplyCodes[0][i] == 0)
1224 continue;
0185bd6f 1225
b6b6f466 1226 storeAppendPrintf(s, "%3d", i);
0185bd6f 1227
95dc7ff4 1228 for (j = 0; j <= MAX_FWD_STATS_IDX; ++j) {
b6b6f466 1229 storeAppendPrintf(s, "\t%d", FwdReplyCodes[j][i]);
62e76326 1230 }
62e76326 1231
b6b6f466 1232 storeAppendPrintf(s, "\n");
e0ebe27c 1233 }
7197b20d 1234}
1235
b6b6f466 1236/**** STATIC MEMBER FUNCTIONS *************************************************/
db1cd23c 1237
b6b6f466 1238bool
955394ce 1239FwdState::reforwardableStatus(const Http::StatusCode s) const
db1cd23c 1240{
b6b6f466 1241 switch (s) {
62e76326 1242
955394ce 1243 case Http::scBadGateway:
62e76326 1244
f11c8e2f 1245 case Http::scGatewayTimeout:
b6b6f466 1246 return true;
62e76326 1247
955394ce 1248 case Http::scForbidden:
62e76326 1249
955394ce 1250 case Http::scInternalServerError:
62e76326 1251
955394ce 1252 case Http::scNotImplemented:
62e76326 1253
955394ce 1254 case Http::scServiceUnavailable:
b6b6f466 1255 return Config.retry.onerror;
62e76326 1256
b6b6f466 1257 default:
1258 return false;
db1cd23c 1259 }
b6b6f466 1260
1261 /* NOTREACHED */
db1cd23c 1262}
8ddcc35d 1263
1264void
b6b6f466 1265FwdState::initModule()
8ddcc35d 1266{
6852be71 1267 RegisterWithCacheManager();
8ddcc35d 1268}
1269
62ee09ca 1270void
84f50787 1271FwdState::RegisterWithCacheManager(void)
62ee09ca 1272{
8822ebee 1273 Mgr::RegisterAction("forward", "Request Forwarding Statistics", fwdStats, 0, 1);
62ee09ca 1274}
1275
b6b6f466 1276void
955394ce 1277FwdState::logReplyStatus(int tries, const Http::StatusCode status)
8ddcc35d 1278{
955394ce 1279 if (status > Http::scInvalidHeader)
62e76326 1280 return;
1281
75eb730e 1282 assert(tries >= 0);
62e76326 1283
8ddcc35d 1284 if (tries > MAX_FWD_STATS_IDX)
62e76326 1285 tries = MAX_FWD_STATS_IDX;
1286
95dc7ff4 1287 ++ FwdReplyCodes[tries][status];
8ddcc35d 1288}
1289
3eebd267
EB
1290bool
1291FwdState::exhaustedTries() const
1292{
1293 return n_tries >= Config.forward_max_tries;
1294}
1295
3dde9e52
CT
1296bool
1297FwdState::pinnedCanRetry() const
1298{
1299 assert(request->flags.pinned);
1300
1301 // pconn race on pinned connection: Currently we do not have any mechanism
1302 // to retry current pinned connection path.
1303 if (pconnRace == raceHappened)
1304 return false;
1305
1306 // If a bumped connection was pinned, then the TLS client was given our peer
1307 // details. Do not retry because we do not ensure that those details stay
1308 // constant. Step1-bumped connections do not get our TLS peer details, are
1309 // never pinned, and, hence, never reach this method.
1310 if (request->flags.sslBumped)
1311 return false;
1312
1313 // The other pinned cases are FTP proxying and connection-based HTTP
1314 // authentication. TODO: Do these cases have restrictions?
1315 return true;
1316}
1317
f5e17947
CT
1318time_t
1319FwdState::connectingTimeout(const Comm::ConnectionPointer &conn) const
1320{
1321 const auto connTimeout = conn->connectTimeout(start_t);
1322 return positiveTimeout(connTimeout);
1323}
1324
b6b6f466 1325/**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
62e76326 1326
057f5854 1327/*
1328 * DPW 2007-05-19
1329 * Formerly static, but now used by client_side_request.cc
1330 */
425de4c8
AJ
1331/// Checks for a TOS value to apply depending on the ACL
1332tos_t
b6b6f466 1333aclMapTOS(acl_tos * head, ACLChecklist * ch)
b6a2f15e 1334{
60019fea 1335 for (acl_tos *l = head; l; l = l->next) {
06bf5384 1336 if (!l->aclList || ch->fastCheck(l->aclList).allowed())
b6b6f466 1337 return l->tos;
1338 }
5894ad28 1339
b6b6f466 1340 return 0;
1341}
5894ad28 1342
425de4c8 1343/// Checks for a netfilter mark value to apply depending on the ACL
244da4ad
AG
1344Ip::NfMarkConfig
1345aclFindNfMarkConfig(acl_nfmark * head, ACLChecklist * ch)
425de4c8 1346{
60019fea 1347 for (acl_nfmark *l = head; l; l = l->next) {
06bf5384 1348 if (!l->aclList || ch->fastCheck(l->aclList).allowed())
244da4ad 1349 return l->markConfig;
425de4c8
AJ
1350 }
1351
244da4ad 1352 return {};
425de4c8
AJ
1353}
1354
cfd66529 1355void
7d74b4b8 1356getOutgoingAddress(HttpRequest * request, const Comm::ConnectionPointer &conn)
b6b6f466 1357{
07f889c1 1358 // skip if an outgoing address is already set.
4dd643d5 1359 if (!conn->local.isAnyAddr()) return;
cfd66529 1360
07f889c1 1361 // ensure that at minimum the wildcard local matches remote protocol
4dd643d5
AJ
1362 if (conn->remote.isIPv4())
1363 conn->local.setIPv4();
07f889c1 1364
cfd66529 1365 // maybe use TPROXY client address
450fe1cb 1366 if (request && request->flags.spoofClientIp) {
739b352a 1367 if (!conn->getPeer() || !conn->getPeer()->options.no_tproxy) {
96d64448
AJ
1368#if FOLLOW_X_FORWARDED_FOR && LINUX_NETFILTER
1369 if (Config.onoff.tproxy_uses_indirect_client)
45906573 1370 conn->local = request->indirect_client_addr;
96d64448
AJ
1371 else
1372#endif
45906573 1373 conn->local = request->client_addr;
3b659606 1374 conn->local.port(0); // let OS pick the source port to prevent address clashes
cfd66529
AJ
1375 // some flags need setting on the socket to use this address
1376 conn->flags |= COMM_DOBIND;
1377 conn->flags |= COMM_TRANSPARENT;
1378 return;
1379 }
b0758e04
AJ
1380 // else no tproxy today ...
1381 }
c303f6e3 1382
b50e327b 1383 if (!Config.accessList.outgoing_address) {
cfd66529 1384 return; // anything will do.
b50e327b
AJ
1385 }
1386
c0941a6a 1387 ACLFilledChecklist ch(NULL, request, NULL);
1b091aec 1388 ch.dst_peer_name = conn->getPeer() ? conn->getPeer()->name : NULL;
cfd66529
AJ
1389 ch.dst_addr = conn->remote;
1390
1391 // TODO use the connection details in ACL.
1392 // needs a bit of rework in ACLFilledChecklist to use Comm::Connection instead of ConnStateData
6db78a1a 1393
289848ca 1394 for (Acl::Address *l = Config.accessList.outgoing_address; l; l = l->next) {
cfd66529
AJ
1395
1396 /* check if the outgoing address is usable to the destination */
4dd643d5 1397 if (conn->remote.isIPv4() != l->addr.isIPv4()) continue;
cfd66529
AJ
1398
1399 /* check ACLs for this outgoing address */
06bf5384 1400 if (!l->aclList || ch.fastCheck(l->aclList).allowed()) {
cfd66529
AJ
1401 conn->local = l->addr;
1402 return;
1403 }
1404 }
b6b6f466 1405}
62e76326 1406
55622953
CT
1407/// \returns the TOS value that should be set on the to-peer connection
1408static tos_t
1409GetTosToServer(HttpRequest * request, Comm::Connection &conn)
425de4c8 1410{
55622953
CT
1411 if (!Ip::Qos::TheConfig.tosToServer)
1412 return 0;
1413
425de4c8 1414 ACLFilledChecklist ch(NULL, request, NULL);
55622953
CT
1415 ch.dst_peer_name = conn.getPeer() ? conn.getPeer()->name : nullptr;
1416 ch.dst_addr = conn.remote;
425de4c8
AJ
1417 return aclMapTOS(Ip::Qos::TheConfig.tosToServer, &ch);
1418}
1419
55622953
CT
1420/// \returns the Netfilter mark that should be set on the to-peer connection
1421static nfmark_t
1422GetNfmarkToServer(HttpRequest * request, Comm::Connection &conn)
b6b6f466 1423{
55622953
CT
1424 if (!Ip::Qos::TheConfig.nfmarkToServer)
1425 return 0;
1426
c0941a6a 1427 ACLFilledChecklist ch(NULL, request, NULL);
55622953
CT
1428 ch.dst_peer_name = conn.getPeer() ? conn.getPeer()->name : nullptr;
1429 ch.dst_addr = conn.remote;
244da4ad
AG
1430 const auto mc = aclFindNfMarkConfig(Ip::Qos::TheConfig.nfmarkToServer, &ch);
1431 return mc.mark;
b6a2f15e 1432}
6ee88490
CT
1433
1434void
1435GetMarkingsToServer(HttpRequest * request, Comm::Connection &conn)
1436{
1437 // Get the server side TOS and Netfilter mark to be set on the connection.
55622953
CT
1438 conn.tos = GetTosToServer(request, conn);
1439 conn.nfmark = GetNfmarkToServer(request, conn);
1440 debugs(17, 3, "from " << conn.local << " tos " << int(conn.tos) << " netfilter mark " << conn.nfmark);
1441}
6ee88490 1442
55622953
CT
1443void
1444ResetMarkingsToServer(HttpRequest * request, Comm::Connection &conn)
1445{
1446 GetMarkingsToServer(request, conn);
1447
1448 // TODO: Avoid these calls if markings has not changed.
1449 if (conn.tos)
1450 Ip::Qos::setSockTos(&conn, conn.tos);
1451 if (conn.nfmark)
1452 Ip::Qos::setSockNfmark(&conn, conn.nfmark);
6ee88490 1453}
f53969cc 1454