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