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