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