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