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