]> git.ipfire.org Git - thirdparty/squid.git/blob - src/FwdState.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / FwdState.cc
1 /*
2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /* DEBUG: section 17 Request Forwarding */
10
11 #include "squid.h"
12 #include "AccessLogEntry.h"
13 #include "acl/Address.h"
14 #include "acl/FilledChecklist.h"
15 #include "acl/Gadgets.h"
16 #include "anyp/PortCfg.h"
17 #include "CacheManager.h"
18 #include "CachePeer.h"
19 #include "client_side.h"
20 #include "clients/forward.h"
21 #include "comm/Connection.h"
22 #include "comm/ConnOpener.h"
23 #include "comm/Loops.h"
24 #include "CommCalls.h"
25 #include "errorpage.h"
26 #include "event.h"
27 #include "fd.h"
28 #include "fde.h"
29 #include "FwdState.h"
30 #include "globals.h"
31 #include "gopher.h"
32 #include "hier_code.h"
33 #include "http.h"
34 #include "http/Stream.h"
35 #include "HttpReply.h"
36 #include "HttpRequest.h"
37 #include "icmp/net_db.h"
38 #include "internal.h"
39 #include "ip/Intercept.h"
40 #include "ip/QosConfig.h"
41 #include "ip/tools.h"
42 #include "MemObject.h"
43 #include "mgr/Registration.h"
44 #include "neighbors.h"
45 #include "pconn.h"
46 #include "PeerPoolMgr.h"
47 #include "PeerSelectState.h"
48 #include "security/BlindPeerConnector.h"
49 #include "SquidConfig.h"
50 #include "SquidTime.h"
51 #include "ssl/PeekingPeerConnector.h"
52 #include "Store.h"
53 #include "StoreClient.h"
54 #include "urn.h"
55 #include "whois.h"
56 #if USE_OPENSSL
57 #include "ssl/cert_validate_message.h"
58 #include "ssl/Config.h"
59 #include "ssl/ErrorDetail.h"
60 #include "ssl/helper.h"
61 #include "ssl/ServerBump.h"
62 #include "ssl/support.h"
63 #else
64 #include "security/EncryptorAnswer.h"
65 #endif
66
67 #include <cerrno>
68
69 static PSC fwdPeerSelectionCompleteWrapper;
70 static CLCB fwdServerClosedWrapper;
71 static CNCB fwdConnectDoneWrapper;
72
73 static OBJH fwdStats;
74
75 #define MAX_FWD_STATS_IDX 9
76 static int FwdReplyCodes[MAX_FWD_STATS_IDX + 1][Http::scInvalidHeader + 1];
77
78 static PconnPool *fwdPconnPool = new PconnPool("server-peers", NULL);
79 CBDATA_CLASS_INIT(FwdState);
80
81 class FwdStatePeerAnswerDialer: public CallDialer, public Security::PeerConnector::CbDialer
82 {
83 public:
84 typedef void (FwdState::*Method)(Security::EncryptorAnswer &);
85
86 FwdStatePeerAnswerDialer(Method method, FwdState *fwd):
87 method_(method), fwd_(fwd), answer_() {}
88
89 /* CallDialer API */
90 virtual bool canDial(AsyncCall &call) { return fwd_.valid(); }
91 void dial(AsyncCall &call) { ((&(*fwd_))->*method_)(answer_); }
92 virtual void print(std::ostream &os) const {
93 os << '(' << fwd_.get() << ", " << answer_ << ')';
94 }
95
96 /* Security::PeerConnector::CbDialer API */
97 virtual Security::EncryptorAnswer &answer() { return answer_; }
98
99 private:
100 Method method_;
101 CbcPointer<FwdState> fwd_;
102 Security::EncryptorAnswer answer_;
103 };
104
105 void
106 FwdState::abort(void* d)
107 {
108 FwdState* fwd = (FwdState*)d;
109 Pointer tmp = fwd; // Grab a temporary pointer to keep the object alive during our scope.
110
111 if (Comm::IsConnOpen(fwd->serverConnection())) {
112 fwd->closeServerConnection("store entry aborted");
113 } else {
114 debugs(17, 7, HERE << "store entry aborted; no connection to close");
115 }
116 fwd->serverDestinations.clear();
117 fwd->self = NULL;
118 }
119
120 void
121 FwdState::closeServerConnection(const char *reason)
122 {
123 debugs(17, 3, "because " << reason << "; " << serverConn);
124 comm_remove_close_handler(serverConn->fd, closeHandler);
125 closeHandler = NULL;
126 fwdPconnPool->noteUses(fd_table[serverConn->fd].pconn.uses);
127 serverConn->close();
128 }
129
130 /**** PUBLIC INTERFACE ********************************************************/
131
132 FwdState::FwdState(const Comm::ConnectionPointer &client, StoreEntry * e, HttpRequest * r, const AccessLogEntryPointer &alp):
133 entry(e),
134 request(r),
135 al(alp),
136 err(NULL),
137 clientConn(client),
138 start_t(squid_curtime),
139 n_tries(0),
140 pconnRace(raceImpossible)
141 {
142 debugs(17, 2, "Forwarding client request " << client << ", url=" << e->url());
143 HTTPMSGLOCK(request);
144 serverDestinations.reserve(Config.forward_max_tries);
145 e->lock("FwdState");
146 EBIT_SET(e->flags, ENTRY_FWD_HDR_WAIT);
147 flags.connected_okay = false;
148 flags.dont_retry = false;
149 flags.forward_completed = false;
150 debugs(17, 3, "FwdState constructed, this=" << this);
151 }
152
153 // Called once, right after object creation, when it is safe to set self
154 void FwdState::start(Pointer aSelf)
155 {
156 // Protect ourselves from being destroyed when the only Server pointing
157 // to us is gone (while we expect to talk to more Servers later).
158 // Once we set self, we are responsible for clearing it when we do not
159 // expect to talk to any servers.
160 self = aSelf; // refcounted
161
162 // We hope that either the store entry aborts or peer is selected.
163 // Otherwise we are going to leak our object.
164
165 // Ftp::Relay needs to preserve control connection on data aborts
166 // so it registers its own abort handler that calls ours when needed.
167 if (!request->flags.ftpNative)
168 entry->registerAbort(FwdState::abort, this);
169
170 #if STRICT_ORIGINAL_DST
171 // Bug 3243: CVE 2009-0801
172 // Bypass of browser same-origin access control in intercepted communication
173 // To resolve this we must force DIRECT and only to the original client destination.
174 const bool isIntercepted = request && !request->flags.redirected && (request->flags.intercepted || request->flags.interceptTproxy);
175 const bool useOriginalDst = Config.onoff.client_dst_passthru || (request && !request->flags.hostVerified);
176 if (isIntercepted && useOriginalDst) {
177 selectPeerForIntercepted();
178 // 3.2 does not suppro re-wrapping inside CONNECT.
179 // our only alternative is to fake destination "found" and continue with the forwarding.
180 startConnectionOrFail();
181 return;
182 }
183 #endif
184
185 // do full route options selection
186 peerSelect(&serverDestinations, request, al, entry, fwdPeerSelectionCompleteWrapper, this);
187 }
188
189 #if STRICT_ORIGINAL_DST
190 /// bypasses peerSelect() when dealing with intercepted requests
191 void
192 FwdState::selectPeerForIntercepted()
193 {
194 // use pinned connection if available
195 Comm::ConnectionPointer p;
196 if (ConnStateData *client = request->pinnedConnection()) {
197 p = client->validatePinnedConnection(request, NULL);
198 if (Comm::IsConnOpen(p)) {
199 /* duplicate peerSelectPinned() effects */
200 p->peerType = PINNED;
201 entry->ping_status = PING_DONE; /* Skip ICP */
202
203 debugs(17, 3, "reusing a pinned conn: " << *p);
204 serverDestinations.push_back(p);
205 } else {
206 debugs(17,2, "Pinned connection is not valid: " << p);
207 ErrorState *anErr = new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, request);
208 fail(anErr);
209 }
210 // Either use the valid pinned connection or fail if it is invalid.
211 return;
212 }
213
214 // use client original destination as second preferred choice
215 p = new Comm::Connection();
216 p->peerType = ORIGINAL_DST;
217 p->remote = clientConn->local;
218 getOutgoingAddress(request, p);
219
220 debugs(17, 3, HERE << "using client original destination: " << *p);
221 serverDestinations.push_back(p);
222 }
223 #endif
224
225 void
226 FwdState::completed()
227 {
228 if (flags.forward_completed) {
229 debugs(17, DBG_IMPORTANT, HERE << "FwdState::completed called on a completed request! Bad!");
230 return;
231 }
232
233 flags.forward_completed = true;
234
235 request->hier.stopPeerClock(false);
236
237 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
238 debugs(17, 3, HERE << "entry aborted");
239 return ;
240 }
241
242 #if URL_CHECKSUM_DEBUG
243
244 entry->mem_obj->checkUrlChecksum();
245 #endif
246
247 if (entry->store_status == STORE_PENDING) {
248 if (entry->isEmpty()) {
249 if (!err) // we quit (e.g., fd closed) before an error or content
250 fail(new ErrorState(ERR_READ_ERROR, Http::scBadGateway, request));
251 assert(err);
252 errorAppendEntry(entry, err);
253 err = NULL;
254 #if USE_OPENSSL
255 if (request->flags.sslPeek && request->clientConnectionManager.valid()) {
256 CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
257 ConnStateData::httpsPeeked, Comm::ConnectionPointer(NULL));
258 }
259 #endif
260 } else {
261 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
262 entry->complete();
263 entry->releaseRequest();
264 }
265 }
266
267 if (storePendingNClients(entry) > 0)
268 assert(!EBIT_TEST(entry->flags, ENTRY_FWD_HDR_WAIT));
269
270 }
271
272 FwdState::~FwdState()
273 {
274 debugs(17, 3, "FwdState destructor start");
275
276 if (! flags.forward_completed)
277 completed();
278
279 doneWithRetries();
280
281 HTTPMSGUNLOCK(request);
282
283 delete err;
284
285 entry->unregisterAbort();
286
287 entry->unlock("FwdState");
288
289 entry = NULL;
290
291 if (calls.connector != NULL) {
292 calls.connector->cancel("FwdState destructed");
293 calls.connector = NULL;
294 }
295
296 if (Comm::IsConnOpen(serverConn))
297 closeServerConnection("~FwdState");
298
299 serverDestinations.clear();
300
301 debugs(17, 3, "FwdState destructed, this=" << this);
302 }
303
304 /**
305 * This is the entry point for client-side to start forwarding
306 * a transaction. It is a static method that may or may not
307 * allocate a FwdState.
308 */
309 void
310 FwdState::Start(const Comm::ConnectionPointer &clientConn, StoreEntry *entry, HttpRequest *request, const AccessLogEntryPointer &al)
311 {
312 /** \note
313 * client_addr == no_addr indicates this is an "internal" request
314 * from peer_digest.c, asn.c, netdb.c, etc and should always
315 * be allowed. yuck, I know.
316 */
317
318 if ( Config.accessList.miss && !request->client_addr.isNoAddr() &&
319 !request->flags.internal && request->url.getScheme() != AnyP::PROTO_CACHE_OBJECT) {
320 /**
321 * Check if this host is allowed to fetch MISSES from us (miss_access).
322 * Intentionally replace the src_addr automatically selected by the checklist code
323 * we do NOT want the indirect client address to be tested here.
324 */
325 ACLFilledChecklist ch(Config.accessList.miss, request, NULL);
326 ch.src_addr = request->client_addr;
327 if (ch.fastCheck() == ACCESS_DENIED) {
328 err_type page_id;
329 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, 1);
330
331 if (page_id == ERR_NONE)
332 page_id = ERR_FORWARDING_DENIED;
333
334 ErrorState *anErr = new ErrorState(page_id, Http::scForbidden, request);
335 errorAppendEntry(entry, anErr); // frees anErr
336 return;
337 }
338 }
339
340 debugs(17, 3, HERE << "'" << entry->url() << "'");
341 /*
342 * This seems like an odd place to bind mem_obj and request.
343 * Might want to assert that request is NULL at this point
344 */
345 entry->mem_obj->request = request;
346 HTTPMSGLOCK(entry->mem_obj->request);
347 #if URL_CHECKSUM_DEBUG
348
349 entry->mem_obj->checkUrlChecksum();
350 #endif
351
352 if (shutting_down) {
353 /* more yuck */
354 ErrorState *anErr = new ErrorState(ERR_SHUTTING_DOWN, Http::scServiceUnavailable, request);
355 errorAppendEntry(entry, anErr); // frees anErr
356 return;
357 }
358
359 if (request->flags.internal) {
360 debugs(17, 2, "calling internalStart() due to request flag");
361 internalStart(clientConn, request, entry);
362 return;
363 }
364
365 switch (request->url.getScheme()) {
366
367 case AnyP::PROTO_CACHE_OBJECT:
368 debugs(17, 2, "calling CacheManager due to request scheme " << request->url.getScheme());
369 CacheManager::GetInstance()->Start(clientConn, request, entry);
370 return;
371
372 case AnyP::PROTO_URN:
373 urnStart(request, entry);
374 return;
375
376 default:
377 FwdState::Pointer fwd = new FwdState(clientConn, entry, request, al);
378 fwd->start(fwd);
379 return;
380 }
381
382 /* NOTREACHED */
383 }
384
385 void
386 FwdState::fwdStart(const Comm::ConnectionPointer &clientConn, StoreEntry *entry, HttpRequest *request)
387 {
388 // Hides AccessLogEntry.h from code that does not supply ALE anyway.
389 Start(clientConn, entry, request, NULL);
390 }
391
392 void
393 FwdState::startConnectionOrFail()
394 {
395 debugs(17, 3, HERE << entry->url());
396
397 if (serverDestinations.size() > 0) {
398 // Ditch error page if it was created before.
399 // A new one will be created if there's another problem
400 delete err;
401 err = NULL;
402
403 // Update the logging information about this new server connection.
404 // Done here before anything else so the errors get logged for
405 // this server link regardless of what happens when connecting to it.
406 // IF sucessfuly connected this top destination will become the serverConnection().
407 syncHierNote(serverDestinations[0], request->url.host());
408 request->clearError();
409
410 connectStart();
411 } else {
412 debugs(17, 3, HERE << "Connection failed: " << entry->url());
413 if (!err) {
414 ErrorState *anErr = new ErrorState(ERR_CANNOT_FORWARD, Http::scInternalServerError, request);
415 fail(anErr);
416 } // else use actual error from last connection attempt
417 self = NULL; // refcounted
418 }
419 }
420
421 void
422 FwdState::fail(ErrorState * errorState)
423 {
424 debugs(17, 3, err_type_str[errorState->type] << " \"" << Http::StatusCodeString(errorState->httpStatus) << "\"\n\t" << entry->url());
425
426 delete err;
427 err = errorState;
428
429 if (!errorState->request) {
430 errorState->request = request;
431 HTTPMSGLOCK(errorState->request);
432 }
433
434 if (err->type != ERR_ZERO_SIZE_OBJECT)
435 return;
436
437 if (pconnRace == racePossible) {
438 debugs(17, 5, HERE << "pconn race happened");
439 pconnRace = raceHappened;
440 }
441
442 if (ConnStateData *pinned_connection = request->pinnedConnection()) {
443 pinned_connection->pinning.zeroReply = true;
444 flags.dont_retry = true; // we want to propagate failure to the client
445 debugs(17, 4, "zero reply on pinned connection");
446 }
447 }
448
449 /**
450 * Frees fwdState without closing FD or generating an abort
451 */
452 void
453 FwdState::unregister(Comm::ConnectionPointer &conn)
454 {
455 debugs(17, 3, HERE << entry->url() );
456 assert(serverConnection() == conn);
457 assert(Comm::IsConnOpen(conn));
458 comm_remove_close_handler(conn->fd, closeHandler);
459 closeHandler = NULL;
460 serverConn = NULL;
461 }
462
463 // \deprecated use unregister(Comm::ConnectionPointer &conn) instead
464 void
465 FwdState::unregister(int fd)
466 {
467 debugs(17, 3, HERE << entry->url() );
468 assert(fd == serverConnection()->fd);
469 unregister(serverConn);
470 }
471
472 /**
473 * FooClient modules call fwdComplete() when they are done
474 * downloading an object. Then, we either 1) re-forward the
475 * request somewhere else if needed, or 2) call storeComplete()
476 * to finish it off
477 */
478 void
479 FwdState::complete()
480 {
481 debugs(17, 3, HERE << entry->url() << "\n\tstatus " << entry->getReply()->sline.status());
482 #if URL_CHECKSUM_DEBUG
483
484 entry->mem_obj->checkUrlChecksum();
485 #endif
486
487 logReplyStatus(n_tries, entry->getReply()->sline.status());
488
489 if (reforward()) {
490 debugs(17, 3, HERE << "re-forwarding " << entry->getReply()->sline.status() << " " << entry->url());
491
492 if (Comm::IsConnOpen(serverConn))
493 unregister(serverConn);
494
495 entry->reset();
496
497 // drop the last path off the selection list. try the next one.
498 serverDestinations.erase(serverDestinations.begin());
499 startConnectionOrFail();
500
501 } else {
502 if (Comm::IsConnOpen(serverConn))
503 debugs(17, 3, HERE << "server FD " << serverConnection()->fd << " not re-forwarding status " << entry->getReply()->sline.status());
504 else
505 debugs(17, 3, HERE << "server (FD closed) not re-forwarding status " << entry->getReply()->sline.status());
506 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
507 entry->complete();
508
509 if (!Comm::IsConnOpen(serverConn))
510 completed();
511
512 self = NULL; // refcounted
513 }
514 }
515
516 /**** CALLBACK WRAPPERS ************************************************************/
517
518 static void
519 fwdPeerSelectionCompleteWrapper(Comm::ConnectionList *, ErrorState *err, void *data)
520 {
521 FwdState *fwd = (FwdState *) data;
522 if (err)
523 fwd->fail(err);
524 fwd->startConnectionOrFail();
525 }
526
527 static void
528 fwdServerClosedWrapper(const CommCloseCbParams &params)
529 {
530 FwdState *fwd = (FwdState *)params.data;
531 fwd->serverClosed(params.fd);
532 }
533
534 void
535 fwdConnectDoneWrapper(const Comm::ConnectionPointer &conn, Comm::Flag status, int xerrno, void *data)
536 {
537 FwdState *fwd = (FwdState *) data;
538 fwd->connectDone(conn, status, xerrno);
539 }
540
541 /**** PRIVATE *****************************************************************/
542
543 /*
544 * FwdState::checkRetry
545 *
546 * Return TRUE if the request SHOULD be retried. This method is
547 * called when the HTTP connection fails, or when the connection
548 * is closed before reading the end of HTTP headers from the server.
549 */
550 bool
551 FwdState::checkRetry()
552 {
553 if (shutting_down)
554 return false;
555
556 if (!self) { // we have aborted before the server called us back
557 debugs(17, 5, HERE << "not retrying because of earlier abort");
558 // we will be destroyed when the server clears its Pointer to us
559 return false;
560 }
561
562 if (entry->store_status != STORE_PENDING)
563 return false;
564
565 if (!entry->isEmpty())
566 return false;
567
568 if (n_tries > Config.forward_max_tries)
569 return false;
570
571 if (squid_curtime - start_t > Config.Timeout.forward)
572 return false;
573
574 if (flags.dont_retry)
575 return false;
576
577 if (request->bodyNibbled())
578 return false;
579
580 // NP: not yet actually connected anywhere. retry is safe.
581 if (!flags.connected_okay)
582 return true;
583
584 if (!checkRetriable())
585 return false;
586
587 return true;
588 }
589
590 /// Whether we may try sending this request again after a failure.
591 bool
592 FwdState::checkRetriable()
593 {
594 // Optimize: A compliant proxy may retry PUTs, but Squid lacks the [rather
595 // complicated] code required to protect the PUT request body from being
596 // nibbled during the first try. Thus, Squid cannot retry some PUTs today.
597 if (request->body_pipe != NULL)
598 return false;
599
600 // RFC2616 9.1 Safe and Idempotent Methods
601 return (request->method.isHttpSafe() || request->method.isIdempotent());
602 }
603
604 void
605 FwdState::serverClosed(int fd)
606 {
607 // XXX: fd is often -1 here
608 debugs(17, 2, "FD " << fd << " " << entry->url() << " after " <<
609 (fd >= 0 ? fd_table[fd].pconn.uses : -1) << " requests");
610 if (fd >= 0 && serverConnection()->fd == fd)
611 fwdPconnPool->noteUses(fd_table[fd].pconn.uses);
612 retryOrBail();
613 }
614
615 void
616 FwdState::retryOrBail()
617 {
618 if (checkRetry()) {
619 debugs(17, 3, HERE << "re-forwarding (" << n_tries << " tries, " << (squid_curtime - start_t) << " secs)");
620 // we should retry the same destination if it failed due to pconn race
621 if (pconnRace == raceHappened)
622 debugs(17, 4, HERE << "retrying the same destination");
623 else
624 serverDestinations.erase(serverDestinations.begin()); // last one failed. try another.
625 startConnectionOrFail();
626 return;
627 }
628
629 // TODO: should we call completed() here and move doneWithRetries there?
630 doneWithRetries();
631
632 request->hier.stopPeerClock(false);
633
634 if (self != NULL && !err && shutting_down && entry->isEmpty()) {
635 ErrorState *anErr = new ErrorState(ERR_SHUTTING_DOWN, Http::scServiceUnavailable, request);
636 errorAppendEntry(entry, anErr);
637 }
638
639 self = NULL; // refcounted
640 }
641
642 // If the Server quits before nibbling at the request body, the body sender
643 // will not know (so that we can retry). Call this if we will not retry. We
644 // will notify the sender so that it does not get stuck waiting for space.
645 void
646 FwdState::doneWithRetries()
647 {
648 if (request && request->body_pipe != NULL)
649 request->body_pipe->expectNoConsumption();
650 }
651
652 // called by the server that failed after calling unregister()
653 void
654 FwdState::handleUnregisteredServerEnd()
655 {
656 debugs(17, 2, HERE << "self=" << self << " err=" << err << ' ' << entry->url());
657 assert(!Comm::IsConnOpen(serverConn));
658 retryOrBail();
659 }
660
661 void
662 FwdState::connectDone(const Comm::ConnectionPointer &conn, Comm::Flag status, int xerrno)
663 {
664 if (status != Comm::OK) {
665 ErrorState *const anErr = makeConnectingError(ERR_CONNECT_FAIL);
666 anErr->xerrno = xerrno;
667 fail(anErr);
668
669 /* it might have been a timeout with a partially open link */
670 if (conn != NULL) {
671 if (conn->getPeer())
672 peerConnectFailed(conn->getPeer());
673
674 conn->close();
675 }
676 retryOrBail();
677 return;
678 }
679
680 serverConn = conn;
681 debugs(17, 3, HERE << serverConnection() << ": '" << entry->url() << "'" );
682
683 closeHandler = comm_add_close_handler(serverConnection()->fd, fwdServerClosedWrapper, this);
684
685 if (!request->flags.pinned) {
686 const CachePeer *p = serverConnection()->getPeer();
687 const bool peerWantsTls = p && p->secure.encryptTransport;
688 // userWillTlsToPeerForUs assumes CONNECT == HTTPS
689 const bool userWillTlsToPeerForUs = p && p->options.originserver &&
690 request->method == Http::METHOD_CONNECT;
691 const bool needTlsToPeer = peerWantsTls && !userWillTlsToPeerForUs;
692 const bool needTlsToOrigin = !p && request->url.getScheme() == AnyP::PROTO_HTTPS;
693 if (needTlsToPeer || needTlsToOrigin || request->flags.sslPeek) {
694 HttpRequest::Pointer requestPointer = request;
695 AsyncCall::Pointer callback = asyncCall(17,4,
696 "FwdState::ConnectedToPeer",
697 FwdStatePeerAnswerDialer(&FwdState::connectedToPeer, this));
698 // Use positive timeout when less than one second is left.
699 const time_t sslNegotiationTimeout = max(static_cast<time_t>(1), timeLeft());
700 Security::PeerConnector *connector = nullptr;
701 #if USE_OPENSSL
702 if (request->flags.sslPeek)
703 connector = new Ssl::PeekingPeerConnector(requestPointer, serverConnection(), clientConn, callback, al, sslNegotiationTimeout);
704 else
705 #endif
706 connector = new Security::BlindPeerConnector(requestPointer, serverConnection(), callback, al, sslNegotiationTimeout);
707 AsyncJob::Start(connector); // will call our callback
708 return;
709 }
710 }
711
712 // if not encrypting just run the post-connect actions
713 Security::EncryptorAnswer nil;
714 connectedToPeer(nil);
715 }
716
717 void
718 FwdState::connectedToPeer(Security::EncryptorAnswer &answer)
719 {
720 if (ErrorState *error = answer.error.get()) {
721 fail(error);
722 answer.error.clear(); // preserve error for errorSendComplete()
723 if (CachePeer *p = serverConnection()->getPeer())
724 peerConnectFailed(p);
725 serverConnection()->close();
726 return;
727 }
728
729 if (answer.tunneled) {
730 // TODO: When ConnStateData establishes tunnels, its state changes
731 // [in ways that may affect logging?]. Consider informing
732 // ConnStateData about our tunnel or otherwise unifying tunnel
733 // establishment [side effects].
734 unregister(serverConn); // async call owns it now
735 complete(); // destroys us
736 return;
737 }
738
739 // should reach ConnStateData before the dispatched Client job starts
740 CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
741 ConnStateData::notePeerConnection, serverConnection());
742
743 if (serverConnection()->getPeer())
744 peerConnectSucceded(serverConnection()->getPeer());
745
746 flags.connected_okay = true;
747 dispatch();
748 }
749
750 void
751 FwdState::connectTimeout(int fd)
752 {
753 debugs(17, 2, "fwdConnectTimeout: FD " << fd << ": '" << entry->url() << "'" );
754 assert(serverDestinations[0] != NULL);
755 assert(fd == serverDestinations[0]->fd);
756
757 if (entry->isEmpty()) {
758 ErrorState *anErr = new ErrorState(ERR_CONNECT_FAIL, Http::scGatewayTimeout, request);
759 anErr->xerrno = ETIMEDOUT;
760 fail(anErr);
761
762 /* This marks the peer DOWN ... */
763 if (serverDestinations[0]->getPeer())
764 peerConnectFailed(serverDestinations[0]->getPeer());
765 }
766
767 if (Comm::IsConnOpen(serverDestinations[0])) {
768 serverDestinations[0]->close();
769 }
770 }
771
772 time_t
773 FwdState::timeLeft() const
774 {
775 /* connection timeout */
776 int ctimeout;
777 if (serverDestinations[0]->getPeer()) {
778 ctimeout = serverDestinations[0]->getPeer()->connect_timeout > 0 ?
779 serverDestinations[0]->getPeer()->connect_timeout : Config.Timeout.peer_connect;
780 } else {
781 ctimeout = Config.Timeout.connect;
782 }
783
784 /* calculate total forwarding timeout ??? */
785 int ftimeout = Config.Timeout.forward - (squid_curtime - start_t);
786 if (ftimeout < 0)
787 ftimeout = 5;
788
789 if (ftimeout < ctimeout)
790 return (time_t)ftimeout;
791 else
792 return (time_t)ctimeout;
793 }
794
795 /// called when serverConn is set to an _open_ to-peer connection
796 void
797 FwdState::syncWithServerConn(const char *host)
798 {
799 if (Ip::Qos::TheConfig.isAclTosActive())
800 Ip::Qos::setSockTos(serverConn, GetTosToServer(request));
801
802 #if SO_MARK
803 if (Ip::Qos::TheConfig.isAclNfmarkActive())
804 Ip::Qos::setSockNfmark(serverConn, GetNfmarkToServer(request));
805 #endif
806
807 syncHierNote(serverConn, host);
808 }
809
810 void
811 FwdState::syncHierNote(const Comm::ConnectionPointer &server, const char *host)
812 {
813 if (request)
814 request->hier.note(server, host);
815 if (al)
816 al->hier.note(server, host);
817 }
818
819 /**
820 * Called after forwarding path selection (via peer select) has taken place
821 * and whenever forwarding needs to attempt a new connection (routing failover).
822 * We have a vector of possible localIP->remoteIP paths now ready to start being connected.
823 */
824 void
825 FwdState::connectStart()
826 {
827 assert(serverDestinations.size() > 0);
828
829 debugs(17, 3, "fwdConnectStart: " << entry->url());
830
831 request->hier.startPeerClock();
832
833 // Do not fowrward bumped connections to parent proxy unless it is an
834 // origin server
835 if (serverDestinations[0]->getPeer() && !serverDestinations[0]->getPeer()->options.originserver && request->flags.sslBumped) {
836 debugs(50, 4, "fwdConnectStart: Ssl bumped connections through parent proxy are not allowed");
837 ErrorState *anErr = new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request);
838 fail(anErr);
839 self = NULL; // refcounted
840 return;
841 }
842
843 request->flags.pinned = false; // XXX: what if the ConnStateData set this to flag existing credentials?
844 // XXX: answer: the peer selection *should* catch it and give us only the pinned peer. so we reverse the =0 step below.
845 // XXX: also, logs will now lie if pinning is broken and leads to an error message.
846 if (serverDestinations[0]->peerType == PINNED) {
847 ConnStateData *pinned_connection = request->pinnedConnection();
848 debugs(17,7, "pinned peer connection: " << pinned_connection);
849 // pinned_connection may become nil after a pconn race
850 serverConn = pinned_connection ? pinned_connection->borrowPinnedConnection(request, serverDestinations[0]->getPeer()) : nullptr;
851 if (Comm::IsConnOpen(serverConn)) {
852 flags.connected_okay = true;
853 ++n_tries;
854 request->flags.pinned = true;
855
856 if (pinned_connection->pinnedAuth())
857 request->flags.auth = true;
858
859 closeHandler = comm_add_close_handler(serverConn->fd, fwdServerClosedWrapper, this);
860
861 syncWithServerConn(pinned_connection->pinning.host);
862
863 // the server may close the pinned connection before this request
864 pconnRace = racePossible;
865 dispatch();
866 return;
867 }
868
869 // Pinned connection failure.
870 debugs(17,2,HERE << "Pinned connection failed: " << pinned_connection);
871 ErrorState *anErr = new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, request);
872 fail(anErr);
873 self = NULL; // refcounted
874 return;
875 }
876
877 // Use pconn to avoid opening a new connection.
878 const char *host = NULL;
879 if (!serverDestinations[0]->getPeer())
880 host = request->url.host();
881
882 Comm::ConnectionPointer temp;
883 // Avoid pconns after races so that the same client does not suffer twice.
884 // This does not increase the total number of connections because we just
885 // closed the connection that failed the race. And re-pinning assumes this.
886 if (pconnRace != raceHappened)
887 temp = pconnPop(serverDestinations[0], host);
888
889 const bool openedPconn = Comm::IsConnOpen(temp);
890 pconnRace = openedPconn ? racePossible : raceImpossible;
891
892 // if we found an open persistent connection to use. use it.
893 if (openedPconn) {
894 serverConn = temp;
895 flags.connected_okay = true;
896 debugs(17, 3, HERE << "reusing pconn " << serverConnection());
897 ++n_tries;
898
899 closeHandler = comm_add_close_handler(serverConnection()->fd, fwdServerClosedWrapper, this);
900
901 syncWithServerConn(request->url.host());
902
903 dispatch();
904 return;
905 }
906
907 // We will try to open a new connection, possibly to the same destination.
908 // We reset serverDestinations[0] in case we are using it again because
909 // ConnOpener modifies its destination argument.
910 serverDestinations[0]->local.port(0);
911 serverConn = NULL;
912
913 #if URL_CHECKSUM_DEBUG
914 entry->mem_obj->checkUrlChecksum();
915 #endif
916
917 GetMarkingsToServer(request, *serverDestinations[0]);
918
919 calls.connector = commCbCall(17,3, "fwdConnectDoneWrapper", CommConnectCbPtrFun(fwdConnectDoneWrapper, this));
920 Comm::ConnOpener *cs = new Comm::ConnOpener(serverDestinations[0], calls.connector, timeLeft());
921 if (host)
922 cs->setHost(host);
923 AsyncJob::Start(cs);
924 }
925
926 void
927 FwdState::dispatch()
928 {
929 debugs(17, 3, clientConn << ": Fetching " << request->method << ' ' << entry->url());
930 /*
931 * Assert that server_fd is set. This is to guarantee that fwdState
932 * is attached to something and will be deallocated when server_fd
933 * is closed.
934 */
935 assert(Comm::IsConnOpen(serverConn));
936
937 fd_note(serverConnection()->fd, entry->url());
938
939 fd_table[serverConnection()->fd].noteUse();
940
941 /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
942 assert(entry->ping_status != PING_WAITING);
943
944 assert(entry->locked());
945
946 EBIT_SET(entry->flags, ENTRY_DISPATCHED);
947
948 netdbPingSite(request->url.host());
949
950 /* Retrieves remote server TOS or MARK value, and stores it as part of the
951 * original client request FD object. It is later used to forward
952 * remote server's TOS/MARK in the response to the client in case of a MISS.
953 */
954 if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
955 if (Comm::IsConnOpen(clientConn) && Comm::IsConnOpen(serverConnection())) {
956 fde * clientFde = &fd_table[clientConn->fd]; // XXX: move the fd_table access into Ip::Qos
957 /* Get the netfilter mark for the connection */
958 Ip::Qos::getNfmarkFromServer(serverConnection(), clientFde);
959 }
960 }
961
962 #if _SQUID_LINUX_
963 /* Bug 2537: The TOS forward part of QOS only applies to patched Linux kernels. */
964 if (Ip::Qos::TheConfig.isHitTosActive()) {
965 if (Comm::IsConnOpen(clientConn)) {
966 fde * clientFde = &fd_table[clientConn->fd]; // XXX: move the fd_table access into Ip::Qos
967 /* Get the TOS value for the packet */
968 Ip::Qos::getTosFromServer(serverConnection(), clientFde);
969 }
970 }
971 #endif
972
973 #if USE_OPENSSL
974 if (request->flags.sslPeek) {
975 CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
976 ConnStateData::httpsPeeked, serverConnection());
977 unregister(serverConn); // async call owns it now
978 complete(); // destroys us
979 return;
980 }
981 #endif
982
983 if (serverConnection()->getPeer() != NULL) {
984 ++ serverConnection()->getPeer()->stats.fetches;
985 request->peer_login = serverConnection()->getPeer()->login;
986 request->peer_domain = serverConnection()->getPeer()->domain;
987 request->flags.auth_no_keytab = serverConnection()->getPeer()->options.auth_no_keytab;
988 httpStart(this);
989 } else {
990 assert(!request->flags.sslPeek);
991 request->peer_login = NULL;
992 request->peer_domain = NULL;
993 request->flags.auth_no_keytab = 0;
994
995 switch (request->url.getScheme()) {
996
997 case AnyP::PROTO_HTTPS:
998 httpStart(this);
999 break;
1000
1001 case AnyP::PROTO_HTTP:
1002 httpStart(this);
1003 break;
1004
1005 case AnyP::PROTO_GOPHER:
1006 gopherStart(this);
1007 break;
1008
1009 case AnyP::PROTO_FTP:
1010 if (request->flags.ftpNative)
1011 Ftp::StartRelay(this);
1012 else
1013 Ftp::StartGateway(this);
1014 break;
1015
1016 case AnyP::PROTO_CACHE_OBJECT:
1017
1018 case AnyP::PROTO_URN:
1019 fatal_dump("Should never get here");
1020 break;
1021
1022 case AnyP::PROTO_WHOIS:
1023 whoisStart(this);
1024 break;
1025
1026 case AnyP::PROTO_WAIS: /* Not implemented */
1027
1028 default:
1029 debugs(17, DBG_IMPORTANT, "WARNING: Cannot retrieve '" << entry->url() << "'.");
1030 ErrorState *anErr = new ErrorState(ERR_UNSUP_REQ, Http::scBadRequest, request);
1031 fail(anErr);
1032 // Set the dont_retry flag because this is not a transient (network) error.
1033 flags.dont_retry = true;
1034 if (Comm::IsConnOpen(serverConn)) {
1035 serverConn->close();
1036 }
1037 break;
1038 }
1039 }
1040 }
1041
1042 /*
1043 * FwdState::reforward
1044 *
1045 * returns TRUE if the transaction SHOULD be re-forwarded to the
1046 * next choice in the serverDestinations list. This method is called when
1047 * peer communication completes normally, or experiences
1048 * some error after receiving the end of HTTP headers.
1049 */
1050 int
1051 FwdState::reforward()
1052 {
1053 StoreEntry *e = entry;
1054
1055 if (EBIT_TEST(e->flags, ENTRY_ABORTED)) {
1056 debugs(17, 3, HERE << "entry aborted");
1057 return 0;
1058 }
1059
1060 assert(e->store_status == STORE_PENDING);
1061 assert(e->mem_obj);
1062 #if URL_CHECKSUM_DEBUG
1063
1064 e->mem_obj->checkUrlChecksum();
1065 #endif
1066
1067 debugs(17, 3, HERE << e->url() << "?" );
1068
1069 if (!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) {
1070 debugs(17, 3, HERE << "No, ENTRY_FWD_HDR_WAIT isn't set");
1071 return 0;
1072 }
1073
1074 if (n_tries > Config.forward_max_tries)
1075 return 0;
1076
1077 if (request->bodyNibbled())
1078 return 0;
1079
1080 if (serverDestinations.size() <= 1) {
1081 // NP: <= 1 since total count includes the recently failed one.
1082 debugs(17, 3, HERE << "No alternative forwarding paths left");
1083 return 0;
1084 }
1085
1086 const Http::StatusCode s = e->getReply()->sline.status();
1087 debugs(17, 3, HERE << "status " << s);
1088 return reforwardableStatus(s);
1089 }
1090
1091 /**
1092 * Create "503 Service Unavailable" or "504 Gateway Timeout" error depending
1093 * on whether this is a validation request. RFC 2616 says that we MUST reply
1094 * with "504 Gateway Timeout" if validation fails and cached reply has
1095 * proxy-revalidate, must-revalidate or s-maxage Cache-Control directive.
1096 */
1097 ErrorState *
1098 FwdState::makeConnectingError(const err_type type) const
1099 {
1100 return new ErrorState(type, request->flags.needValidation ?
1101 Http::scGatewayTimeout : Http::scServiceUnavailable, request);
1102 }
1103
1104 static void
1105 fwdStats(StoreEntry * s)
1106 {
1107 int i;
1108 int j;
1109 storeAppendPrintf(s, "Status");
1110
1111 for (j = 1; j < MAX_FWD_STATS_IDX; ++j) {
1112 storeAppendPrintf(s, "\ttry#%d", j);
1113 }
1114
1115 storeAppendPrintf(s, "\n");
1116
1117 for (i = 0; i <= (int) Http::scInvalidHeader; ++i) {
1118 if (FwdReplyCodes[0][i] == 0)
1119 continue;
1120
1121 storeAppendPrintf(s, "%3d", i);
1122
1123 for (j = 0; j <= MAX_FWD_STATS_IDX; ++j) {
1124 storeAppendPrintf(s, "\t%d", FwdReplyCodes[j][i]);
1125 }
1126
1127 storeAppendPrintf(s, "\n");
1128 }
1129 }
1130
1131 /**** STATIC MEMBER FUNCTIONS *************************************************/
1132
1133 bool
1134 FwdState::reforwardableStatus(const Http::StatusCode s) const
1135 {
1136 switch (s) {
1137
1138 case Http::scBadGateway:
1139
1140 case Http::scGatewayTimeout:
1141 return true;
1142
1143 case Http::scForbidden:
1144
1145 case Http::scInternalServerError:
1146
1147 case Http::scNotImplemented:
1148
1149 case Http::scServiceUnavailable:
1150 return Config.retry.onerror;
1151
1152 default:
1153 return false;
1154 }
1155
1156 /* NOTREACHED */
1157 }
1158
1159 /**
1160 * Decide where details need to be gathered to correctly describe a persistent connection.
1161 * What is needed:
1162 * - the address/port details about this link
1163 * - domain name of server at other end of this link (either peer or requested host)
1164 */
1165 void
1166 FwdState::pconnPush(Comm::ConnectionPointer &conn, const char *domain)
1167 {
1168 if (conn->getPeer()) {
1169 fwdPconnPool->push(conn, NULL);
1170 } else {
1171 fwdPconnPool->push(conn, domain);
1172 }
1173 }
1174
1175 Comm::ConnectionPointer
1176 FwdState::pconnPop(const Comm::ConnectionPointer &dest, const char *domain)
1177 {
1178 bool retriable = checkRetriable();
1179 if (!retriable && Config.accessList.serverPconnForNonretriable) {
1180 ACLFilledChecklist ch(Config.accessList.serverPconnForNonretriable, request, NULL);
1181 retriable = (ch.fastCheck() == ACCESS_ALLOWED);
1182 }
1183 // always call shared pool first because we need to close an idle
1184 // connection there if we have to use a standby connection.
1185 Comm::ConnectionPointer conn = fwdPconnPool->pop(dest, domain, retriable);
1186 if (!Comm::IsConnOpen(conn)) {
1187 // either there was no pconn to pop or this is not a retriable xaction
1188 if (CachePeer *peer = dest->getPeer()) {
1189 if (peer->standby.pool)
1190 conn = peer->standby.pool->pop(dest, domain, true);
1191 }
1192 }
1193 return conn; // open, closed, or nil
1194 }
1195
1196 void
1197 FwdState::initModule()
1198 {
1199 RegisterWithCacheManager();
1200 }
1201
1202 void
1203 FwdState::RegisterWithCacheManager(void)
1204 {
1205 Mgr::RegisterAction("forward", "Request Forwarding Statistics", fwdStats, 0, 1);
1206 }
1207
1208 void
1209 FwdState::logReplyStatus(int tries, const Http::StatusCode status)
1210 {
1211 if (status > Http::scInvalidHeader)
1212 return;
1213
1214 assert(tries >= 0);
1215
1216 if (tries > MAX_FWD_STATS_IDX)
1217 tries = MAX_FWD_STATS_IDX;
1218
1219 ++ FwdReplyCodes[tries][status];
1220 }
1221
1222 /**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
1223
1224 /*
1225 * DPW 2007-05-19
1226 * Formerly static, but now used by client_side_request.cc
1227 */
1228 /// Checks for a TOS value to apply depending on the ACL
1229 tos_t
1230 aclMapTOS(acl_tos * head, ACLChecklist * ch)
1231 {
1232 for (acl_tos *l = head; l; l = l->next) {
1233 if (!l->aclList || ch->fastCheck(l->aclList) == ACCESS_ALLOWED)
1234 return l->tos;
1235 }
1236
1237 return 0;
1238 }
1239
1240 /// Checks for a netfilter mark value to apply depending on the ACL
1241 nfmark_t
1242 aclMapNfmark(acl_nfmark * head, ACLChecklist * ch)
1243 {
1244 for (acl_nfmark *l = head; l; l = l->next) {
1245 if (!l->aclList || ch->fastCheck(l->aclList) == ACCESS_ALLOWED)
1246 return l->nfmark;
1247 }
1248
1249 return 0;
1250 }
1251
1252 void
1253 getOutgoingAddress(HttpRequest * request, Comm::ConnectionPointer conn)
1254 {
1255 // skip if an outgoing address is already set.
1256 if (!conn->local.isAnyAddr()) return;
1257
1258 // ensure that at minimum the wildcard local matches remote protocol
1259 if (conn->remote.isIPv4())
1260 conn->local.setIPv4();
1261
1262 // maybe use TPROXY client address
1263 if (request && request->flags.spoofClientIp) {
1264 if (!conn->getPeer() || !conn->getPeer()->options.no_tproxy) {
1265 #if FOLLOW_X_FORWARDED_FOR && LINUX_NETFILTER
1266 if (Config.onoff.tproxy_uses_indirect_client)
1267 conn->local = request->indirect_client_addr;
1268 else
1269 #endif
1270 conn->local = request->client_addr;
1271 // some flags need setting on the socket to use this address
1272 conn->flags |= COMM_DOBIND;
1273 conn->flags |= COMM_TRANSPARENT;
1274 return;
1275 }
1276 // else no tproxy today ...
1277 }
1278
1279 if (!Config.accessList.outgoing_address) {
1280 return; // anything will do.
1281 }
1282
1283 ACLFilledChecklist ch(NULL, request, NULL);
1284 ch.dst_peer_name = conn->getPeer() ? conn->getPeer()->name : NULL;
1285 ch.dst_addr = conn->remote;
1286
1287 // TODO use the connection details in ACL.
1288 // needs a bit of rework in ACLFilledChecklist to use Comm::Connection instead of ConnStateData
1289
1290 for (Acl::Address *l = Config.accessList.outgoing_address; l; l = l->next) {
1291
1292 /* check if the outgoing address is usable to the destination */
1293 if (conn->remote.isIPv4() != l->addr.isIPv4()) continue;
1294
1295 /* check ACLs for this outgoing address */
1296 if (!l->aclList || ch.fastCheck(l->aclList) == ACCESS_ALLOWED) {
1297 conn->local = l->addr;
1298 return;
1299 }
1300 }
1301 }
1302
1303 tos_t
1304 GetTosToServer(HttpRequest * request)
1305 {
1306 ACLFilledChecklist ch(NULL, request, NULL);
1307 return aclMapTOS(Ip::Qos::TheConfig.tosToServer, &ch);
1308 }
1309
1310 nfmark_t
1311 GetNfmarkToServer(HttpRequest * request)
1312 {
1313 ACLFilledChecklist ch(NULL, request, NULL);
1314 return aclMapNfmark(Ip::Qos::TheConfig.nfmarkToServer, &ch);
1315 }
1316
1317 void
1318 GetMarkingsToServer(HttpRequest * request, Comm::Connection &conn)
1319 {
1320 // Get the server side TOS and Netfilter mark to be set on the connection.
1321 if (Ip::Qos::TheConfig.isAclTosActive()) {
1322 conn.tos = GetTosToServer(request);
1323 debugs(17, 3, "from " << conn.local << " tos " << int(conn.tos));
1324 }
1325
1326 #if SO_MARK && USE_LIBCAP
1327 conn.nfmark = GetNfmarkToServer(request);
1328 debugs(17, 3, "from " << conn.local << " netfilter mark " << conn.nfmark);
1329 #else
1330 conn.nfmark = 0;
1331 #endif
1332 }
1333