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