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