]> git.ipfire.org Git - thirdparty/squid.git/blame - src/forward.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / forward.cc
CommitLineData
41462d93 1/*
41462d93 2 * DEBUG: section 17 Request Forwarding
3 * AUTHOR: Duane Wessels
4 *
2b6662ba 5 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 6 * ----------------------------------------------------------
41462d93 7 *
2b6662ba 8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
41462d93 16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
26ac0430 21 *
41462d93 22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26ac0430 26 *
41462d93 27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
cbdec147 29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 30 *
41462d93 31 */
32
33
f7f3304a 34#include "squid-old.h"
b6b6f466 35#include "forward.h"
c0941a6a
AR
36#include "acl/FilledChecklist.h"
37#include "acl/Gadgets.h"
25b481e6 38#include "CacheManager.h"
f9b72e0c 39#include "comm/Connection.h"
aed188fd 40#include "comm/ConnOpener.h"
cfd66529 41#include "CommCalls.h"
d841c88d 42#include "comm/Loops.h"
aa839030 43#include "event.h"
44#include "errorpage.h"
45#include "fde.h"
bbaf2685 46#include "hier_code.h"
924f73bc 47#include "HttpReply.h"
aa839030 48#include "HttpRequest.h"
425de4c8 49#include "ip/QosConfig.h"
aa839030 50#include "MemObject.h"
781ce8ff 51#include "pconn.h"
cfd66529 52#include "PeerSelectState.h"
aa839030 53#include "SquidTime.h"
54#include "Store.h"
9b5c4a9a 55#include "icmp/net_db.h"
96d89ea0 56#include "ip/Intercept.h"
407b9bb1 57#include "ip/tools.h"
8822ebee 58#include "mgr/Registration.h"
4db984be
CT
59#if USE_SSL
60#include "ssl/support.h"
4d16918e 61#include "ssl/ErrorDetail.h"
4db984be 62#endif
cfd66529 63
6b679a01 64static PSC fwdPeerSelectionCompleteWrapper;
575d05c4 65static CLCB fwdServerClosedWrapper;
b6b6f466 66#if USE_SSL
67static PF fwdNegotiateSSLWrapper;
68#endif
b6b6f466 69static CNCB fwdConnectDoneWrapper;
70
8ddcc35d 71static OBJH fwdStats;
72
73#define MAX_FWD_STATS_IDX 9
9977e14b 74static int FwdReplyCodes[MAX_FWD_STATS_IDX + 1][HTTP_INVALID_HEADER + 1];
db1cd23c 75
781ce8ff 76static PconnPool *fwdPconnPool = new PconnPool("server-side");
b6b6f466 77CBDATA_CLASS_INIT(FwdState);
781ce8ff 78
429871db 79void
80FwdState::abort(void* d)
81{
82 FwdState* fwd = (FwdState*)d;
6ecaf21a 83 Pointer tmp = fwd; // Grab a temporary pointer to keep the object alive during our scope.
429871db 84
6b679a01 85 if (Comm::IsConnOpen(fwd->serverConnection())) {
5229395c 86 comm_remove_close_handler(fwd->serverConnection()->fd, fwdServerClosedWrapper, fwd);
6dd9a2e4
AJ
87 debugs(17, 3, HERE << "store entry aborted; closing " <<
88 fwd->serverConnection());
89 fwd->serverConnection()->close();
90 } else {
91 debugs(17, 7, HERE << "store entry aborted; no connection to close");
cff02fa6 92 }
00ae51e4 93 fwd->serverDestinations.clean();
429871db 94 fwd->self = NULL;
95}
96
b6b6f466 97/**** PUBLIC INTERFACE ********************************************************/
c7f9eb6d 98
be364179 99FwdState::FwdState(const Comm::ConnectionPointer &client, StoreEntry * e, HttpRequest * r)
db1cd23c 100{
3157a816 101 debugs(17, 2, HERE << "Forwarding client request " << client << ", url=" << e->url() );
b6b6f466 102 entry = e;
5c336a3b 103 clientConn = client;
6dd9f4bd 104 request = HTTPMSGLOCK(r);
bc81ee68 105 pconnRace = raceImpossible;
b6b6f466 106 start_t = squid_curtime;
00ae51e4 107 serverDestinations.reserve(Config.forward_max_tries);
3d0ac046 108 e->lock();
b6b6f466 109 EBIT_SET(e->flags, ENTRY_FWD_HDR_WAIT);
7a0fb323 110}
111
112// Called once, right after object creation, when it is safe to set self
fc68f6b1 113void FwdState::start(Pointer aSelf)
114{
7a0fb323 115 // Protect ourselves from being destroyed when the only Server pointing
116 // to us is gone (while we expect to talk to more Servers later).
117 // Once we set self, we are responsible for clearing it when we do not
118 // expect to talk to any servers.
119 self = aSelf; // refcounted
120
121 // We hope that either the store entry aborts or peer is selected.
122 // Otherwise we are going to leak our object.
34266cde 123
3900307b 124 entry->registerAbort(FwdState::abort, this);
bfe4e2fe
AJ
125
126 // Bug 3243: CVE 2009-0801
127 // Bypass of browser same-origin access control in intercepted communication
128 // To resolve this we must force DIRECT and only to the original client destination.
2962f8b8
AJ
129 const bool isIntercepted = request && !request->flags.redirected && (request->flags.intercepted || request->flags.spoof_client_ip);
130 const bool useOriginalDst = Config.onoff.client_dst_passthru || (request && !request->flags.hostVerified);
131 if (isIntercepted && useOriginalDst) {
bfe4e2fe
AJ
132 Comm::ConnectionPointer p = new Comm::Connection();
133 p->remote = clientConn->local;
134 p->peerType = ORIGINAL_DST;
135 getOutgoingAddress(request, p);
136 serverDestinations.push_back(p);
137
138 // destination "found". continue with the forwarding.
139 startConnectionOrFail();
140 } else {
141 // do full route options selection
142 peerSelect(&serverDestinations, request, entry, fwdPeerSelectionCompleteWrapper, this);
143 }
db1cd23c 144}
145
802a8c1d 146void
147FwdState::completed()
41462d93 148{
fc68f6b1 149 if (flags.forward_completed == 1) {
150 debugs(17, 1, HERE << "FwdState::completed called on a completed request! Bad!");
151 return;
152 }
153
154 flags.forward_completed = 1;
802a8c1d 155
c4a88a3e
CT
156 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
157 debugs(17, 3, HERE << "entry aborted");
158 return ;
159 }
160
bc87dc25 161#if URL_CHECKSUM_DEBUG
62e76326 162
b6b6f466 163 entry->mem_obj->checkUrlChecksum();
225644d7 164#endif
62e76326 165
b6b6f466 166 if (entry->store_status == STORE_PENDING) {
167 if (entry->isEmpty()) {
168 assert(err);
169 errorAppendEntry(entry, err);
170 err = NULL;
62e76326 171 } else {
b6b6f466 172 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
173 entry->complete();
d88e3c49 174 entry->releaseRequest();
62e76326 175 }
f563eea9 176 }
62e76326 177
b6b6f466 178 if (storePendingNClients(entry) > 0)
179 assert(!EBIT_TEST(entry->flags, ENTRY_FWD_HDR_WAIT));
62e76326 180
802a8c1d 181}
182
183FwdState::~FwdState()
184{
185 debugs(17, 3, HERE << "FwdState destructor starting");
fc68f6b1 186
802a8c1d 187 if (! flags.forward_completed)
fc68f6b1 188 completed();
802a8c1d 189
9d2760b6
AR
190 doneWithRetries();
191
6dd9f4bd 192 HTTPMSGUNLOCK(request);
62e76326 193
913524f0 194 delete err;
62e76326 195
3900307b 196 entry->unregisterAbort();
429871db 197
97b5e68f 198 entry->unlock();
62e76326 199
b6b6f466 200 entry = NULL;
62e76326 201
e3a4aecc
AJ
202 if (calls.connector != NULL) {
203 calls.connector->cancel("FwdState destructed");
204 calls.connector = NULL;
205 }
206
6b679a01 207 if (Comm::IsConnOpen(serverConn)) {
5229395c
AJ
208 comm_remove_close_handler(serverConnection()->fd, fwdServerClosedWrapper, this);
209 debugs(17, 3, HERE << "closing FD " << serverConnection()->fd);
00ae51e4 210 serverConn->close();
b6b6f466 211 }
fc68f6b1 212
00ae51e4 213 serverDestinations.clean();
cfd66529 214
b6b6f466 215 debugs(17, 3, HERE << "FwdState destructor done");
216}
62e76326 217
38413773 218/**
b6b6f466 219 * This is the entry point for client-side to start forwarding
220 * a transaction. It is a static method that may or may not
221 * allocate a FwdState.
222 */
be0c6690 223void
be364179 224FwdState::fwdStart(const Comm::ConnectionPointer &clientConn, StoreEntry *entry, HttpRequest *request)
b6b6f466 225{
b50e327b 226 /** \note
b6b6f466 227 * client_addr == no_addr indicates this is an "internal" request
228 * from peer_digest.c, asn.c, netdb.c, etc and should always
229 * be allowed. yuck, I know.
230 */
62e76326 231
b50e327b 232 if ( Config.accessList.miss && !request->client_addr.IsNoAddr() &&
39a19cb7 233 request->protocol != AnyP::PROTO_INTERNAL && request->protocol != AnyP::PROTO_CACHE_OBJECT) {
b50e327b 234 /**
b6b6f466 235 * Check if this host is allowed to fetch MISSES from us (miss_access)
236 */
c0941a6a 237 ACLFilledChecklist ch(Config.accessList.miss, request, NULL);
b6b6f466 238 ch.src_addr = request->client_addr;
239 ch.my_addr = request->my_addr;
2efeb0b7 240 if (ch.fastCheck() == ACCESS_DENIED) {
b6b6f466 241 err_type page_id;
9ce7856a 242 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, 1);
b6b6f466 243
244 if (page_id == ERR_NONE)
245 page_id = ERR_FORWARDING_DENIED;
246
913524f0 247 ErrorState *anErr = new ErrorState(page_id, HTTP_FORBIDDEN, request);
b6b6f466 248 errorAppendEntry(entry, anErr); // frees anErr
be0c6690 249 return;
b6b6f466 250 }
251 }
252
cfd66529 253 debugs(17, 3, HERE << "'" << entry->url() << "'");
f4ef658f 254 /*
255 * This seems like an odd place to bind mem_obj and request.
256 * Might want to assert that request is NULL at this point
257 */
6dd9f4bd 258 entry->mem_obj->request = HTTPMSGLOCK(request);
b6b6f466 259#if URL_CHECKSUM_DEBUG
260
261 entry->mem_obj->checkUrlChecksum();
262#endif
263
264 if (shutting_down) {
265 /* more yuck */
913524f0 266 ErrorState *anErr = new ErrorState(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE, request);
b6b6f466 267 errorAppendEntry(entry, anErr); // frees anErr
be0c6690 268 return;
6801f8a8 269 }
62e76326 270
b6b6f466 271 switch (request->protocol) {
272
0c3d3f65 273 case AnyP::PROTO_INTERNAL:
e37bd29b 274 internalStart(clientConn, request, entry);
be0c6690 275 return;
b6b6f466 276
39a19cb7 277 case AnyP::PROTO_CACHE_OBJECT:
5c336a3b 278 CacheManager::GetInstance()->Start(clientConn, request, entry);
be0c6690 279 return;
b6b6f466 280
0c3d3f65 281 case AnyP::PROTO_URN:
b6b6f466 282 urnStart(request, entry);
be0c6690 283 return;
b6b6f466 284
285 default:
5c336a3b 286 FwdState::Pointer fwd = new FwdState(clientConn, entry, request);
7a0fb323 287 fwd->start(fwd);
be0c6690 288 return;
b6b6f466 289 }
290
291 /* NOTREACHED */
41462d93 292}
293
cfd66529 294void
6b679a01 295FwdState::startConnectionOrFail()
cfd66529 296{
8652f8e7 297 debugs(17, 3, HERE << entry->url());
cfd66529 298
00ae51e4 299 if (serverDestinations.size() > 0) {
8652f8e7
AJ
300 // Ditch error page if it was created before.
301 // A new one will be created if there's another problem
913524f0
AJ
302 delete err;
303 err = NULL;
8652f8e7
AJ
304
305 // Update the logging information about this new server connection.
306 // Done here before anything else so the errors get logged for
307 // this server link regardless of what happens when connecting to it.
308 // IF sucessfuly connected this top destination will become the serverConnection().
309 request->hier.note(serverDestinations[0], request->GetHost());
310
cfd66529
AJ
311 connectStart();
312 } else {
95b83010
CT
313 debugs(17, 3, HERE << "Connection failed: " << entry->url());
314 if (!err) {
913524f0 315 ErrorState *anErr = new ErrorState(ERR_CANNOT_FORWARD, HTTP_INTERNAL_SERVER_ERROR, request);
8652f8e7 316 anErr->xerrno = errno;
95b83010 317 fail(anErr);
8652f8e7 318 } // else use actual error from last connection attempt
cfd66529
AJ
319 self = NULL; // refcounted
320 }
321}
322
b6b6f466 323void
324FwdState::fail(ErrorState * errorState)
325{
38413773 326 debugs(17, 3, HERE << err_type_str[errorState->type] << " \"" << httpStatusString(errorState->httpStatus) << "\"\n\t" << entry->url() );
b6b6f466 327
913524f0 328 delete err;
b6b6f466 329 err = errorState;
330
331 if (!errorState->request)
6dd9f4bd 332 errorState->request = HTTPMSGLOCK(request);
64b66b76 333
bc81ee68
AR
334 if (pconnRace == racePossible && err->type == ERR_ZERO_SIZE_OBJECT) {
335 debugs(17, 5, HERE << "pconn race happened");
336 pconnRace = raceHappened;
337 }
338
2f3e52b5
CT
339#if USE_SSL
340 if (errorState->type == ERR_SECURE_CONNECT_FAIL && errorState->detail)
341 request->detailError(errorState->type, errorState->detail->errorNo());
342 else
343#endif
e83cdc25 344 request->detailError(errorState->type, errorState->xerrno);
b6b6f466 345}
346
38413773 347/**
b6b6f466 348 * Frees fwdState without closing FD or generating an abort
349 */
350void
00ae51e4 351FwdState::unregister(Comm::ConnectionPointer &conn)
5229395c
AJ
352{
353 debugs(17, 3, HERE << entry->url() );
354 assert(serverConnection() == conn);
6b679a01 355 assert(Comm::IsConnOpen(conn));
5229395c 356 comm_remove_close_handler(conn->fd, fwdServerClosedWrapper, this);
00ae51e4 357 serverConn = NULL;
5229395c
AJ
358}
359
360// Legacy method to be removed in favor of the above as soon as possible
361void
b6b6f466 362FwdState::unregister(int fd)
363{
5229395c
AJ
364 debugs(17, 3, HERE << entry->url() );
365 assert(fd == serverConnection()->fd);
00ae51e4 366 unregister(serverConn);
b6b6f466 367}
368
38413773 369/**
b6b6f466 370 * server-side modules call fwdComplete() when they are done
371 * downloading an object. Then, we either 1) re-forward the
372 * request somewhere else if needed, or 2) call storeComplete()
373 * to finish it off
374 */
375void
376FwdState::complete()
377{
cfd66529 378 debugs(17, 3, HERE << entry->url() << "\n\tstatus " << entry->getReply()->sline.status );
b6b6f466 379#if URL_CHECKSUM_DEBUG
380
381 entry->mem_obj->checkUrlChecksum();
382#endif
383
384 logReplyStatus(n_tries, entry->getReply()->sline.status);
385
386 if (reforward()) {
cfd66529 387 debugs(17, 3, HERE << "re-forwarding " << entry->getReply()->sline.status << " " << entry->url());
b6b6f466 388
6b679a01 389 if (Comm::IsConnOpen(serverConn))
00ae51e4 390 unregister(serverConn);
b6b6f466 391
cfd66529
AJ
392 entry->reset();
393
8652f8e7
AJ
394 // drop the last path off the selection list. try the next one.
395 serverDestinations.shift();
396 startConnectionOrFail();
397
b6b6f466 398 } else {
6b679a01
AJ
399 if (Comm::IsConnOpen(serverConn))
400 debugs(17, 3, HERE << "server FD " << serverConnection()->fd << " not re-forwarding status " << entry->getReply()->sline.status);
401 else
402 debugs(17, 3, HERE << "server (FD closed) not re-forwarding status " << entry->getReply()->sline.status);
9e5c22cf
AJ
403 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
404 entry->complete();
fc68f6b1 405
6b679a01 406 if (!Comm::IsConnOpen(serverConn))
fc68f6b1 407 completed();
408
7a0fb323 409 self = NULL; // refcounted
b6b6f466 410 }
411}
412
413
414/**** CALLBACK WRAPPERS ************************************************************/
415
416static void
a37fdd8a 417fwdPeerSelectionCompleteWrapper(Comm::ConnectionList * unused, ErrorState *err, void *data)
b6b6f466 418{
419 FwdState *fwd = (FwdState *) data;
a37fdd8a
AJ
420 if (err)
421 fwd->fail(err);
6b679a01 422 fwd->startConnectionOrFail();
b6b6f466 423}
424
425static void
575d05c4 426fwdServerClosedWrapper(const CommCloseCbParams &params)
b6b6f466 427{
575d05c4
AJ
428 FwdState *fwd = (FwdState *)params.data;
429 fwd->serverClosed(params.fd);
b6b6f466 430}
431
b6b6f466 432#if USE_SSL
433static void
434fwdNegotiateSSLWrapper(int fd, void *data)
435{
436 FwdState *fwd = (FwdState *) data;
437 fwd->negotiateSSL(fd);
438}
439
440#endif
441
cfd66529 442void
f01d4b80 443fwdConnectDoneWrapper(const Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data)
b6b6f466 444{
445 FwdState *fwd = (FwdState *) data;
aed188fd 446 fwd->connectDone(conn, status, xerrno);
b6b6f466 447}
448
449/**** PRIVATE *****************************************************************/
450
545782b8 451/*
452 * FwdState::checkRetry
26ac0430 453 *
545782b8 454 * Return TRUE if the request SHOULD be retried. This method is
455 * called when the HTTP connection fails, or when the connection
456 * is closed before server-side read the end of HTTP headers.
457 */
b6b6f466 458bool
459FwdState::checkRetry()
68bd6892 460{
d8fd0f18 461 if (shutting_down)
b6b6f466 462 return false;
62e76326 463
9d2760b6
AR
464 if (!self) { // we have aborted before the server called us back
465 debugs(17, 5, HERE << "not retrying because of earlier abort");
466 // we will be destroyed when the server clears its Pointer to us
467 return false;
468 }
469
b6b6f466 470 if (entry->store_status != STORE_PENDING)
471 return false;
62e76326 472
b6b6f466 473 if (!entry->isEmpty())
474 return false;
62e76326 475
b6b6f466 476 if (n_tries > 10)
477 return false;
62e76326 478
b6b6f466 479 if (origin_tries > 2)
480 return false;
4ed0e075 481
b6b6f466 482 if (squid_curtime - start_t > Config.Timeout.forward)
483 return false;
62e76326 484
b6b6f466 485 if (flags.dont_retry)
486 return false;
62e76326 487
0bbd5532
AJ
488 // NP: not yet actually connected anywhere. retry is safe.
489 if (!flags.connected_okay)
490 return true;
491
5d4989a8 492 if (!checkRetriable())
493 return false;
494
58217e94 495 if (request->bodyNibbled())
b6b6f466 496 return false;
62e76326 497
b6b6f466 498 return true;
68bd6892 499}
500
545782b8 501/*
502 * FwdState::checkRetriable
503 *
504 * Return TRUE if this is the kind of request that can be retried
505 * after a failure. If the request is not retriable then we don't
506 * want to risk sending it on a persistent connection. Instead we'll
507 * force it to go on a new HTTP connection.
508 */
b6b6f466 509bool
510FwdState::checkRetriable()
cb928909 511{
cb928909 512 /* RFC2616 9.1 Safe and Idempotent Methods */
914b89a2 513 switch (request->method.id()) {
cb928909 514 /* 9.1.1 Safe Methods */
515
516 case METHOD_GET:
517
518 case METHOD_HEAD:
914b89a2 519 /* 9.1.2 Idempotent Methods */
cb928909 520
521 case METHOD_PUT:
522
523 case METHOD_DELETE:
524
525 case METHOD_OPTIONS:
526
527 case METHOD_TRACE:
528 break;
529
530 default:
b6b6f466 531 return false;
cb928909 532 }
533
b6b6f466 534 return true;
cb928909 535}
536
b6b6f466 537void
538FwdState::serverClosed(int fd)
910169e5 539{
cfd66529 540 debugs(17, 2, HERE << "FD " << fd << " " << entry->url());
3e8c047e 541 retryOrBail();
542}
543
544void
26ac0430
AJ
545FwdState::retryOrBail()
546{
b6b6f466 547 if (checkRetry()) {
cfd66529 548 debugs(17, 3, HERE << "re-forwarding (" << n_tries << " tries, " << (squid_curtime - start_t) << " secs)");
bc81ee68
AR
549 // we should retry the same destination if it failed due to pconn race
550 if (pconnRace == raceHappened)
551 debugs(17, 4, HERE << "retrying the same destination");
552 else
553 serverDestinations.shift(); // last one failed. try another.
8652f8e7
AJ
554 startConnectionOrFail();
555 return;
d8fd0f18 556 }
62e76326 557
9d2760b6
AR
558 // TODO: should we call completed() here and move doneWithRetries there?
559 doneWithRetries();
560
561 if (self != NULL && !err && shutting_down) {
913524f0 562 ErrorState *anErr = new ErrorState(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE, request);
f01d4b80 563 errorAppendEntry(entry, anErr);
f563eea9 564 }
62e76326 565
b6b6f466 566 self = NULL; // refcounted
910169e5 567}
568
9d2760b6
AR
569// If the Server quits before nibbling at the request body, the body sender
570// will not know (so that we can retry). Call this if we will not retry. We
571// will notify the sender so that it does not get stuck waiting for space.
572void
573FwdState::doneWithRetries()
574{
575 if (request && request->body_pipe != NULL)
576 request->body_pipe->expectNoConsumption();
577}
578
3e8c047e 579// called by the server that failed after calling unregister()
580void
581FwdState::handleUnregisteredServerEnd()
582{
cfd66529 583 debugs(17, 2, HERE << "self=" << self << " err=" << err << ' ' << entry->url());
6b679a01 584 assert(!Comm::IsConnOpen(serverConn));
3e8c047e 585 retryOrBail();
586}
587
a7ad6e4e 588#if USE_SSL
b6b6f466 589void
590FwdState::negotiateSSL(int fd)
a7ad6e4e 591{
8e9bae99 592 unsigned long ssl_lib_error = SSL_ERROR_NONE;
a7ad6e4e 593 SSL *ssl = fd_table[fd].ssl;
594 int ret;
62e76326 595
a7ad6e4e 596 if ((ret = SSL_connect(ssl)) <= 0) {
62e76326 597 int ssl_error = SSL_get_error(ssl, ret);
8e9bae99
CT
598#ifdef EPROTO
599 int sysErrNo = EPROTO;
600#else
601 int sysErrNo = EACCES;
602#endif
62e76326 603
604 switch (ssl_error) {
605
606 case SSL_ERROR_WANT_READ:
d841c88d 607 Comm::SetSelect(fd, COMM_SELECT_READ, fwdNegotiateSSLWrapper, this, 0);
62e76326 608 return;
609
610 case SSL_ERROR_WANT_WRITE:
d841c88d 611 Comm::SetSelect(fd, COMM_SELECT_WRITE, fwdNegotiateSSLWrapper, this, 0);
62e76326 612 return;
613
8e9bae99
CT
614 case SSL_ERROR_SSL:
615 case SSL_ERROR_SYSCALL:
616 ssl_lib_error = ERR_get_error();
26ac0430 617 debugs(81, 1, "fwdNegotiateSSL: Error negotiating SSL connection on FD " << fd <<
8e9bae99 618 ": " << ERR_error_string(ssl_lib_error, NULL) << " (" << ssl_error <<
26ac0430 619 "/" << ret << "/" << errno << ")");
62e76326 620
8e9bae99
CT
621 // store/report errno when ssl_error is SSL_ERROR_SYSCALL, ssl_lib_error is 0, and ret is -1
622 if (ssl_error == SSL_ERROR_SYSCALL && ret == -1 && ssl_lib_error == 0)
623 sysErrNo = errno;
62e76326 624
8e9bae99
CT
625 // falling through to complete error handling
626
627 default:
628 ErrorState *const anErr = makeConnectingError(ERR_SECURE_CONNECT_FAIL);
629 anErr->xerrno = sysErrNo;
62e76326 630
4d16918e 631 Ssl::ErrorDetail *errFromFailure = (Ssl::ErrorDetail *)SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail);
e34763f4 632 if (errFromFailure != NULL) {
4d16918e
CT
633 // The errFromFailure is attached to the ssl object
634 // and will be released when ssl object destroyed.
635 // Copy errFromFailure to a new Ssl::ErrorDetail object
636 anErr->detail = new Ssl::ErrorDetail(*errFromFailure);
ef5fbcff 637 } else {
8e9bae99
CT
638 // clientCert can be be NULL
639 X509 *client_cert = SSL_get_peer_certificate(ssl);
640 anErr->detail = new Ssl::ErrorDetail(SQUID_ERR_SSL_HANDSHAKE, client_cert);
641 if (client_cert)
642 X509_free(client_cert);
643 }
644
645 if (ssl_lib_error != SSL_ERROR_NONE)
646 anErr->detail->setLibError(ssl_lib_error);
4d16918e 647
b6b6f466 648 fail(anErr);
62e76326 649
5229395c
AJ
650 if (serverConnection()->getPeer()) {
651 peerConnectFailed(serverConnection()->getPeer());
62e76326 652 }
653
00ae51e4 654 serverConn->close();
62e76326 655 return;
656 }
a7ad6e4e 657 }
62e76326 658
5229395c
AJ
659 if (serverConnection()->getPeer() && !SSL_session_reused(ssl)) {
660 if (serverConnection()->getPeer()->sslSession)
661 SSL_SESSION_free(serverConnection()->getPeer()->sslSession);
f38c5e43 662
5229395c 663 serverConnection()->getPeer()->sslSession = SSL_get1_session(ssl);
f38c5e43 664 }
665
b6b6f466 666 dispatch();
a7ad6e4e 667}
668
b6b6f466 669void
670FwdState::initiateSSL()
a7ad6e4e 671{
a7ad6e4e 672 SSL *ssl;
673 SSL_CTX *sslContext = NULL;
5229395c
AJ
674 const peer *peer = serverConnection()->getPeer();
675 int fd = serverConnection()->fd;
62e76326 676
a7ad6e4e 677 if (peer) {
62e76326 678 assert(peer->use_ssl);
679 sslContext = peer->sslContext;
a7ad6e4e 680 } else {
62e76326 681 sslContext = Config.ssl_client.sslContext;
a7ad6e4e 682 }
62e76326 683
a7ad6e4e 684 assert(sslContext);
62e76326 685
a7ad6e4e 686 if ((ssl = SSL_new(sslContext)) == NULL) {
bf8fe701 687 debugs(83, 1, "fwdInitiateSSL: Error allocating handle: " << ERR_error_string(ERR_get_error(), NULL) );
913524f0 688 ErrorState *anErr = new ErrorState(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
b6b6f466 689 anErr->xerrno = errno;
b6b6f466 690 fail(anErr);
691 self = NULL; // refcounted
62e76326 692 return;
a7ad6e4e 693 }
62e76326 694
a7ad6e4e 695 SSL_set_fd(ssl, fd);
62e76326 696
a7ad6e4e 697 if (peer) {
62e76326 698 if (peer->ssldomain)
699 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->ssldomain);
700
a7ad6e4e 701#if NOT_YET
62e76326 702
703 else if (peer->name)
704 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->name);
705
a7ad6e4e 706#endif
62e76326 707
708 else
709 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->host);
f38c5e43 710
711 if (peer->sslSession)
712 SSL_set_session(ssl, peer->sslSession);
713
a7ad6e4e 714 } else {
cc192b50 715 SSL_set_ex_data(ssl, ssl_ex_index_server, (void*)request->GetHost());
253749a8
CT
716
717 // We need to set SNI TLS extension only in the case we are
718 // connecting direct to origin server
719 Ssl::setClientSNI(ssl, request->GetHost());
a7ad6e4e 720 }
62e76326 721
4d5a6db8 722 // Create the ACL check list now, while we have access to more info.
723 // The list is used in ssl_verify_cb() and is freed in ssl_free().
724 if (acl_access *acl = Config.ssl_client.cert_error) {
c0941a6a 725 ACLFilledChecklist *check = new ACLFilledChecklist(acl, request, dash_str);
4d5a6db8 726 check->fd(fd);
727 SSL_set_ex_data(ssl, ssl_ex_index_cert_error_check, check);
728 }
729
a7ad6e4e 730 fd_table[fd].ssl = ssl;
731 fd_table[fd].read_method = &ssl_read_method;
732 fd_table[fd].write_method = &ssl_write_method;
b6b6f466 733 negotiateSSL(fd);
a7ad6e4e 734}
62e76326 735
a7ad6e4e 736#endif
737
b6b6f466 738void
f01d4b80 739FwdState::connectDone(const Comm::ConnectionPointer &conn, comm_err_t status, int xerrno)
41462d93 740{
2f538b78 741 if (status != COMM_OK) {
9c8a6c3b 742 ErrorState *const anErr = makeConnectingError(ERR_CONNECT_FAIL);
b6b6f466 743 anErr->xerrno = xerrno;
b6b6f466 744 fail(anErr);
62e76326 745
2f538b78 746 /* it might have been a timeout with a partially open link */
00ae51e4
AJ
747 if (conn != NULL) {
748 if (conn->getPeer())
749 peerConnectFailed(conn->getPeer());
62e76326 750
80463bb4 751 conn->close();
2f538b78 752 }
aed188fd 753 retryOrBail();
2f538b78
AJ
754 return;
755 }
62e76326 756
00ae51e4
AJ
757 serverConn = conn;
758
f01d4b80 759 debugs(17, 3, HERE << serverConnection() << ": '" << entry->url() << "'" );
62e76326 760
5229395c 761 comm_add_close_handler(serverConnection()->fd, fwdServerClosedWrapper, this);
2f538b78 762
5229395c
AJ
763 if (serverConnection()->getPeer())
764 peerConnectSucceded(serverConnection()->getPeer());
2f538b78
AJ
765
766#if USE_SSL
ea33d92b 767 if (!request->flags.pinned) {
39322eba
J
768 if ((serverConnection()->getPeer() && serverConnection()->getPeer()->use_ssl) ||
769 (!serverConnection()->getPeer() && request->protocol == AnyP::PROTO_HTTPS)) {
770 initiateSSL();
771 return;
772 }
41462d93 773 }
2f538b78
AJ
774#endif
775
0bbd5532 776 flags.connected_okay = true;
2f538b78 777 dispatch();
41462d93 778}
779
b6b6f466 780void
781FwdState::connectTimeout(int fd)
41462d93 782{
bf8fe701 783 debugs(17, 2, "fwdConnectTimeout: FD " << fd << ": '" << entry->url() << "'" );
6b679a01
AJ
784 assert(serverDestinations[0] != NULL);
785 assert(fd == serverDestinations[0]->fd);
62e76326 786
528b2c61 787 if (entry->isEmpty()) {
913524f0 788 ErrorState *anErr = new ErrorState(ERR_CONNECT_FAIL, HTTP_GATEWAY_TIMEOUT, request);
b6b6f466 789 anErr->xerrno = ETIMEDOUT;
790 fail(anErr);
62e76326 791
cfd66529 792 /* This marks the peer DOWN ... */
6b679a01
AJ
793 if (serverDestinations[0]->getPeer())
794 peerConnectFailed(serverDestinations[0]->getPeer());
41462d93 795 }
62e76326 796
6b679a01
AJ
797 if (Comm::IsConnOpen(serverDestinations[0])) {
798 serverDestinations[0]->close();
746beefe 799 }
41462d93 800}
801
cfd66529 802/**
ae18571d
AJ
803 * Called after Forwarding path selection (via peer select) has taken place.
804 * And whenever forwarding needs to attempt a new connection (routing failover)
805 * We have a vector of possible localIP->remoteIP paths now ready to start being connected.
cfd66529 806 */
b6b6f466 807void
808FwdState::connectStart()
41462d93 809{
6b679a01
AJ
810 assert(serverDestinations.size() > 0);
811
cfd66529 812 debugs(17, 3, "fwdConnectStart: " << entry->url());
62e76326 813
3ff65596
AR
814 if (n_tries == 0) // first attempt
815 request->hier.first_conn_start = current_time;
816
cfd66529
AJ
817 /* connection timeout */
818 int ctimeout;
6b679a01
AJ
819 if (serverDestinations[0]->getPeer()) {
820 ctimeout = serverDestinations[0]->getPeer()->connect_timeout > 0 ?
821 serverDestinations[0]->getPeer()->connect_timeout : Config.Timeout.peer_connect;
db1cd23c 822 } else {
62e76326 823 ctimeout = Config.Timeout.connect;
db1cd23c 824 }
62e76326 825
cfd66529
AJ
826 /* calculate total forwarding timeout ??? */
827 int ftimeout = Config.Timeout.forward - (squid_curtime - start_t);
777831e0 828 if (ftimeout < 0)
829 ftimeout = 5;
830
831 if (ftimeout < ctimeout)
832 ctimeout = ftimeout;
833
1b76e6c1 834 if (serverDestinations[0]->getPeer() && request->flags.sslBumped == true) {
b68415a1 835 debugs(50, 4, "fwdConnectStart: Ssl bumped connections through parrent proxy are not allowed");
913524f0 836 ErrorState *anErr = new ErrorState(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE, request);
b68415a1
CT
837 fail(anErr);
838 self = NULL; // refcounted
839 return;
985bfe6b 840 }
d67acb4e 841
2efeb0b7
AJ
842 request->flags.pinned = 0; // XXX: what if the ConnStateData set this to flag existing credentials?
843 // XXX: answer: the peer selection *should* catch it and give us only the pinned peer. so we reverse the =0 step below.
844 // XXX: also, logs will now lie if pinning is broken and leads to an error message.
6b679a01 845 if (serverDestinations[0]->peerType == PINNED) {
26ac0430 846 ConnStateData *pinned_connection = request->pinnedConnection();
bc81ee68
AR
847 // pinned_connection may become nil after a pconn race
848 if (pinned_connection)
849 serverConn = pinned_connection->validatePinnedConnection(request, serverDestinations[0]->getPeer());
850 else
851 serverConn = NULL;
e3a4aecc 852 if (Comm::IsConnOpen(serverConn)) {
d67acb4e 853#if 0
e3a4aecc
AJ
854 if (!serverConn->getPeer())
855 serverConn->peerType = HIER_DIRECT;
d67acb4e 856#endif
d67acb4e
AJ
857 n_tries++;
858 request->flags.pinned = 1;
859 if (pinned_connection->pinnedAuth())
860 request->flags.auth = 1;
bc81ee68
AR
861 // the server may close the pinned connection before this request
862 pconnRace = racePossible;
6b679a01 863 dispatch();
d67acb4e
AJ
864 return;
865 }
26ac0430 866 /* Failure. Fall back on next path */
b1cf2350 867 debugs(17,2,HERE << " Pinned connection " << pinned_connection << " not valid.");
00ae51e4 868 serverDestinations.shift();
6b679a01 869 startConnectionOrFail();
d67acb4e 870 return;
26ac0430 871 }
d67acb4e 872
642a305c 873 // Use pconn to avoid opening a new connection.
cfd66529 874 const char *host;
6b679a01
AJ
875 if (serverDestinations[0]->getPeer()) {
876 host = serverDestinations[0]->getPeer()->host;
af6a12ee 877 } else {
06093389 878 host = request->GetHost();
06093389 879 }
bc81ee68
AR
880
881 Comm::ConnectionPointer temp;
882 // Avoid pconns after races so that the same client does not suffer twice.
883 // This does not increase the total number of connections because we just
884 // closed the connection that failed the race. And re-pinning assumes this.
885 if (pconnRace != raceHappened)
886 temp = fwdPconnPool->pop(serverDestinations[0], host, checkRetriable());
bbf706ff 887
bc81ee68
AR
888 const bool openedPconn = Comm::IsConnOpen(temp);
889 pconnRace = openedPconn ? racePossible : raceImpossible;
cfd66529 890
6b679a01 891 // if we found an open persistent connection to use. use it.
bc81ee68 892 if (openedPconn) {
80463bb4 893 serverConn = temp;
642a305c 894 debugs(17, 3, HERE << "reusing pconn " << serverConnection());
c8ceec27 895 n_tries++;
4ed0e075 896
5229395c 897 if (!serverConnection()->getPeer())
c8ceec27 898 origin_tries++;
74780d33 899
5229395c 900 comm_add_close_handler(serverConnection()->fd, fwdServerClosedWrapper, this);
b5523edc
AJ
901
902 /* Update server side TOS and Netfilter mark on the connection. */
903 if (Ip::Qos::TheConfig.isAclTosActive()) {
904 temp->tos = GetTosToServer(request);
905 Ip::Qos::setSockTos(temp, temp->tos);
906 }
907#if SO_MARK
908 if (Ip::Qos::TheConfig.isAclNfmarkActive()) {
909 temp->nfmark = GetNfmarkToServer(request);
910 Ip::Qos::setSockNfmark(temp, temp->nfmark);
911 }
912#endif
913
c8ceec27 914 dispatch();
c8ceec27 915 return;
41462d93 916 }
62e76326 917
bc81ee68
AR
918 // We will try to open a new connection, possibly to the same destination.
919 // We reset serverDestinations[0] in case we are using it again because
920 // ConnOpener modifies its destination argument.
921 serverDestinations[0]->local.SetPort(0);
922 serverConn = NULL;
923
bc87dc25 924#if URL_CHECKSUM_DEBUG
b6b6f466 925 entry->mem_obj->checkUrlChecksum();
bc87dc25 926#endif
62e76326 927
b5523edc
AJ
928 /* Get the server side TOS and Netfilter mark to be set on the connection. */
929 if (Ip::Qos::TheConfig.isAclTosActive()) {
930 serverDestinations[0]->tos = GetTosToServer(request);
931 }
11e8cfe3 932#if SO_MARK && USE_LIBCAP
a750e510 933 serverDestinations[0]->nfmark = GetNfmarkToServer(request);
25b481e6 934 debugs(17, 3, "fwdConnectStart: got outgoing addr " << serverDestinations[0]->local << ", tos " << int(serverDestinations[0]->tos)
a750e510 935 << ", netfilter mark " << serverDestinations[0]->nfmark);
425de4c8 936#else
a750e510 937 serverDestinations[0]->nfmark = 0;
25b481e6 938 debugs(17, 3, "fwdConnectStart: got outgoing addr " << serverDestinations[0]->local << ", tos " << int(serverDestinations[0]->tos));
b5523edc
AJ
939#endif
940
e3a4aecc
AJ
941 calls.connector = commCbCall(17,3, "fwdConnectDoneWrapper", CommConnectCbPtrFun(fwdConnectDoneWrapper, this));
942 Comm::ConnOpener *cs = new Comm::ConnOpener(serverDestinations[0], calls.connector, ctimeout);
aed188fd 943 cs->setHost(host);
855150a4 944 AsyncJob::Start(cs);
41462d93 945}
946
b6b6f466 947void
948FwdState::dispatch()
41462d93 949{
5c336a3b 950 debugs(17, 3, HERE << clientConn << ": Fetching '" << RequestMethodStr(request->method) << " " << entry->url() << "'");
e0ebe27c 951 /*
952 * Assert that server_fd is set. This is to guarantee that fwdState
953 * is attached to something and will be deallocated when server_fd
954 * is closed.
955 */
6b679a01 956 assert(Comm::IsConnOpen(serverConn));
62e76326 957
5229395c 958 fd_note(serverConnection()->fd, entry->url());
62e76326 959
5229395c 960 fd_table[serverConnection()->fd].noteUse(fwdPconnPool);
62e76326 961
a7ad6e4e 962 /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
963 assert(entry->ping_status != PING_WAITING);
62e76326 964
a7ad6e4e 965 assert(entry->lock_count);
62e76326 966
a7ad6e4e 967 EBIT_SET(entry->flags, ENTRY_DISPATCHED);
62e76326 968
cc192b50 969 netdbPingSite(request->GetHost());
62e76326 970
425de4c8 971 /* Retrieves remote server TOS or MARK value, and stores it as part of the
7172612f 972 * original client request FD object. It is later used to forward
425de4c8 973 * remote server's TOS/MARK in the response to the client in case of a MISS.
7172612f 974 */
425de4c8 975 if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
b5523edc
AJ
976 if (Comm::IsConnOpen(clientConn) && Comm::IsConnOpen(serverConnection())) {
977 fde * clientFde = &fd_table[clientConn->fd]; // XXX: move the fd_table access into Ip::Qos
425de4c8 978 /* Get the netfilter mark for the connection */
b5523edc 979 Ip::Qos::getNfmarkFromServer(serverConnection(), clientFde);
425de4c8
AJ
980 }
981 }
982
983#if _SQUID_LINUX_
984 /* Bug 2537: The TOS forward part of QOS only applies to patched Linux kernels. */
985 if (Ip::Qos::TheConfig.isHitTosActive()) {
b5523edc
AJ
986 if (Comm::IsConnOpen(clientConn)) {
987 fde * clientFde = &fd_table[clientConn->fd]; // XXX: move the fd_table access into Ip::Qos
425de4c8 988 /* Get the TOS value for the packet */
b5523edc 989 Ip::Qos::getTosFromServer(serverConnection(), clientFde);
7172612f 990 }
26ac0430 991 }
7172612f
AJ
992#endif
993
5229395c
AJ
994 if (serverConnection()->getPeer() != NULL) {
995 serverConnection()->getPeer()->stats.fetches++;
996 request->peer_login = serverConnection()->getPeer()->login;
997 request->peer_domain = serverConnection()->getPeer()->domain;
b6b6f466 998 httpStart(this);
41462d93 999 } else {
b6b6f466 1000 request->peer_login = NULL;
1001 request->peer_domain = NULL;
62e76326 1002
1003 switch (request->protocol) {
a7ad6e4e 1004#if USE_SSL
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:
b6b6f466 1020 ftpStart(this);
62e76326 1021 break;
1022
39a19cb7 1023 case AnyP::PROTO_CACHE_OBJECT:
62e76326 1024
0c3d3f65 1025 case AnyP::PROTO_INTERNAL:
62e76326 1026
0c3d3f65 1027 case AnyP::PROTO_URN:
62e76326 1028 fatal_dump("Should never get here");
1029 break;
1030
0c3d3f65 1031 case AnyP::PROTO_WHOIS:
b6b6f466 1032 whoisStart(this);
62e76326 1033 break;
1034
0c3d3f65 1035 case AnyP::PROTO_WAIS: /* Not implemented */
fc68f6b1 1036
62e76326 1037 default:
7be06178 1038 debugs(17, DBG_IMPORTANT, "WARNING: Cannot retrieve '" << entry->url() << "'.");
913524f0 1039 ErrorState *anErr = new ErrorState(ERR_UNSUP_REQ, HTTP_BAD_REQUEST, request);
b6b6f466 1040 fail(anErr);
7be06178 1041 // Set the dont_retry flag because this is not a transient (network) error.
b6b6f466 1042 flags.dont_retry = 1;
6b679a01 1043 if (Comm::IsConnOpen(serverConn)) {
00ae51e4 1044 serverConn->close();
746beefe 1045 }
62e76326 1046 break;
1047 }
41462d93 1048 }
1049}
1050
545782b8 1051/*
1052 * FwdState::reforward
1053 *
1054 * returns TRUE if the transaction SHOULD be re-forwarded to the
8bbb16e3 1055 * next choice in the serverDestinations list. This method is called when
545782b8 1056 * server-side communication completes normally, or experiences
1057 * some error after receiving the end of HTTP headers.
1058 */
b6b6f466 1059int
1060FwdState::reforward()
db1cd23c 1061{
b6b6f466 1062 StoreEntry *e = entry;
db1cd23c 1063 http_status s;
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
b6b6f466 1087 if (origin_tries > 1)
4ed0e075 1088 return 0;
1089
58217e94 1090 if (request->bodyNibbled())
62e76326 1091 return 0;
1092
8652f8e7
AJ
1093 if (serverDestinations.size() <= 1) {
1094 // NP: <= 1 since total count includes the recently failed one.
cfd66529 1095 debugs(17, 3, HERE << "No alternative forwarding paths left");
62e76326 1096 return 0;
db1cd23c 1097 }
62e76326 1098
528b2c61 1099 s = e->getReply()->sline.status;
cfd66529 1100 debugs(17, 3, HERE << "status " << s);
b6b6f466 1101 return reforwardableStatus(s);
db1cd23c 1102}
1103
2ac4f6b5
AR
1104/**
1105 * Create "503 Service Unavailable" or "504 Gateway Timeout" error depending
1106 * on whether this is a validation request. RFC 2616 says that we MUST reply
1107 * with "504 Gateway Timeout" if validation fails and cached reply has
1108 * proxy-revalidate, must-revalidate or s-maxage Cache-Control directive.
1109 */
1110ErrorState *
1111FwdState::makeConnectingError(const err_type type) const
1112{
913524f0
AJ
1113 return new ErrorState(type, request->flags.need_validation ?
1114 HTTP_GATEWAY_TIMEOUT : HTTP_SERVICE_UNAVAILABLE, request);
2ac4f6b5
AR
1115}
1116
b6b6f466 1117static void
1118fwdStats(StoreEntry * s)
64d8034e 1119{
b6b6f466 1120 int i;
1121 int j;
1122 storeAppendPrintf(s, "Status");
62e76326 1123
b6b6f466 1124 for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
1125 storeAppendPrintf(s, "\ttry#%d", j + 1);
64d8034e 1126 }
64d8034e 1127
b6b6f466 1128 storeAppendPrintf(s, "\n");
0185bd6f 1129
b6b6f466 1130 for (i = 0; i <= (int) HTTP_INVALID_HEADER; i++) {
1131 if (FwdReplyCodes[0][i] == 0)
1132 continue;
0185bd6f 1133
b6b6f466 1134 storeAppendPrintf(s, "%3d", i);
0185bd6f 1135
b6b6f466 1136 for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
1137 storeAppendPrintf(s, "\t%d", FwdReplyCodes[j][i]);
62e76326 1138 }
62e76326 1139
b6b6f466 1140 storeAppendPrintf(s, "\n");
e0ebe27c 1141 }
7197b20d 1142}
1143
a4bd7d1f 1144
b6b6f466 1145/**** STATIC MEMBER FUNCTIONS *************************************************/
db1cd23c 1146
b6b6f466 1147bool
1148FwdState::reforwardableStatus(http_status s)
db1cd23c 1149{
b6b6f466 1150 switch (s) {
62e76326 1151
b6b6f466 1152 case HTTP_BAD_GATEWAY:
62e76326 1153
b6b6f466 1154 case HTTP_GATEWAY_TIMEOUT:
1155 return true;
62e76326 1156
b6b6f466 1157 case HTTP_FORBIDDEN:
62e76326 1158
b6b6f466 1159 case HTTP_INTERNAL_SERVER_ERROR:
62e76326 1160
b6b6f466 1161 case HTTP_NOT_IMPLEMENTED:
62e76326 1162
b6b6f466 1163 case HTTP_SERVICE_UNAVAILABLE:
1164 return Config.retry.onerror;
62e76326 1165
b6b6f466 1166 default:
1167 return false;
db1cd23c 1168 }
b6b6f466 1169
1170 /* NOTREACHED */
db1cd23c 1171}
8ddcc35d 1172
06093389
AJ
1173/**
1174 * Decide where details need to be gathered to correctly describe a persistent connection.
1175 * What is needed:
642a305c
AJ
1176 * - the address/port details about this link
1177 * - domain name of server at other end of this link (either peer or requested host)
06093389 1178 */
781ce8ff 1179void
642a305c 1180FwdState::pconnPush(Comm::ConnectionPointer &conn, const char *domain)
781ce8ff 1181{
642a305c
AJ
1182 if (conn->getPeer()) {
1183 fwdPconnPool->push(conn, conn->getPeer()->name);
06093389 1184 } else {
642a305c 1185 fwdPconnPool->push(conn, domain);
06093389 1186 }
781ce8ff 1187}
1188
8ddcc35d 1189void
b6b6f466 1190FwdState::initModule()
8ddcc35d 1191{
6852be71 1192 RegisterWithCacheManager();
8ddcc35d 1193}
1194
62ee09ca 1195void
84f50787 1196FwdState::RegisterWithCacheManager(void)
62ee09ca 1197{
8822ebee 1198 Mgr::RegisterAction("forward", "Request Forwarding Statistics", fwdStats, 0, 1);
62ee09ca 1199}
1200
b6b6f466 1201void
1202FwdState::logReplyStatus(int tries, http_status status)
8ddcc35d 1203{
1204 if (status > HTTP_INVALID_HEADER)
62e76326 1205 return;
1206
75eb730e 1207 assert(tries >= 0);
62e76326 1208
8ddcc35d 1209 if (tries > MAX_FWD_STATS_IDX)
62e76326 1210 tries = MAX_FWD_STATS_IDX;
1211
8ddcc35d 1212 FwdReplyCodes[tries][status]++;
1213}
1214
b6b6f466 1215/**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
62e76326 1216
057f5854 1217/*
1218 * DPW 2007-05-19
1219 * Formerly static, but now used by client_side_request.cc
1220 */
425de4c8
AJ
1221/// Checks for a TOS value to apply depending on the ACL
1222tos_t
b6b6f466 1223aclMapTOS(acl_tos * head, ACLChecklist * ch)
b6a2f15e 1224{
b6b6f466 1225 acl_tos *l;
62e76326 1226
b6b6f466 1227 for (l = head; l; l = l->next) {
2efeb0b7 1228 if (!l->aclList || ch->fastCheck(l->aclList) == ACCESS_ALLOWED)
b6b6f466 1229 return l->tos;
1230 }
5894ad28 1231
b6b6f466 1232 return 0;
1233}
5894ad28 1234
425de4c8
AJ
1235/// Checks for a netfilter mark value to apply depending on the ACL
1236nfmark_t
1237aclMapNfmark(acl_nfmark * head, ACLChecklist * ch)
1238{
1239 acl_nfmark *l;
1240
1241 for (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.
cfd66529
AJ
1253 if (!conn->local.IsAnyAddr()) return;
1254
07f889c1
AJ
1255 // ensure that at minimum the wildcard local matches remote protocol
1256 if (conn->remote.IsIPv4())
1257 conn->local.SetIPv4();
1258
cfd66529 1259 // maybe use TPROXY client address
b0758e04 1260 if (request && request->flags.spoof_client_ip) {
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);
739b352a 1281 ch.dst_peer = conn->getPeer();
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
cfd66529
AJ
1287 acl_address *l;
1288 for (l = Config.accessList.outgoing_address; l; l = l->next) {
1289
1290 /* check if the outgoing address is usable to the destination */
1291 if (conn->remote.IsIPv4() != l->addr.IsIPv4()) continue;
1292
1293 /* check ACLs for this outgoing address */
2efeb0b7 1294 if (!l->aclList || ch.fastCheck(l->aclList) == ACCESS_ALLOWED) {
cfd66529
AJ
1295 conn->local = l->addr;
1296 return;
1297 }
1298 }
b6b6f466 1299}
62e76326 1300
425de4c8
AJ
1301tos_t
1302GetTosToServer(HttpRequest * request)
1303{
1304 ACLFilledChecklist ch(NULL, request, NULL);
1305
1306 if (request) {
1307 ch.src_addr = request->client_addr;
1308 ch.my_addr = request->my_addr;
1309 }
1310
1311 return aclMapTOS(Ip::Qos::TheConfig.tosToServer, &ch);
1312}
1313
1314nfmark_t
1315GetNfmarkToServer(HttpRequest * request)
b6b6f466 1316{
c0941a6a 1317 ACLFilledChecklist ch(NULL, request, NULL);
62e76326 1318
b6b6f466 1319 if (request) {
1320 ch.src_addr = request->client_addr;
1321 ch.my_addr = request->my_addr;
b6a2f15e 1322 }
62e76326 1323
425de4c8 1324 return aclMapNfmark(Ip::Qos::TheConfig.nfmarkToServer, &ch);
b6a2f15e 1325}