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