]> git.ipfire.org Git - thirdparty/squid.git/blame - src/forward.cc
Correct EUI logging documentation
[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"
aa839030 38#include "CacheManager.h"
39#include "event.h"
40#include "errorpage.h"
41#include "fde.h"
bbaf2685 42#include "hier_code.h"
924f73bc 43#include "HttpReply.h"
aa839030 44#include "HttpRequest.h"
425de4c8 45#include "ip/QosConfig.h"
aa839030 46#include "MemObject.h"
781ce8ff 47#include "pconn.h"
aa839030 48#include "SquidTime.h"
49#include "Store.h"
9b5c4a9a 50#include "icmp/net_db.h"
96d89ea0 51#include "ip/Intercept.h"
407b9bb1 52#include "ip/tools.h"
8822ebee 53#include "mgr/Registration.h"
4db984be
CT
54#if USE_SSL
55#include "ssl/support.h"
4d16918e 56#include "ssl/ErrorDetail.h"
4db984be 57#endif
fc68f6b1 58
b6b6f466 59static PSC fwdStartCompleteWrapper;
60static PF fwdServerClosedWrapper;
61#if USE_SSL
62static PF fwdNegotiateSSLWrapper;
63#endif
64static PF fwdConnectTimeoutWrapper;
65static EVH fwdConnectStartWrapper;
66static CNCB fwdConnectDoneWrapper;
67
8ddcc35d 68static OBJH fwdStats;
b6b6f466 69static void fwdServerFree(FwdServer * fs);
8ddcc35d 70
71#define MAX_FWD_STATS_IDX 9
9977e14b 72static int FwdReplyCodes[MAX_FWD_STATS_IDX + 1][HTTP_INVALID_HEADER + 1];
db1cd23c 73
781ce8ff 74static PconnPool *fwdPconnPool = new PconnPool("server-side");
b6b6f466 75CBDATA_CLASS_INIT(FwdState);
781ce8ff 76
429871db 77void
78FwdState::abort(void* d)
79{
80 FwdState* fwd = (FwdState*)d;
6ecaf21a 81 Pointer tmp = fwd; // Grab a temporary pointer to keep the object alive during our scope.
429871db 82
83 if (fwd->server_fd >= 0) {
84 comm_close(fwd->server_fd);
85 fwd->server_fd = -1;
86 }
87
88 fwd->self = NULL;
89}
90
b6b6f466 91/**** PUBLIC INTERFACE ********************************************************/
c7f9eb6d 92
b6b6f466 93FwdState::FwdState(int fd, StoreEntry * e, HttpRequest * r)
db1cd23c 94{
b6b6f466 95 entry = e;
96 client_fd = fd;
97 server_fd = -1;
6dd9f4bd 98 request = HTTPMSGLOCK(r);
b6b6f466 99 start_t = squid_curtime;
34266cde 100
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);
7a0fb323 118 peerSelect(request, entry, fwdStartCompleteWrapper, this);
429871db 119
fc68f6b1 120 // TODO: set self _after_ the peer is selected because we do not need
7a0fb323 121 // self until we start talking to some Server.
db1cd23c 122}
123
802a8c1d 124void
125FwdState::completed()
41462d93 126{
fc68f6b1 127 if (flags.forward_completed == 1) {
128 debugs(17, 1, HERE << "FwdState::completed called on a completed request! Bad!");
129 return;
130 }
131
132 flags.forward_completed = 1;
802a8c1d 133
bc87dc25 134#if URL_CHECKSUM_DEBUG
62e76326 135
b6b6f466 136 entry->mem_obj->checkUrlChecksum();
225644d7 137#endif
62e76326 138
b6b6f466 139 if (entry->store_status == STORE_PENDING) {
140 if (entry->isEmpty()) {
141 assert(err);
142 errorAppendEntry(entry, err);
143 err = NULL;
62e76326 144 } else {
b6b6f466 145 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
146 entry->complete();
d88e3c49 147 entry->releaseRequest();
62e76326 148 }
f563eea9 149 }
62e76326 150
b6b6f466 151 if (storePendingNClients(entry) > 0)
152 assert(!EBIT_TEST(entry->flags, ENTRY_FWD_HDR_WAIT));
62e76326 153
802a8c1d 154}
155
156FwdState::~FwdState()
157{
158 debugs(17, 3, HERE << "FwdState destructor starting");
fc68f6b1 159
802a8c1d 160 if (! flags.forward_completed)
fc68f6b1 161 completed();
802a8c1d 162
b6b6f466 163 serversFree(&servers);
62e76326 164
9d2760b6
AR
165 doneWithRetries();
166
6dd9f4bd 167 HTTPMSGUNLOCK(request);
62e76326 168
b6b6f466 169 if (err)
170 errorStateFree(err);
62e76326 171
3900307b 172 entry->unregisterAbort();
429871db 173
97b5e68f 174 entry->unlock();
62e76326 175
b6b6f466 176 entry = NULL;
62e76326 177
c2f45110 178 int fd = server_fd;
179
180 if (fd > -1) {
b6b6f466 181 server_fd = -1;
c2f45110 182 comm_remove_close_handler(fd, fwdServerClosedWrapper, this);
bf8fe701 183 debugs(17, 3, "fwdStateFree: closing FD " << fd);
c2f45110 184 comm_close(fd);
b6b6f466 185 }
fc68f6b1 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
b6b6f466 196FwdState::fwdStart(int client_fd, StoreEntry *entry, HttpRequest *request)
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() &&
af6a12ee 205 request->protocol != PROTO_INTERNAL && request->protocol != PROTO_CACHEOBJ) {
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;
b6b6f466 212 int answer = ch.fastCheck();
213
214 if (answer == 0) {
215 err_type page_id;
9ce7856a 216 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, 1);
b6b6f466 217
218 if (page_id == ERR_NONE)
219 page_id = ERR_FORWARDING_DENIED;
220
2cc81f1f 221 ErrorState *anErr = errorCon(page_id, HTTP_FORBIDDEN, request);
b6b6f466 222
223 errorAppendEntry(entry, anErr); // frees anErr
224
be0c6690 225 return;
b6b6f466 226 }
227 }
228
bf8fe701 229 debugs(17, 3, "FwdState::start() '" << entry->url() << "'");
f4ef658f 230 /*
231 * This seems like an odd place to bind mem_obj and request.
232 * Might want to assert that request is NULL at this point
233 */
6dd9f4bd 234 entry->mem_obj->request = HTTPMSGLOCK(request);
b6b6f466 235#if URL_CHECKSUM_DEBUG
236
237 entry->mem_obj->checkUrlChecksum();
238#endif
239
240 if (shutting_down) {
241 /* more yuck */
2cc81f1f 242 ErrorState *anErr = errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE, request);
b6b6f466 243 errorAppendEntry(entry, anErr); // frees anErr
be0c6690 244 return;
6801f8a8 245 }
62e76326 246
b6b6f466 247 switch (request->protocol) {
248
249 case PROTO_INTERNAL:
250 internalStart(request, entry);
be0c6690 251 return;
b6b6f466 252
253 case PROTO_CACHEOBJ:
c83f0bd5 254 CacheManager::GetInstance()->Start(client_fd, request, entry);
be0c6690 255 return;
b6b6f466 256
257 case PROTO_URN:
258 urnStart(request, entry);
be0c6690 259 return;
b6b6f466 260
261 default:
7a0fb323 262 FwdState::Pointer fwd = new FwdState(client_fd, entry, request);
2ad20b4f 263
fc68f6b1 264 /* If we need to transparently proxy the request
cc192b50 265 * then we need the client source protocol, address and port */
26ac0430 266 if (request->flags.spoof_client_ip) {
2ad20b4f 267 fwd->src = request->client_addr;
2ad20b4f 268 }
fc68f6b1 269
7a0fb323 270 fwd->start(fwd);
be0c6690 271 return;
b6b6f466 272 }
273
274 /* NOTREACHED */
41462d93 275}
276
b6b6f466 277void
278FwdState::fail(ErrorState * errorState)
279{
38413773 280 debugs(17, 3, HERE << err_type_str[errorState->type] << " \"" << httpStatusString(errorState->httpStatus) << "\"\n\t" << entry->url() );
b6b6f466 281
282 if (err)
283 errorStateFree(err);
284
285 err = errorState;
286
287 if (!errorState->request)
6dd9f4bd 288 errorState->request = HTTPMSGLOCK(request);
64b66b76
CT
289
290 request->detailError(errorState->type, errorState->xerrno);
b6b6f466 291}
292
38413773 293/**
b6b6f466 294 * Frees fwdState without closing FD or generating an abort
295 */
296void
297FwdState::unregister(int fd)
298{
38413773 299 debugs(17, 3, HERE << entry->url() );
b6b6f466 300 assert(fd == server_fd);
301 assert(fd > -1);
302 comm_remove_close_handler(fd, fwdServerClosedWrapper, this);
303 server_fd = -1;
304}
305
38413773 306/**
b6b6f466 307 * server-side modules call fwdComplete() when they are done
308 * downloading an object. Then, we either 1) re-forward the
309 * request somewhere else if needed, or 2) call storeComplete()
310 * to finish it off
311 */
312void
313FwdState::complete()
314{
315 StoreEntry *e = entry;
316 assert(entry->store_status == STORE_PENDING);
38413773 317 debugs(17, 3, HERE << e->url() << "\n\tstatus " << entry->getReply()->sline.status );
b6b6f466 318#if URL_CHECKSUM_DEBUG
319
320 entry->mem_obj->checkUrlChecksum();
321#endif
322
323 logReplyStatus(n_tries, entry->getReply()->sline.status);
324
325 if (reforward()) {
bf8fe701 326 debugs(17, 3, "fwdComplete: re-forwarding " << entry->getReply()->sline.status << " " << e->url());
b6b6f466 327
328 if (server_fd > -1)
329 unregister(server_fd);
330
3900307b 331 e->reset();
b6b6f466 332
333 startComplete(servers);
334 } else {
4d6c56a6 335 debugs(17, 3, "fwdComplete: server FD " << server_fd << " not re-forwarding status " << entry->getReply()->sline.status);
9e5c22cf
AJ
336 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
337 entry->complete();
fc68f6b1 338
339 if (server_fd < 0)
340 completed();
341
7a0fb323 342 self = NULL; // refcounted
b6b6f466 343 }
344}
345
346
347/**** CALLBACK WRAPPERS ************************************************************/
348
349static void
350fwdStartCompleteWrapper(FwdServer * servers, void *data)
351{
352 FwdState *fwd = (FwdState *) data;
353 fwd->startComplete(servers);
354}
355
356static void
357fwdServerClosedWrapper(int fd, void *data)
358{
359 FwdState *fwd = (FwdState *) data;
360 fwd->serverClosed(fd);
361}
362
363static void
364fwdConnectStartWrapper(void *data)
365{
366 FwdState *fwd = (FwdState *) data;
367 fwd->connectStart();
368}
369
370#if USE_SSL
371static void
372fwdNegotiateSSLWrapper(int fd, void *data)
373{
374 FwdState *fwd = (FwdState *) data;
375 fwd->negotiateSSL(fd);
376}
377
378#endif
379
380static void
3ff65596 381fwdConnectDoneWrapper(int server_fd, const DnsLookupDetails &dns, comm_err_t status, int xerrno, void *data)
b6b6f466 382{
383 FwdState *fwd = (FwdState *) data;
3ff65596 384 fwd->connectDone(server_fd, dns, status, xerrno);
b6b6f466 385}
386
387static void
388fwdConnectTimeoutWrapper(int fd, void *data)
389{
390 FwdState *fwd = (FwdState *) data;
391 fwd->connectTimeout(fd);
392}
393
394/*
395 * Accounts for closed persistent connections
396 */
397static void
398fwdPeerClosed(int fd, void *data)
399{
400 peer *p = (peer *)data;
401 p->stats.conn_open--;
402}
403
404/**** PRIVATE *****************************************************************/
405
545782b8 406/*
407 * FwdState::checkRetry
26ac0430 408 *
545782b8 409 * Return TRUE if the request SHOULD be retried. This method is
410 * called when the HTTP connection fails, or when the connection
411 * is closed before server-side read the end of HTTP headers.
412 */
b6b6f466 413bool
414FwdState::checkRetry()
68bd6892 415{
d8fd0f18 416 if (shutting_down)
b6b6f466 417 return false;
62e76326 418
9d2760b6
AR
419 if (!self) { // we have aborted before the server called us back
420 debugs(17, 5, HERE << "not retrying because of earlier abort");
421 // we will be destroyed when the server clears its Pointer to us
422 return false;
423 }
424
b6b6f466 425 if (entry->store_status != STORE_PENDING)
426 return false;
62e76326 427
b6b6f466 428 if (!entry->isEmpty())
429 return false;
62e76326 430
b6b6f466 431 if (n_tries > 10)
432 return false;
62e76326 433
b6b6f466 434 if (origin_tries > 2)
435 return false;
4ed0e075 436
b6b6f466 437 if (squid_curtime - start_t > Config.Timeout.forward)
438 return false;
62e76326 439
b6b6f466 440 if (flags.dont_retry)
441 return false;
62e76326 442
5d4989a8 443 if (!checkRetriable())
444 return false;
445
58217e94 446 if (request->bodyNibbled())
b6b6f466 447 return false;
62e76326 448
b6b6f466 449 return true;
68bd6892 450}
451
545782b8 452/*
453 * FwdState::checkRetriable
454 *
455 * Return TRUE if this is the kind of request that can be retried
456 * after a failure. If the request is not retriable then we don't
457 * want to risk sending it on a persistent connection. Instead we'll
458 * force it to go on a new HTTP connection.
459 */
b6b6f466 460bool
461FwdState::checkRetriable()
cb928909 462{
463 /* If there is a request body then Squid can only try once
464 * even if the method is indempotent
465 */
466
5f8252d2 467 if (request->body_pipe != NULL)
b6b6f466 468 return false;
cb928909 469
470 /* RFC2616 9.1 Safe and Idempotent Methods */
914b89a2 471 switch (request->method.id()) {
cb928909 472 /* 9.1.1 Safe Methods */
473
474 case METHOD_GET:
475
476 case METHOD_HEAD:
914b89a2 477 /* 9.1.2 Idempotent Methods */
cb928909 478
479 case METHOD_PUT:
480
481 case METHOD_DELETE:
482
483 case METHOD_OPTIONS:
484
485 case METHOD_TRACE:
486 break;
487
488 default:
b6b6f466 489 return false;
cb928909 490 }
491
b6b6f466 492 return true;
cb928909 493}
494
b6b6f466 495void
496FwdState::serverClosed(int fd)
910169e5 497{
bf8fe701 498 debugs(17, 2, "fwdServerClosed: FD " << fd << " " << entry->url());
b6b6f466 499 assert(server_fd == fd);
500 server_fd = -1;
62e76326 501
3e8c047e 502 retryOrBail();
503}
504
505void
26ac0430
AJ
506FwdState::retryOrBail()
507{
b6b6f466 508 if (checkRetry()) {
509 int originserver = (servers->_peer == NULL);
4a7a3d56 510 debugs(17, 3, "fwdServerClosed: re-forwarding (" << n_tries << " tries, " << (squid_curtime - start_t) << " secs)");
62e76326 511
b6b6f466 512 if (servers->next) {
62e76326 513 /* use next, or cycle if origin server isn't last */
b6b6f466 514 FwdServer *fs = servers;
62e76326 515 FwdServer **T, *T2 = NULL;
b6b6f466 516 servers = fs->next;
62e76326 517
3d0ac046 518 for (T = &servers; *T; T2 = *T, T = &(*T)->next);
62e76326 519 if (T2 && T2->_peer) {
520 /* cycle */
521 *T = fs;
522 fs->next = NULL;
523 } else {
524 /* Use next. The last "direct" entry is retried multiple times */
b6b6f466 525 servers = fs->next;
62e76326 526 fwdServerFree(fs);
4ed0e075 527 originserver = 0;
62e76326 528 }
529 }
530
5eee4ac6
AJ
531 /* Ditch error page if it was created before.
532 * A new one will be created if there's another problem */
80cd008f
AR
533 if (err) {
534 errorStateFree(err);
535 err = NULL;
536 }
5eee4ac6 537
4ed0e075 538 /* use eventAdd to break potential call sequence loops and to slow things down a little */
b6b6f466 539 eventAdd("fwdConnectStart", fwdConnectStartWrapper, this, originserver ? 0.05 : 0.005, 0);
62e76326 540
541 return;
d8fd0f18 542 }
62e76326 543
9d2760b6
AR
544 // TODO: should we call completed() here and move doneWithRetries there?
545 doneWithRetries();
546
547 if (self != NULL && !err && shutting_down) {
2cc81f1f 548 errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE, request);
f563eea9 549 }
62e76326 550
b6b6f466 551 self = NULL; // refcounted
910169e5 552}
553
9d2760b6
AR
554// If the Server quits before nibbling at the request body, the body sender
555// will not know (so that we can retry). Call this if we will not retry. We
556// will notify the sender so that it does not get stuck waiting for space.
557void
558FwdState::doneWithRetries()
559{
560 if (request && request->body_pipe != NULL)
561 request->body_pipe->expectNoConsumption();
562}
563
3e8c047e 564// called by the server that failed after calling unregister()
565void
566FwdState::handleUnregisteredServerEnd()
567{
568 debugs(17, 2, "handleUnregisteredServerEnd: self=" << self <<
26ac0430 569 " err=" << err << ' ' << entry->url());
3e8c047e 570 assert(server_fd < 0);
571 retryOrBail();
572}
573
a7ad6e4e 574#if USE_SSL
b6b6f466 575void
576FwdState::negotiateSSL(int fd)
a7ad6e4e 577{
b6b6f466 578 FwdServer *fs = servers;
a7ad6e4e 579 SSL *ssl = fd_table[fd].ssl;
580 int ret;
62e76326 581
a7ad6e4e 582 if ((ret = SSL_connect(ssl)) <= 0) {
62e76326 583 int ssl_error = SSL_get_error(ssl, ret);
584
585 switch (ssl_error) {
586
587 case SSL_ERROR_WANT_READ:
b6b6f466 588 commSetSelect(fd, COMM_SELECT_READ, fwdNegotiateSSLWrapper, this, 0);
62e76326 589 return;
590
591 case SSL_ERROR_WANT_WRITE:
b6b6f466 592 commSetSelect(fd, COMM_SELECT_WRITE, fwdNegotiateSSLWrapper, this, 0);
62e76326 593 return;
594
595 default:
26ac0430
AJ
596 debugs(81, 1, "fwdNegotiateSSL: Error negotiating SSL connection on FD " << fd <<
597 ": " << ERR_error_string(ERR_get_error(), NULL) << " (" << ssl_error <<
598 "/" << ret << "/" << errno << ")");
2ac4f6b5 599 ErrorState *const anErr = makeConnectingError(ERR_SECURE_CONNECT_FAIL);
a7ad6e4e 600#ifdef EPROTO
62e76326 601
b6b6f466 602 anErr->xerrno = EPROTO;
a7ad6e4e 603#else
62e76326 604
b6b6f466 605 anErr->xerrno = EACCES;
a7ad6e4e 606#endif
62e76326 607
4d16918e 608 Ssl::ErrorDetail *errFromFailure = (Ssl::ErrorDetail *)SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail);
e34763f4 609 if (errFromFailure != NULL) {
4d16918e
CT
610 // The errFromFailure is attached to the ssl object
611 // and will be released when ssl object destroyed.
612 // Copy errFromFailure to a new Ssl::ErrorDetail object
613 anErr->detail = new Ssl::ErrorDetail(*errFromFailure);
614 }
615
b6b6f466 616 fail(anErr);
62e76326 617
618 if (fs->_peer) {
619 peerConnectFailed(fs->_peer);
620 fs->_peer->stats.conn_open--;
621 }
622
623 comm_close(fd);
624 return;
625 }
a7ad6e4e 626 }
62e76326 627
f38c5e43 628 if (fs->_peer && !SSL_session_reused(ssl)) {
629 if (fs->_peer->sslSession)
630 SSL_SESSION_free(fs->_peer->sslSession);
631
632 fs->_peer->sslSession = SSL_get1_session(ssl);
633 }
634
b6b6f466 635 dispatch();
a7ad6e4e 636}
637
b6b6f466 638void
639FwdState::initiateSSL()
a7ad6e4e 640{
b6b6f466 641 FwdServer *fs = servers;
642 int fd = server_fd;
a7ad6e4e 643 SSL *ssl;
644 SSL_CTX *sslContext = NULL;
645 peer *peer = fs->_peer;
62e76326 646
a7ad6e4e 647 if (peer) {
62e76326 648 assert(peer->use_ssl);
649 sslContext = peer->sslContext;
a7ad6e4e 650 } else {
62e76326 651 sslContext = Config.ssl_client.sslContext;
a7ad6e4e 652 }
62e76326 653
a7ad6e4e 654 assert(sslContext);
62e76326 655
a7ad6e4e 656 if ((ssl = SSL_new(sslContext)) == NULL) {
bf8fe701 657 debugs(83, 1, "fwdInitiateSSL: Error allocating handle: " << ERR_error_string(ERR_get_error(), NULL) );
2cc81f1f 658 ErrorState *anErr = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
b6b6f466 659 anErr->xerrno = errno;
b6b6f466 660 fail(anErr);
661 self = NULL; // refcounted
62e76326 662 return;
a7ad6e4e 663 }
62e76326 664
a7ad6e4e 665 SSL_set_fd(ssl, fd);
62e76326 666
a7ad6e4e 667 if (peer) {
62e76326 668 if (peer->ssldomain)
669 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->ssldomain);
670
a7ad6e4e 671#if NOT_YET
62e76326 672
673 else if (peer->name)
674 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->name);
675
a7ad6e4e 676#endif
62e76326 677
678 else
679 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->host);
f38c5e43 680
681 if (peer->sslSession)
682 SSL_set_session(ssl, peer->sslSession);
683
a7ad6e4e 684 } else {
cc192b50 685 SSL_set_ex_data(ssl, ssl_ex_index_server, (void*)request->GetHost());
a7ad6e4e 686 }
62e76326 687
4d5a6db8 688 // Create the ACL check list now, while we have access to more info.
689 // The list is used in ssl_verify_cb() and is freed in ssl_free().
690 if (acl_access *acl = Config.ssl_client.cert_error) {
c0941a6a 691 ACLFilledChecklist *check = new ACLFilledChecklist(acl, request, dash_str);
4d5a6db8 692 check->fd(fd);
693 SSL_set_ex_data(ssl, ssl_ex_index_cert_error_check, check);
694 }
695
a7ad6e4e 696 fd_table[fd].ssl = ssl;
697 fd_table[fd].read_method = &ssl_read_method;
698 fd_table[fd].write_method = &ssl_write_method;
b6b6f466 699 negotiateSSL(fd);
a7ad6e4e 700}
62e76326 701
a7ad6e4e 702#endif
703
b6b6f466 704void
3ff65596 705FwdState::connectDone(int aServerFD, const DnsLookupDetails &dns, comm_err_t status, int xerrno)
41462d93 706{
b6b6f466 707 FwdServer *fs = servers;
708 assert(server_fd == aServerFD);
62e76326 709
3ff65596
AR
710 request->recordLookup(dns);
711
beed27a2 712 if (Config.onoff.log_ip_on_direct && status != COMM_ERR_DNS && fs->code == HIER_DIRECT)
74780d33 713 updateHierarchyInfo();
beed27a2 714
41462d93 715 if (status == COMM_ERR_DNS) {
62e76326 716 /*
717 * Only set the dont_retry flag if the DNS lookup fails on
718 * a direct connection. If DNS lookup fails when trying
719 * a neighbor cache, we may want to retry another option.
720 */
721
722 if (NULL == fs->_peer)
b6b6f466 723 flags.dont_retry = 1;
62e76326 724
cc192b50 725 debugs(17, 4, "fwdConnectDone: Unknown host: " << request->GetHost());
62e76326 726
2ac4f6b5 727 ErrorState *const anErr = makeConnectingError(ERR_DNS_FAIL);
62e76326 728
3ff65596 729 anErr->dnsError = dns.error;
62e76326 730
b6b6f466 731 fail(anErr);
62e76326 732
62e76326 733 comm_close(server_fd);
41462d93 734 } else if (status != COMM_OK) {
62e76326 735 assert(fs);
2ac4f6b5 736 ErrorState *const anErr = makeConnectingError(ERR_CONNECT_FAIL);
b6b6f466 737 anErr->xerrno = xerrno;
62e76326 738
b6b6f466 739 fail(anErr);
62e76326 740
a4bd7d1f 741 if (fs->_peer)
62e76326 742 peerConnectFailed(fs->_peer);
62e76326 743
744 comm_close(server_fd);
41462d93 745 } else {
bf8fe701 746 debugs(17, 3, "fwdConnectDone: FD " << server_fd << ": '" << entry->url() << "'" );
62e76326 747
748 if (fs->_peer)
749 peerConnectSucceded(fs->_peer);
750
a7ad6e4e 751#if USE_SSL
62e76326 752
753 if ((fs->_peer && fs->_peer->use_ssl) ||
754 (!fs->_peer && request->protocol == PROTO_HTTPS)) {
b6b6f466 755 initiateSSL();
62e76326 756 return;
757 }
758
a7ad6e4e 759#endif
b6b6f466 760 dispatch();
41462d93 761 }
41462d93 762}
763
b6b6f466 764void
765FwdState::connectTimeout(int fd)
41462d93 766{
beed27a2 767 FwdServer *fs = servers;
768
bf8fe701 769 debugs(17, 2, "fwdConnectTimeout: FD " << fd << ": '" << entry->url() << "'" );
b6b6f466 770 assert(fd == server_fd);
62e76326 771
beed27a2 772 if (Config.onoff.log_ip_on_direct && fs->code == HIER_DIRECT && fd_table[fd].ipaddr[0])
74780d33 773 updateHierarchyInfo();
beed27a2 774
528b2c61 775 if (entry->isEmpty()) {
2cc81f1f 776 ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_GATEWAY_TIMEOUT, request);
b6b6f466 777 anErr->xerrno = ETIMEDOUT;
778 fail(anErr);
62e76326 779 /*
26ac0430 780 * This marks the peer DOWN ...
62e76326 781 */
782
b6b6f466 783 if (servers)
784 if (servers->_peer)
785 peerConnectFailed(servers->_peer);
41462d93 786 }
62e76326 787
41462d93 788 comm_close(fd);
789}
790
b6b6f466 791void
792FwdState::connectStart()
41462d93 793{
3900307b 794 const char *url = entry->url();
cb928909 795 int fd = -1;
b6b6f466 796 FwdServer *fs = servers;
db1cd23c 797 const char *host;
798 unsigned short port;
777831e0 799 int ctimeout;
b6b6f466 800 int ftimeout = Config.Timeout.forward - (squid_curtime - start_t);
62e76326 801
b7ac5457 802 Ip::Address outgoing;
b7ac5457 803 Ip::Address client_addr;
db1cd23c 804 assert(fs);
b6b6f466 805 assert(server_fd == -1);
bf8fe701 806 debugs(17, 3, "fwdConnectStart: " << url);
62e76326 807
3ff65596
AR
808 if (n_tries == 0) // first attempt
809 request->hier.first_conn_start = current_time;
810
29b8d8d6 811 if (fs->_peer) {
62e76326 812 ctimeout = fs->_peer->connect_timeout > 0 ? fs->_peer->connect_timeout
813 : Config.Timeout.peer_connect;
db1cd23c 814 } else {
62e76326 815 ctimeout = Config.Timeout.connect;
db1cd23c 816 }
62e76326 817
b0758e04
AJ
818 if (request->flags.spoof_client_ip) {
819 if (!fs->_peer || !fs->_peer->options.no_tproxy)
820 client_addr = request->client_addr;
821 // else no tproxy today ...
822 }
fc68f6b1 823
777831e0 824 if (ftimeout < 0)
825 ftimeout = 5;
826
827 if (ftimeout < ctimeout)
828 ctimeout = ftimeout;
829
d67acb4e
AJ
830
831 request->flags.pinned = 0;
832 if (fs->code == PINNED) {
26ac0430
AJ
833 ConnStateData *pinned_connection = request->pinnedConnection();
834 assert(pinned_connection);
d67acb4e
AJ
835 fd = pinned_connection->validatePinnedConnection(request, fs->_peer);
836 if (fd >= 0) {
26ac0430 837 pinned_connection->unpinConnection();
d67acb4e
AJ
838#if 0
839 if (!fs->_peer)
840 fs->code = HIER_DIRECT;
841#endif
842 server_fd = fd;
843 n_tries++;
844 request->flags.pinned = 1;
845 if (pinned_connection->pinnedAuth())
846 request->flags.auth = 1;
847 comm_add_close_handler(fd, fwdServerClosedWrapper, this);
60b8c73c 848 updateHierarchyInfo();
3ff65596 849 connectDone(fd, DnsLookupDetails(), COMM_OK, 0);
d67acb4e
AJ
850 return;
851 }
26ac0430 852 /* Failure. Fall back on next path */
60b8c73c 853 debugs(17,2,HERE << " Pinned connection " << pinned_connection << " not valid. Releasing.");
d67acb4e
AJ
854 request->releasePinnedConnection();
855 servers = fs->next;
856 fwdServerFree(fs);
857 connectStart();
858 return;
26ac0430 859 }
d67acb4e 860
af6a12ee 861 if (fs->_peer) {
06093389
AJ
862 host = fs->_peer->host;
863 port = fs->_peer->http_port;
864 fd = fwdPconnPool->pop(fs->_peer->name, fs->_peer->http_port, request->GetHost(), client_addr, checkRetriable());
af6a12ee 865 } else {
06093389
AJ
866 host = request->GetHost();
867 port = request->port;
868 fd = fwdPconnPool->pop(host, port, NULL, client_addr, checkRetriable());
869 }
c8ceec27 870 if (fd >= 0) {
871 debugs(17, 3, "fwdConnectStart: reusing pconn FD " << fd);
872 server_fd = fd;
873 n_tries++;
4ed0e075 874
74780d33 875 if (!fs->_peer)
c8ceec27 876 origin_tries++;
74780d33
AJ
877
878 updateHierarchyInfo();
4ed0e075 879
c8ceec27 880 comm_add_close_handler(fd, fwdServerClosedWrapper, this);
4ed0e075 881
152e24b3
HN
882 // TODO: Avoid this if %<lp is not used? F->local_port is often cached.
883 request->hier.peer_local_port = comm_local_port(fd);
884
c8ceec27 885 dispatch();
4ed0e075 886
c8ceec27 887 return;
41462d93 888 }
62e76326 889
bc87dc25 890#if URL_CHECKSUM_DEBUG
b6b6f466 891 entry->mem_obj->checkUrlChecksum();
bc87dc25 892#endif
62e76326 893
6db78a1a 894 outgoing = getOutgoingAddr(request, fs->_peer);
62e76326 895
407b9bb1
AJ
896 // if IPv6 is disabled try to force IPv4-only outgoing.
897 if (!Ip::EnableIpv6 && !outgoing.SetIPv4()) {
715b5def 898 debugs(50, 4, "fwdConnectStart: IPv6 is Disabled. Cannot connect from " << outgoing);
407b9bb1 899 ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
715b5def 900 anErr->xerrno = EAFNOSUPPORT;
407b9bb1
AJ
901 fail(anErr);
902 self = NULL; // refcounted
903 return;
904 }
905
906 // if IPv6 is split-stack, prefer IPv4
907 if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK) {
908 // NP: This is not a great choice of default,
909 // but with the current Internet being IPv4-majority has a higher success rate.
910 // if setting to IPv4 fails we dont care, that just means to use IPv6 outgoing.
911 outgoing.SetIPv4();
912 }
913
425de4c8 914 tos_t tos = GetTosToServer(request);
d6827718 915
425de4c8
AJ
916#if SO_MARK
917 nfmark_t mark = GetNfmarkToServer(request);
918 debugs(17, 3, "fwdConnectStart: got outgoing addr " << outgoing << ", tos " << int(tos)
ab745b44 919 << ", netfilter mark " << mark);
425de4c8
AJ
920#else
921 nfmark_t mark = 0;
922 debugs(17, 3, "fwdConnectStart: got outgoing addr " << outgoing << ", tos " << int(tos));
923#endif
62e76326 924
56832054 925 int commFlags = COMM_NONBLOCKING;
f165d2fb 926 if (request->flags.spoof_client_ip) {
b0758e04 927 if (!fs->_peer || !fs->_peer->options.no_tproxy)
56832054 928 commFlags |= COMM_TRANSPARENT;
b0758e04 929 // else no tproxy today ...
c303f6e3 930 }
62e76326 931
425de4c8 932 fd = comm_openex(SOCK_STREAM, IPPROTO_TCP, outgoing, commFlags, tos, mark, url);
232820e2 933
cc192b50 934 debugs(17, 3, "fwdConnectStart: got TCP FD " << fd);
935
41462d93 936 if (fd < 0) {
bf8fe701 937 debugs(50, 4, "fwdConnectStart: " << xstrerror());
2cc81f1f 938 ErrorState *anErr = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
b6b6f466 939 anErr->xerrno = errno;
940 fail(anErr);
941 self = NULL; // refcounted
62e76326 942 return;
41462d93 943 }
62e76326 944
b6b6f466 945 server_fd = fd;
946 n_tries++;
4ed0e075 947
948 if (!fs->_peer)
b6b6f466 949 origin_tries++;
4ed0e075 950
152e24b3
HN
951 request->hier.peer_local_port = comm_local_port(fd);
952
c7f9eb6d 953 /*
954 * stats.conn_open is used to account for the number of
955 * connections that we have open to the peer, so we can limit
956 * based on the max-conn option. We need to increment here,
957 * even if the connection may fail.
958 */
62e76326 959
a4bd7d1f 960 if (fs->_peer) {
62e76326 961 fs->_peer->stats.conn_open++;
a4bd7d1f 962 comm_add_close_handler(fd, fwdPeerClosed, fs->_peer);
963 }
62e76326 964
b6b6f466 965 comm_add_close_handler(fd, fwdServerClosedWrapper, this);
62e76326 966
b6b6f466 967 commSetTimeout(fd, ctimeout, fwdConnectTimeoutWrapper, this);
62e76326 968
74780d33 969 updateHierarchyInfo();
b6b6f466 970 commConnectStart(fd, host, port, fwdConnectDoneWrapper, this);
41462d93 971}
972
b6b6f466 973void
974FwdState::startComplete(FwdServer * theServers)
41462d93 975{
bf8fe701 976 debugs(17, 3, "fwdStartComplete: " << entry->url() );
62e76326 977
b6b6f466 978 if (theServers != NULL) {
979 servers = theServers;
980 connectStart();
41462d93 981 } else {
b6b6f466 982 startFail();
41462d93 983 }
41462d93 984}
985
b6b6f466 986void
987FwdState::startFail()
41462d93 988{
bf8fe701 989 debugs(17, 3, "fwdStartFail: " << entry->url() );
2cc81f1f 990 ErrorState *anErr = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE, request);
b6b6f466 991 anErr->xerrno = errno;
992 fail(anErr);
993 self = NULL; // refcounted
41462d93 994}
910169e5 995
b6b6f466 996void
997FwdState::dispatch()
41462d93 998{
c7f9eb6d 999 peer *p = NULL;
60745f24 1000 debugs(17, 3, "fwdDispatch: FD " << client_fd << ": Fetching '" << RequestMethodStr(request->method) << " " << entry->url() << "'" );
e0ebe27c 1001 /*
1002 * Assert that server_fd is set. This is to guarantee that fwdState
1003 * is attached to something and will be deallocated when server_fd
1004 * is closed.
1005 */
a7ad6e4e 1006 assert(server_fd > -1);
62e76326 1007
3900307b 1008 fd_note(server_fd, entry->url());
62e76326 1009
781ce8ff 1010 fd_table[server_fd].noteUse(fwdPconnPool);
62e76326 1011
a7ad6e4e 1012 /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
1013 assert(entry->ping_status != PING_WAITING);
62e76326 1014
a7ad6e4e 1015 assert(entry->lock_count);
62e76326 1016
a7ad6e4e 1017 EBIT_SET(entry->flags, ENTRY_DISPATCHED);
62e76326 1018
cc192b50 1019 netdbPingSite(request->GetHost());
62e76326 1020
425de4c8
AJ
1021 /* Update server side TOS and Netfilter mark if using persistent connections. */
1022 if (Config.onoff.server_pconns) {
1023 if (Ip::Qos::TheConfig.isAclTosActive()) {
1024 tos_t tos = GetTosToServer(request);
1025 Ip::Qos::setSockTos(server_fd, tos);
1026 }
1027#if SO_MARK
1028 if (Ip::Qos::TheConfig.isAclNfmarkActive()) {
1029 nfmark_t mark = GetNfmarkToServer(request);
1030 Ip::Qos::setSockNfmark(server_fd, mark);
1031 }
1032#endif
1033 }
2201bec6 1034
425de4c8 1035 /* Retrieves remote server TOS or MARK value, and stores it as part of the
7172612f 1036 * original client request FD object. It is later used to forward
425de4c8 1037 * remote server's TOS/MARK in the response to the client in case of a MISS.
7172612f 1038 */
425de4c8
AJ
1039 if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
1040 fde * clientFde = &fd_table[client_fd];
1041 fde * servFde = &fd_table[server_fd];
1042 if (clientFde && servFde) {
1043 /* Get the netfilter mark for the connection */
1044 Ip::Qos::getNfmarkFromServer(server_fd, servFde, clientFde);
1045 }
1046 }
1047
1048#if _SQUID_LINUX_
1049 /* Bug 2537: The TOS forward part of QOS only applies to patched Linux kernels. */
1050 if (Ip::Qos::TheConfig.isHitTosActive()) {
1051 fde * clientFde = &fd_table[client_fd];
1052 if (clientFde) {
1053 /* Get the TOS value for the packet */
1054 Ip::Qos::getTosFromServer(server_fd, clientFde);
7172612f 1055 }
26ac0430 1056 }
7172612f
AJ
1057#endif
1058
b6b6f466 1059 if (servers && (p = servers->_peer)) {
62e76326 1060 p->stats.fetches++;
b6b6f466 1061 request->peer_login = p->login;
1062 request->peer_domain = p->domain;
1063 httpStart(this);
41462d93 1064 } else {
b6b6f466 1065 request->peer_login = NULL;
1066 request->peer_domain = NULL;
62e76326 1067
1068 switch (request->protocol) {
a7ad6e4e 1069#if USE_SSL
62e76326 1070
1071 case PROTO_HTTPS:
b6b6f466 1072 httpStart(this);
62e76326 1073 break;
a7ad6e4e 1074#endif
62e76326 1075
1076 case PROTO_HTTP:
b6b6f466 1077 httpStart(this);
62e76326 1078 break;
1079
1080 case PROTO_GOPHER:
b6b6f466 1081 gopherStart(this);
62e76326 1082 break;
1083
1084 case PROTO_FTP:
b6b6f466 1085 ftpStart(this);
62e76326 1086 break;
1087
62e76326 1088 case PROTO_CACHEOBJ:
1089
1090 case PROTO_INTERNAL:
1091
1092 case PROTO_URN:
1093 fatal_dump("Should never get here");
1094 break;
1095
1096 case PROTO_WHOIS:
b6b6f466 1097 whoisStart(this);
62e76326 1098 break;
1099
db80e881 1100 case PROTO_WAIS: /* Not implemented */
fc68f6b1 1101
62e76326 1102 default:
bf8fe701 1103 debugs(17, 1, "fwdDispatch: Cannot retrieve '" << entry->url() << "'" );
2cc81f1f 1104 ErrorState *anErr = errorCon(ERR_UNSUP_REQ, HTTP_BAD_REQUEST, request);
b6b6f466 1105 fail(anErr);
62e76326 1106 /*
1107 * Force a persistent connection to be closed because
1108 * some Netscape browsers have a bug that sends CONNECT
1109 * requests as GET's over persistent connections.
1110 */
1111 request->flags.proxy_keepalive = 0;
1112 /*
1113 * Set the dont_retry flag becuase this is not a
1114 * transient (network) error; its a bug.
1115 */
b6b6f466 1116 flags.dont_retry = 1;
1117 comm_close(server_fd);
62e76326 1118 break;
1119 }
41462d93 1120 }
1121}
1122
545782b8 1123/*
1124 * FwdState::reforward
1125 *
1126 * returns TRUE if the transaction SHOULD be re-forwarded to the
1127 * next choice in the FwdServers list. This method is called when
1128 * server-side communication completes normally, or experiences
1129 * some error after receiving the end of HTTP headers.
1130 */
b6b6f466 1131int
1132FwdState::reforward()
db1cd23c 1133{
b6b6f466 1134 StoreEntry *e = entry;
1135 FwdServer *fs = servers;
db1cd23c 1136 http_status s;
1137 assert(e->store_status == STORE_PENDING);
1138 assert(e->mem_obj);
bc87dc25 1139#if URL_CHECKSUM_DEBUG
62e76326 1140
528b2c61 1141 e->mem_obj->checkUrlChecksum();
bc87dc25 1142#endif
62e76326 1143
bf8fe701 1144 debugs(17, 3, "fwdReforward: " << e->url() << "?" );
62e76326 1145
d6eb18d6 1146 if (!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) {
bf8fe701 1147 debugs(17, 3, "fwdReforward: No, ENTRY_FWD_HDR_WAIT isn't set");
62e76326 1148 return 0;
d6eb18d6 1149 }
62e76326 1150
437823b4 1151 if (n_tries > Config.forward_max_tries)
62e76326 1152 return 0;
1153
b6b6f466 1154 if (origin_tries > 1)
4ed0e075 1155 return 0;
1156
58217e94 1157 if (request->bodyNibbled())
62e76326 1158 return 0;
1159
db1cd23c 1160 assert(fs);
62e76326 1161
b6b6f466 1162 servers = fs->next;
62e76326 1163
db1cd23c 1164 fwdServerFree(fs);
62e76326 1165
b6b6f466 1166 if (servers == NULL) {
bf8fe701 1167 debugs(17, 3, "fwdReforward: No forward-servers left");
62e76326 1168 return 0;
db1cd23c 1169 }
62e76326 1170
528b2c61 1171 s = e->getReply()->sline.status;
4a7a3d56 1172 debugs(17, 3, "fwdReforward: status " << s);
b6b6f466 1173 return reforwardableStatus(s);
db1cd23c 1174}
1175
2ac4f6b5
AR
1176/**
1177 * Create "503 Service Unavailable" or "504 Gateway Timeout" error depending
1178 * on whether this is a validation request. RFC 2616 says that we MUST reply
1179 * with "504 Gateway Timeout" if validation fails and cached reply has
1180 * proxy-revalidate, must-revalidate or s-maxage Cache-Control directive.
1181 */
1182ErrorState *
1183FwdState::makeConnectingError(const err_type type) const
1184{
1185 return errorCon(type, request->flags.need_validation ?
1186 HTTP_GATEWAY_TIMEOUT : HTTP_SERVICE_UNAVAILABLE, request);
1187}
1188
b6b6f466 1189static void
1190fwdStats(StoreEntry * s)
64d8034e 1191{
b6b6f466 1192 int i;
1193 int j;
1194 storeAppendPrintf(s, "Status");
62e76326 1195
b6b6f466 1196 for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
1197 storeAppendPrintf(s, "\ttry#%d", j + 1);
64d8034e 1198 }
64d8034e 1199
b6b6f466 1200 storeAppendPrintf(s, "\n");
0185bd6f 1201
b6b6f466 1202 for (i = 0; i <= (int) HTTP_INVALID_HEADER; i++) {
1203 if (FwdReplyCodes[0][i] == 0)
1204 continue;
0185bd6f 1205
b6b6f466 1206 storeAppendPrintf(s, "%3d", i);
0185bd6f 1207
b6b6f466 1208 for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
1209 storeAppendPrintf(s, "\t%d", FwdReplyCodes[j][i]);
62e76326 1210 }
62e76326 1211
b6b6f466 1212 storeAppendPrintf(s, "\n");
e0ebe27c 1213 }
7197b20d 1214}
1215
a4bd7d1f 1216
b6b6f466 1217/**** STATIC MEMBER FUNCTIONS *************************************************/
db1cd23c 1218
b6b6f466 1219bool
1220FwdState::reforwardableStatus(http_status s)
db1cd23c 1221{
b6b6f466 1222 switch (s) {
62e76326 1223
b6b6f466 1224 case HTTP_BAD_GATEWAY:
62e76326 1225
b6b6f466 1226 case HTTP_GATEWAY_TIMEOUT:
1227 return true;
62e76326 1228
b6b6f466 1229 case HTTP_FORBIDDEN:
62e76326 1230
b6b6f466 1231 case HTTP_INTERNAL_SERVER_ERROR:
62e76326 1232
b6b6f466 1233 case HTTP_NOT_IMPLEMENTED:
62e76326 1234
b6b6f466 1235 case HTTP_SERVICE_UNAVAILABLE:
1236 return Config.retry.onerror;
62e76326 1237
b6b6f466 1238 default:
1239 return false;
db1cd23c 1240 }
b6b6f466 1241
1242 /* NOTREACHED */
db1cd23c 1243}
8ddcc35d 1244
06093389
AJ
1245/**
1246 * Decide where details need to be gathered to correctly describe a persistent connection.
1247 * What is needed:
d85b8894
AJ
1248 * - host name of server at other end of this link (either peer or requested host)
1249 * - port to which we connected the other end of this link (for peer or request)
1250 * - domain for which the connection is supposed to be used
1251 * - address of the client for which we made the connection
06093389 1252 */
781ce8ff 1253void
b7ac5457 1254FwdState::pconnPush(int fd, const peer *_peer, const HttpRequest *req, const char *domain, Ip::Address &client_addr)
781ce8ff 1255{
06093389
AJ
1256 if (_peer) {
1257 fwdPconnPool->push(fd, _peer->name, _peer->http_port, domain, client_addr);
1258 } else {
1259 /* small performance improvement, using NULL for domain instead of listing it twice */
1260 /* although this will leave a gap open for url-rewritten domains to share a link */
1261 fwdPconnPool->push(fd, req->GetHost(), req->port, NULL, client_addr);
1262 }
781ce8ff 1263}
1264
8ddcc35d 1265void
b6b6f466 1266FwdState::initModule()
8ddcc35d 1267{
b6b6f466 1268 memDataInit(MEM_FWD_SERVER, "FwdServer", sizeof(FwdServer), 0);
6852be71 1269 RegisterWithCacheManager();
8ddcc35d 1270}
1271
62ee09ca 1272void
84f50787 1273FwdState::RegisterWithCacheManager(void)
62ee09ca 1274{
8822ebee 1275 Mgr::RegisterAction("forward", "Request Forwarding Statistics", fwdStats, 0, 1);
62ee09ca 1276}
1277
b6b6f466 1278void
1279FwdState::logReplyStatus(int tries, http_status status)
8ddcc35d 1280{
1281 if (status > HTTP_INVALID_HEADER)
62e76326 1282 return;
1283
8ddcc35d 1284 assert(tries);
62e76326 1285
8ddcc35d 1286 tries--;
62e76326 1287
8ddcc35d 1288 if (tries > MAX_FWD_STATS_IDX)
62e76326 1289 tries = MAX_FWD_STATS_IDX;
1290
8ddcc35d 1291 FwdReplyCodes[tries][status]++;
1292}
1293
b6b6f466 1294void
1295FwdState::serversFree(FwdServer ** FSVR)
8ddcc35d 1296{
b6b6f466 1297 FwdServer *fs;
62e76326 1298
b6b6f466 1299 while ((fs = *FSVR)) {
1300 *FSVR = fs->next;
1301 fwdServerFree(fs);
9977e14b 1302 }
b6b6f466 1303}
62e76326 1304
93280d3c
AR
1305/** From Comment #5 by Henrik Nordstrom made at
1306http://www.squid-cache.org/bugs/show_bug.cgi?id=2391 on 2008-09-19
1307
1308updateHierarchyInfo should be called each time a new path has been
1309selected or when more information about the path is available (i.e. the
1310server IP), and when it's called it needs to be given reasonable
1311arguments describing the now selected path..
1312
1313It does not matter from a functional perspective if it gets called a few
1314times more than what is really needed, but calling it too often may
1315obviously hurt performance.
93280d3c 1316*/
74780d33
AJ
1317// updates HierarchyLogEntry, guessing nextHop and its format
1318void
1319FwdState::updateHierarchyInfo()
1320{
1321 assert(request);
1322
1323 FwdServer *fs = servers;
1324 assert(fs);
1325
74780d33
AJ
1326 const char *nextHop = NULL;
1327
dc029c2f 1328 if (fs->_peer) {
ed9f056a
HN
1329 // went to peer, log peer host name
1330 nextHop = fs->_peer->name;
74780d33
AJ
1331 } else {
1332 // went DIRECT, must honor log_ip_on_direct
1333
1334 // XXX: or should we use request->host_addr here? how?
1335 assert(server_fd >= 0);
1336 nextHop = fd_table[server_fd].ipaddr;
1337 if (!Config.onoff.log_ip_on_direct || !nextHop[0])
1338 nextHop = request->GetHost(); // domain name
dc029c2f 1339 }
74780d33
AJ
1340
1341 assert(nextHop);
1342 hierarchyNote(&request->hier, fs->code, nextHop);
1343}
1344
1345
b6b6f466 1346/**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
62e76326 1347
b6b6f466 1348static void
1349fwdServerFree(FwdServer * fs)
1350{
1351 cbdataReferenceDone(fs->_peer);
1352 memFree(fs, MEM_FWD_SERVER);
1353}
62e76326 1354
b7ac5457 1355static Ip::Address
cc192b50 1356aclMapAddr(acl_address * head, ACLChecklist * ch)
b6b6f466 1357{
1358 acl_address *l;
62e76326 1359
b7ac5457 1360 Ip::Address addr;
62e76326 1361
26ac0430 1362 for (l = head; l; l = l->next) {
b50e327b 1363 if (!l->aclList || ch->matchAclListFast(l->aclList))
b6b6f466 1364 return l->addr;
9977e14b 1365 }
b6b6f466 1366
cc192b50 1367 addr.SetAnyAddr();
b6b6f466 1368 return addr;
8ddcc35d 1369}
b6a2f15e 1370
057f5854 1371/*
1372 * DPW 2007-05-19
1373 * Formerly static, but now used by client_side_request.cc
1374 */
425de4c8
AJ
1375/// Checks for a TOS value to apply depending on the ACL
1376tos_t
b6b6f466 1377aclMapTOS(acl_tos * head, ACLChecklist * ch)
b6a2f15e 1378{
b6b6f466 1379 acl_tos *l;
62e76326 1380
b6b6f466 1381 for (l = head; l; l = l->next) {
b50e327b 1382 if (!l->aclList || ch->matchAclListFast(l->aclList))
b6b6f466 1383 return l->tos;
1384 }
5894ad28 1385
b6b6f466 1386 return 0;
1387}
5894ad28 1388
425de4c8
AJ
1389/// Checks for a netfilter mark value to apply depending on the ACL
1390nfmark_t
1391aclMapNfmark(acl_nfmark * head, ACLChecklist * ch)
1392{
1393 acl_nfmark *l;
1394
1395 for (l = head; l; l = l->next) {
1396 if (!l->aclList || ch->matchAclListFast(l->aclList))
1397 return l->nfmark;
1398 }
1399
1400 return 0;
1401}
1402
b7ac5457 1403Ip::Address
6db78a1a 1404getOutgoingAddr(HttpRequest * request, struct peer *dst_peer)
b6b6f466 1405{
b0758e04 1406 if (request && request->flags.spoof_client_ip) {
96d64448
AJ
1407 if (!dst_peer || !dst_peer->options.no_tproxy) {
1408#if FOLLOW_X_FORWARDED_FOR && LINUX_NETFILTER
1409 if (Config.onoff.tproxy_uses_indirect_client)
1410 return request->indirect_client_addr;
1411 else
1412#endif
656a1484 1413 return request->client_addr;
96d64448 1414 }
b0758e04
AJ
1415 // else no tproxy today ...
1416 }
c303f6e3 1417
b50e327b 1418 if (!Config.accessList.outgoing_address) {
b7ac5457 1419 return Ip::Address(); // anything will do.
b50e327b
AJ
1420 }
1421
c0941a6a 1422 ACLFilledChecklist ch(NULL, request, NULL);
6db78a1a
AJ
1423 ch.dst_peer = dst_peer;
1424
26ac0430 1425 if (request) {
9aaf363a
AJ
1426#if FOLLOW_X_FORWARDED_FOR
1427 if (Config.onoff.acl_uses_indirect_client)
1428 ch.src_addr = request->indirect_client_addr;
1429 else
1430#endif
f892cbd2 1431 ch.src_addr = request->client_addr;
b6b6f466 1432 ch.my_addr = request->my_addr;
b6b6f466 1433 }
62e76326 1434
b6b6f466 1435 return aclMapAddr(Config.accessList.outgoing_address, &ch);
1436}
62e76326 1437
425de4c8
AJ
1438tos_t
1439GetTosToServer(HttpRequest * request)
1440{
1441 ACLFilledChecklist ch(NULL, request, NULL);
1442
1443 if (request) {
1444 ch.src_addr = request->client_addr;
1445 ch.my_addr = request->my_addr;
1446 }
1447
1448 return aclMapTOS(Ip::Qos::TheConfig.tosToServer, &ch);
1449}
1450
1451nfmark_t
1452GetNfmarkToServer(HttpRequest * request)
b6b6f466 1453{
c0941a6a 1454 ACLFilledChecklist ch(NULL, request, NULL);
62e76326 1455
b6b6f466 1456 if (request) {
1457 ch.src_addr = request->client_addr;
1458 ch.my_addr = request->my_addr;
b6a2f15e 1459 }
62e76326 1460
425de4c8 1461 return aclMapNfmark(Ip::Qos::TheConfig.nfmarkToServer, &ch);
b6a2f15e 1462}