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