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