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