]> git.ipfire.org Git - thirdparty/squid.git/blob - src/FwdState.cc
Properly log pinned connection destination (%<A).
[thirdparty/squid.git] / src / FwdState.cc
1 /*
2 * DEBUG: section 17 Request Forwarding
3 * AUTHOR: Duane Wessels
4 *
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
7 *
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.
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.
21 *
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.
26 *
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
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30 *
31 */
32
33 #include "squid.h"
34 #include "AccessLogEntry.h"
35 #include "acl/AclAddress.h"
36 #include "acl/FilledChecklist.h"
37 #include "acl/Gadgets.h"
38 #include "anyp/PortCfg.h"
39 #include "CachePeer.h"
40 #include "CacheManager.h"
41 #include "client_side.h"
42 #include "comm/Connection.h"
43 #include "comm/ConnOpener.h"
44 #include "comm/Loops.h"
45 #include "CommCalls.h"
46 #include "errorpage.h"
47 #include "event.h"
48 #include "fd.h"
49 #include "fde.h"
50 #include "ftp.h"
51 #include "FtpGatewayServer.h"
52 #include "FwdState.h"
53 #include "globals.h"
54 #include "gopher.h"
55 #include "hier_code.h"
56 #include "http.h"
57 #include "HttpReply.h"
58 #include "HttpRequest.h"
59 #include "icmp/net_db.h"
60 #include "internal.h"
61 #include "ip/Intercept.h"
62 #include "ip/QosConfig.h"
63 #include "ip/tools.h"
64 #include "MemObject.h"
65 #include "mgr/Registration.h"
66 #include "neighbors.h"
67 #include "pconn.h"
68 #include "PeerSelectState.h"
69 #include "SquidConfig.h"
70 #include "SquidTime.h"
71 #include "Store.h"
72 #include "StoreClient.h"
73 #include "urn.h"
74 #include "whois.h"
75 #if USE_SSL
76 #include "ssl/cert_validate_message.h"
77 #include "ssl/Config.h"
78 #include "ssl/helper.h"
79 #include "ssl/support.h"
80 #include "ssl/ErrorDetail.h"
81 #include "ssl/ServerBump.h"
82 #endif
83 #if HAVE_ERRNO_H
84 #include <errno.h>
85 #endif
86
87 static PSC fwdPeerSelectionCompleteWrapper;
88 static CLCB fwdServerClosedWrapper;
89 #if USE_SSL
90 static PF fwdNegotiateSSLWrapper;
91 #endif
92 static CNCB fwdConnectDoneWrapper;
93
94 static OBJH fwdStats;
95
96 #define MAX_FWD_STATS_IDX 9
97 static int FwdReplyCodes[MAX_FWD_STATS_IDX + 1][Http::scInvalidHeader + 1];
98
99 static PconnPool *fwdPconnPool = new PconnPool("server-side");
100 CBDATA_CLASS_INIT(FwdState);
101
102 void
103 FwdState::abort(void* d)
104 {
105 FwdState* fwd = (FwdState*)d;
106 Pointer tmp = fwd; // Grab a temporary pointer to keep the object alive during our scope.
107
108 if (Comm::IsConnOpen(fwd->serverConnection())) {
109 comm_remove_close_handler(fwd->serverConnection()->fd, fwdServerClosedWrapper, fwd);
110 debugs(17, 3, HERE << "store entry aborted; closing " <<
111 fwd->serverConnection());
112 fwd->serverConnection()->close();
113 } else {
114 debugs(17, 7, HERE << "store entry aborted; no connection to close");
115 }
116 fwd->serverDestinations.clean();
117 fwd->self = NULL;
118 }
119
120 /**** PUBLIC INTERFACE ********************************************************/
121
122 FwdState::FwdState(const Comm::ConnectionPointer &client, StoreEntry * e, HttpRequest * r, const AccessLogEntryPointer &alp):
123 al(alp)
124 {
125 debugs(17, 2, HERE << "Forwarding client request " << client << ", url=" << e->url() );
126 entry = e;
127 clientConn = client;
128 request = r;
129 HTTPMSGLOCK(request);
130 pconnRace = raceImpossible;
131 start_t = squid_curtime;
132 serverDestinations.reserve(Config.forward_max_tries);
133 e->lock();
134 EBIT_SET(e->flags, ENTRY_FWD_HDR_WAIT);
135 }
136
137 // Called once, right after object creation, when it is safe to set self
138 void FwdState::start(Pointer aSelf)
139 {
140 // Protect ourselves from being destroyed when the only Server pointing
141 // to us is gone (while we expect to talk to more Servers later).
142 // Once we set self, we are responsible for clearing it when we do not
143 // expect to talk to any servers.
144 self = aSelf; // refcounted
145
146 // We hope that either the store entry aborts or peer is selected.
147 // Otherwise we are going to leak our object.
148
149 entry->registerAbort(FwdState::abort, this);
150
151 #if STRICT_ORIGINAL_DST
152 // Bug 3243: CVE 2009-0801
153 // Bypass of browser same-origin access control in intercepted communication
154 // To resolve this we must force DIRECT and only to the original client destination.
155 const bool isIntercepted = request && !request->flags.redirected && (request->flags.intercepted || request->flags.interceptTproxy);
156 const bool useOriginalDst = Config.onoff.client_dst_passthru || (request && !request->flags.hostVerified);
157 if (isIntercepted && useOriginalDst) {
158 selectPeerForIntercepted();
159 // 3.2 does not suppro re-wrapping inside CONNECT.
160 // our only alternative is to fake destination "found" and continue with the forwarding.
161 startConnectionOrFail();
162 return;
163 }
164 #endif
165
166 // do full route options selection
167 peerSelect(&serverDestinations, request, entry, fwdPeerSelectionCompleteWrapper, this);
168 }
169
170 #if STRICT_ORIGINAL_DST
171 /// bypasses peerSelect() when dealing with intercepted requests
172 void
173 FwdState::selectPeerForIntercepted()
174 {
175 // use pinned connection if available
176 Comm::ConnectionPointer p;
177 if (ConnStateData *client = request->pinnedConnection()) {
178 p = client->validatePinnedConnection(request, NULL);
179 if (Comm::IsConnOpen(p)) {
180 /* duplicate peerSelectPinned() effects */
181 p->peerType = PINNED;
182 entry->ping_status = PING_DONE; /* Skip ICP */
183
184 debugs(17, 3, "reusing a pinned conn: " << *p);
185 serverDestinations.push_back(p);
186 } else {
187 debugs(17,2, "Pinned connection is not valid: " << p);
188 ErrorState *anErr = new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, request);
189 fail(anErr);
190 }
191 // Either use the valid pinned connection or fail if it is invalid.
192 return;
193 }
194
195 // use client original destination as second preferred choice
196 p = new Comm::Connection();
197 p->peerType = ORIGINAL_DST;
198 p->remote = clientConn->local;
199 getOutgoingAddress(request, p);
200
201 debugs(17, 3, HERE << "using client original destination: " << *p);
202 serverDestinations.push_back(p);
203 }
204 #endif
205
206 void
207 FwdState::completed()
208 {
209 if (flags.forward_completed) {
210 debugs(17, DBG_IMPORTANT, HERE << "FwdState::completed called on a completed request! Bad!");
211 return;
212 }
213
214 flags.forward_completed = true;
215
216 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
217 debugs(17, 3, HERE << "entry aborted");
218 return ;
219 }
220
221 #if URL_CHECKSUM_DEBUG
222
223 entry->mem_obj->checkUrlChecksum();
224 #endif
225
226 if (entry->store_status == STORE_PENDING) {
227 if (entry->isEmpty()) {
228 if (!err) // we quit (e.g., fd closed) before an error or content
229 fail(new ErrorState(ERR_READ_ERROR, Http::scBadGateway, request));
230 assert(err);
231 errorAppendEntry(entry, err);
232 err = NULL;
233 #if USE_SSL
234 if (request->flags.sslPeek && request->clientConnectionManager.valid()) {
235 CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
236 ConnStateData::httpsPeeked, Comm::ConnectionPointer(NULL));
237 }
238 #endif
239 } else {
240 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
241 entry->complete();
242 entry->releaseRequest();
243 }
244 }
245
246 if (storePendingNClients(entry) > 0)
247 assert(!EBIT_TEST(entry->flags, ENTRY_FWD_HDR_WAIT));
248
249 }
250
251 FwdState::~FwdState()
252 {
253 debugs(17, 3, HERE << "FwdState destructor starting");
254
255 if (! flags.forward_completed)
256 completed();
257
258 doneWithRetries();
259
260 HTTPMSGUNLOCK(request);
261
262 delete err;
263
264 entry->unregisterAbort();
265
266 entry->unlock();
267
268 entry = NULL;
269
270 if (calls.connector != NULL) {
271 calls.connector->cancel("FwdState destructed");
272 calls.connector = NULL;
273 }
274
275 if (Comm::IsConnOpen(serverConn)) {
276 comm_remove_close_handler(serverConnection()->fd, fwdServerClosedWrapper, this);
277 debugs(17, 3, HERE << "closing FD " << serverConnection()->fd);
278 serverConn->close();
279 }
280
281 serverDestinations.clean();
282
283 debugs(17, 3, HERE << "FwdState destructor done");
284 }
285
286 /**
287 * This is the entry point for client-side to start forwarding
288 * a transaction. It is a static method that may or may not
289 * allocate a FwdState.
290 */
291 void
292 FwdState::Start(const Comm::ConnectionPointer &clientConn, StoreEntry *entry, HttpRequest *request, const AccessLogEntryPointer &al)
293 {
294 /** \note
295 * client_addr == no_addr indicates this is an "internal" request
296 * from peer_digest.c, asn.c, netdb.c, etc and should always
297 * be allowed. yuck, I know.
298 */
299
300 if ( Config.accessList.miss && !request->client_addr.isNoAddr() &&
301 request->protocol != AnyP::PROTO_INTERNAL && request->protocol != AnyP::PROTO_CACHE_OBJECT) {
302 /**
303 * Check if this host is allowed to fetch MISSES from us (miss_access).
304 * Intentionally replace the src_addr automatically selected by the checklist code
305 * we do NOT want the indirect client address to be tested here.
306 */
307 ACLFilledChecklist ch(Config.accessList.miss, request, NULL);
308 ch.src_addr = request->client_addr;
309 if (ch.fastCheck() == ACCESS_DENIED) {
310 err_type page_id;
311 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, 1);
312
313 if (page_id == ERR_NONE)
314 page_id = ERR_FORWARDING_DENIED;
315
316 ErrorState *anErr = new ErrorState(page_id, Http::scForbidden, request);
317 errorAppendEntry(entry, anErr); // frees anErr
318 return;
319 }
320 }
321
322 debugs(17, 3, HERE << "'" << entry->url() << "'");
323 /*
324 * This seems like an odd place to bind mem_obj and request.
325 * Might want to assert that request is NULL at this point
326 */
327 entry->mem_obj->request = request;
328 HTTPMSGLOCK(entry->mem_obj->request);
329 #if URL_CHECKSUM_DEBUG
330
331 entry->mem_obj->checkUrlChecksum();
332 #endif
333
334 if (shutting_down) {
335 /* more yuck */
336 ErrorState *anErr = new ErrorState(ERR_SHUTTING_DOWN, Http::scServiceUnavailable, request);
337 errorAppendEntry(entry, anErr); // frees anErr
338 return;
339 }
340
341 switch (request->protocol) {
342
343 case AnyP::PROTO_INTERNAL:
344 internalStart(clientConn, request, entry);
345 return;
346
347 case AnyP::PROTO_CACHE_OBJECT:
348 CacheManager::GetInstance()->Start(clientConn, request, entry);
349 return;
350
351 case AnyP::PROTO_URN:
352 urnStart(request, entry);
353 return;
354
355 default:
356 FwdState::Pointer fwd = new FwdState(clientConn, entry, request, al);
357 fwd->start(fwd);
358 return;
359 }
360
361 /* NOTREACHED */
362 }
363
364 void
365 FwdState::fwdStart(const Comm::ConnectionPointer &clientConn, StoreEntry *entry, HttpRequest *request)
366 {
367 // Hides AccessLogEntry.h from code that does not supply ALE anyway.
368 Start(clientConn, entry, request, NULL);
369 }
370
371 void
372 FwdState::startConnectionOrFail()
373 {
374 debugs(17, 3, HERE << entry->url());
375
376 if (serverDestinations.size() > 0) {
377 // Ditch error page if it was created before.
378 // A new one will be created if there's another problem
379 delete err;
380 err = NULL;
381
382 // Update the logging information about this new server connection.
383 // Done here before anything else so the errors get logged for
384 // this server link regardless of what happens when connecting to it.
385 // IF sucessfuly connected this top destination will become the serverConnection().
386 request->hier.note(serverDestinations[0], request->GetHost());
387 request->clearError();
388
389 connectStart();
390 } else {
391 debugs(17, 3, HERE << "Connection failed: " << entry->url());
392 if (!err) {
393 ErrorState *anErr = new ErrorState(ERR_CANNOT_FORWARD, Http::scInternalServerError, request);
394 fail(anErr);
395 } // else use actual error from last connection attempt
396 self = NULL; // refcounted
397 }
398 }
399
400 void
401 FwdState::fail(ErrorState * errorState)
402 {
403 debugs(17, 3, err_type_str[errorState->type] << " \"" << Http::StatusCodeString(errorState->httpStatus) << "\"\n\t" << entry->url());
404
405 delete err;
406 err = errorState;
407
408 if (!errorState->request) {
409 errorState->request = request;
410 HTTPMSGLOCK(errorState->request);
411 }
412
413 if (err->type != ERR_ZERO_SIZE_OBJECT)
414 return;
415
416 if (pconnRace == racePossible) {
417 debugs(17, 5, HERE << "pconn race happened");
418 pconnRace = raceHappened;
419 }
420
421 if (ConnStateData *pinned_connection = request->pinnedConnection()) {
422 pinned_connection->pinning.zeroReply = true;
423 flags.dont_retry = true; // we want to propagate failure to the client
424 debugs(17, 4, "zero reply on pinned connection");
425 }
426 }
427
428 /**
429 * Frees fwdState without closing FD or generating an abort
430 */
431 void
432 FwdState::unregister(Comm::ConnectionPointer &conn)
433 {
434 debugs(17, 3, HERE << entry->url() );
435 assert(serverConnection() == conn);
436 assert(Comm::IsConnOpen(conn));
437 comm_remove_close_handler(conn->fd, fwdServerClosedWrapper, this);
438 serverConn = NULL;
439 }
440
441 // Legacy method to be removed in favor of the above as soon as possible
442 void
443 FwdState::unregister(int fd)
444 {
445 debugs(17, 3, HERE << entry->url() );
446 assert(fd == serverConnection()->fd);
447 unregister(serverConn);
448 }
449
450 /**
451 * server-side modules call fwdComplete() when they are done
452 * downloading an object. Then, we either 1) re-forward the
453 * request somewhere else if needed, or 2) call storeComplete()
454 * to finish it off
455 */
456 void
457 FwdState::complete()
458 {
459 debugs(17, 3, HERE << entry->url() << "\n\tstatus " << entry->getReply()->sline.status());
460 #if URL_CHECKSUM_DEBUG
461
462 entry->mem_obj->checkUrlChecksum();
463 #endif
464
465 logReplyStatus(n_tries, entry->getReply()->sline.status());
466
467 if (reforward()) {
468 debugs(17, 3, HERE << "re-forwarding " << entry->getReply()->sline.status() << " " << entry->url());
469
470 if (Comm::IsConnOpen(serverConn))
471 unregister(serverConn);
472
473 entry->reset();
474
475 // drop the last path off the selection list. try the next one.
476 serverDestinations.shift();
477 startConnectionOrFail();
478
479 } else {
480 if (Comm::IsConnOpen(serverConn))
481 debugs(17, 3, HERE << "server FD " << serverConnection()->fd << " not re-forwarding status " << entry->getReply()->sline.status());
482 else
483 debugs(17, 3, HERE << "server (FD closed) not re-forwarding status " << entry->getReply()->sline.status());
484 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
485 entry->complete();
486
487 if (!Comm::IsConnOpen(serverConn))
488 completed();
489
490 self = NULL; // refcounted
491 }
492 }
493
494 /**** CALLBACK WRAPPERS ************************************************************/
495
496 static void
497 fwdPeerSelectionCompleteWrapper(Comm::ConnectionList * unused, ErrorState *err, void *data)
498 {
499 FwdState *fwd = (FwdState *) data;
500 if (err)
501 fwd->fail(err);
502 fwd->startConnectionOrFail();
503 }
504
505 static void
506 fwdServerClosedWrapper(const CommCloseCbParams &params)
507 {
508 FwdState *fwd = (FwdState *)params.data;
509 fwd->serverClosed(params.fd);
510 }
511
512 #if USE_SSL
513 static void
514 fwdNegotiateSSLWrapper(int fd, void *data)
515 {
516 FwdState *fwd = (FwdState *) data;
517 fwd->negotiateSSL(fd);
518 }
519
520 #endif
521
522 void
523 fwdConnectDoneWrapper(const Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data)
524 {
525 FwdState *fwd = (FwdState *) data;
526 fwd->connectDone(conn, status, xerrno);
527 }
528
529 /**** PRIVATE *****************************************************************/
530
531 /*
532 * FwdState::checkRetry
533 *
534 * Return TRUE if the request SHOULD be retried. This method is
535 * called when the HTTP connection fails, or when the connection
536 * is closed before server-side read the end of HTTP headers.
537 */
538 bool
539 FwdState::checkRetry()
540 {
541 if (shutting_down)
542 return false;
543
544 if (!self) { // we have aborted before the server called us back
545 debugs(17, 5, HERE << "not retrying because of earlier abort");
546 // we will be destroyed when the server clears its Pointer to us
547 return false;
548 }
549
550 if (entry->store_status != STORE_PENDING)
551 return false;
552
553 if (!entry->isEmpty())
554 return false;
555
556 if (n_tries > Config.forward_max_tries)
557 return false;
558
559 if (squid_curtime - start_t > Config.Timeout.forward)
560 return false;
561
562 if (flags.dont_retry)
563 return false;
564
565 if (request->bodyNibbled())
566 return false;
567
568 // NP: not yet actually connected anywhere. retry is safe.
569 if (!flags.connected_okay)
570 return true;
571
572 if (!checkRetriable())
573 return false;
574
575 return true;
576 }
577
578 /*
579 * FwdState::checkRetriable
580 *
581 * Return TRUE if this is the kind of request that can be retried
582 * after a failure. If the request is not retriable then we don't
583 * want to risk sending it on a persistent connection. Instead we'll
584 * force it to go on a new HTTP connection.
585 */
586 bool
587 FwdState::checkRetriable()
588 {
589 // Optimize: A compliant proxy may retry PUTs, but Squid lacks the [rather
590 // complicated] code required to protect the PUT request body from being
591 // nibbled during the first try. Thus, Squid cannot retry some PUTs today.
592 if (request->body_pipe != NULL)
593 return false;
594
595 // RFC2616 9.1 Safe and Idempotent Methods
596 return (request->method.isHttpSafe() || request->method.isIdempotent());
597 }
598
599 void
600 FwdState::serverClosed(int fd)
601 {
602 debugs(17, 2, HERE << "FD " << fd << " " << entry->url());
603 retryOrBail();
604 }
605
606 void
607 FwdState::retryOrBail()
608 {
609 if (checkRetry()) {
610 debugs(17, 3, HERE << "re-forwarding (" << n_tries << " tries, " << (squid_curtime - start_t) << " secs)");
611 // we should retry the same destination if it failed due to pconn race
612 if (pconnRace == raceHappened)
613 debugs(17, 4, HERE << "retrying the same destination");
614 else
615 serverDestinations.shift(); // last one failed. try another.
616 startConnectionOrFail();
617 return;
618 }
619
620 // TODO: should we call completed() here and move doneWithRetries there?
621 doneWithRetries();
622
623 if (self != NULL && !err && shutting_down) {
624 ErrorState *anErr = new ErrorState(ERR_SHUTTING_DOWN, Http::scServiceUnavailable, request);
625 errorAppendEntry(entry, anErr);
626 }
627
628 self = NULL; // refcounted
629 }
630
631 // If the Server quits before nibbling at the request body, the body sender
632 // will not know (so that we can retry). Call this if we will not retry. We
633 // will notify the sender so that it does not get stuck waiting for space.
634 void
635 FwdState::doneWithRetries()
636 {
637 if (request && request->body_pipe != NULL)
638 request->body_pipe->expectNoConsumption();
639 }
640
641 // called by the server that failed after calling unregister()
642 void
643 FwdState::handleUnregisteredServerEnd()
644 {
645 debugs(17, 2, HERE << "self=" << self << " err=" << err << ' ' << entry->url());
646 assert(!Comm::IsConnOpen(serverConn));
647 retryOrBail();
648 }
649
650 #if USE_SSL
651 void
652 FwdState::negotiateSSL(int fd)
653 {
654 unsigned long ssl_lib_error = SSL_ERROR_NONE;
655 SSL *ssl = fd_table[fd].ssl;
656 int ret;
657
658 if ((ret = SSL_connect(ssl)) <= 0) {
659 int ssl_error = SSL_get_error(ssl, ret);
660 #ifdef EPROTO
661 int sysErrNo = EPROTO;
662 #else
663 int sysErrNo = EACCES;
664 #endif
665
666 switch (ssl_error) {
667
668 case SSL_ERROR_WANT_READ:
669 Comm::SetSelect(fd, COMM_SELECT_READ, fwdNegotiateSSLWrapper, this, 0);
670 return;
671
672 case SSL_ERROR_WANT_WRITE:
673 Comm::SetSelect(fd, COMM_SELECT_WRITE, fwdNegotiateSSLWrapper, this, 0);
674 return;
675
676 case SSL_ERROR_SSL:
677 case SSL_ERROR_SYSCALL:
678 ssl_lib_error = ERR_get_error();
679 debugs(81, DBG_IMPORTANT, "fwdNegotiateSSL: Error negotiating SSL connection on FD " << fd <<
680 ": " << ERR_error_string(ssl_lib_error, NULL) << " (" << ssl_error <<
681 "/" << ret << "/" << errno << ")");
682
683 // store/report errno when ssl_error is SSL_ERROR_SYSCALL, ssl_lib_error is 0, and ret is -1
684 if (ssl_error == SSL_ERROR_SYSCALL && ret == -1 && ssl_lib_error == 0)
685 sysErrNo = errno;
686
687 // falling through to complete error handling
688
689 default:
690 // TODO: move into a method before merge
691 Ssl::ErrorDetail *errDetails;
692 Ssl::ErrorDetail *errFromFailure = (Ssl::ErrorDetail *)SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail);
693 if (errFromFailure != NULL) {
694 // The errFromFailure is attached to the ssl object
695 // and will be released when ssl object destroyed.
696 // Copy errFromFailure to a new Ssl::ErrorDetail object.
697 errDetails = new Ssl::ErrorDetail(*errFromFailure);
698 } else {
699 // server_cert can be NULL here
700 X509 *server_cert = SSL_get_peer_certificate(ssl);
701 errDetails = new Ssl::ErrorDetail(SQUID_ERR_SSL_HANDSHAKE, server_cert, NULL);
702 X509_free(server_cert);
703 }
704
705 if (ssl_lib_error != SSL_ERROR_NONE)
706 errDetails->setLibError(ssl_lib_error);
707
708 if (request->clientConnectionManager.valid()) {
709 // remember the server certificate from the ErrorDetail object
710 if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
711 serverBump->serverCert.resetAndLock(errDetails->peerCert());
712
713 // remember validation errors, if any
714 if (Ssl::CertErrors *errs = static_cast<Ssl::CertErrors*>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors)))
715 serverBump->sslErrors = cbdataReference(errs);
716 }
717 }
718
719 // For intercepted connections, set the host name to the server
720 // certificate CN. Otherwise, we just hope that CONNECT is using
721 // a user-entered address (a host name or a user-entered IP).
722 const bool isConnectRequest = !request->clientConnectionManager->port->flags.isIntercepted();
723 if (request->flags.sslPeek && !isConnectRequest) {
724 if (X509 *srvX509 = errDetails->peerCert()) {
725 if (const char *name = Ssl::CommonHostName(srvX509)) {
726 request->SetHost(name);
727 debugs(83, 3, HERE << "reset request host: " << name);
728 }
729 }
730 }
731
732 ErrorState *const anErr = makeConnectingError(ERR_SECURE_CONNECT_FAIL);
733 anErr->xerrno = sysErrNo;
734 anErr->detail = errDetails;
735 fail(anErr);
736
737 if (serverConnection()->getPeer()) {
738 peerConnectFailed(serverConnection()->getPeer());
739 }
740
741 serverConn->close();
742 return;
743 }
744 }
745
746 if (request->clientConnectionManager.valid()) {
747 // remember the server certificate from the ErrorDetail object
748 if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
749 serverBump->serverCert.reset(SSL_get_peer_certificate(ssl));
750
751 // remember validation errors, if any
752 if (Ssl::CertErrors *errs = static_cast<Ssl::CertErrors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors)))
753 serverBump->sslErrors = cbdataReference(errs);
754 }
755 }
756
757 if (serverConnection()->getPeer() && !SSL_session_reused(ssl)) {
758 if (serverConnection()->getPeer()->sslSession)
759 SSL_SESSION_free(serverConnection()->getPeer()->sslSession);
760
761 serverConnection()->getPeer()->sslSession = SSL_get1_session(ssl);
762 }
763
764 if (Ssl::TheConfig.ssl_crt_validator) {
765 Ssl::CertValidationRequest validationRequest;
766 // WARNING: Currently we do not use any locking for any of the
767 // members of the Ssl::CertValidationRequest class. In this code the
768 // Ssl::CertValidationRequest object used only to pass data to
769 // Ssl::CertValidationHelper::submit method.
770 validationRequest.ssl = ssl;
771 validationRequest.domainName = request->GetHost();
772 if (Ssl::CertErrors *errs = static_cast<Ssl::CertErrors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors)))
773 // validationRequest disappears on return so no need to cbdataReference
774 validationRequest.errors = errs;
775 else
776 validationRequest.errors = NULL;
777 try {
778 debugs(83, 5, "Sending SSL certificate for validation to ssl_crtvd.");
779 Ssl::CertValidationHelper::GetInstance()->sslSubmit(validationRequest, sslCrtvdHandleReplyWrapper, this);
780 return;
781 } catch (const std::exception &e) {
782 debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtvd " <<
783 "request for " << validationRequest.domainName <<
784 " certificate: " << e.what() << "; will now block to " <<
785 "validate that certificate.");
786 // fall through to do blocking in-process generation.
787 ErrorState *anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request);
788 fail(anErr);
789 if (serverConnection()->getPeer()) {
790 peerConnectFailed(serverConnection()->getPeer());
791 }
792 serverConn->close();
793 self = NULL;
794 return;
795 }
796 }
797
798 dispatch();
799 }
800
801 void
802 FwdState::sslCrtvdHandleReplyWrapper(void *data, Ssl::CertValidationResponse const &validationResponse)
803 {
804 FwdState * fwd = (FwdState *)(data);
805 fwd->sslCrtvdHandleReply(validationResponse);
806 }
807
808 void
809 FwdState::sslCrtvdHandleReply(Ssl::CertValidationResponse const &validationResponse)
810 {
811 Ssl::CertErrors *errs = NULL;
812 Ssl::ErrorDetail *errDetails = NULL;
813 bool validatorFailed = false;
814 if (!Comm::IsConnOpen(serverConnection())) {
815 return;
816 }
817
818 debugs(83,5, request->GetHost() << " cert validation result: " << validationResponse.resultCode);
819
820 if (validationResponse.resultCode == HelperReply::Error)
821 errs = sslCrtvdCheckForErrors(validationResponse, errDetails);
822 else if (validationResponse.resultCode != HelperReply::Okay)
823 validatorFailed = true;
824
825 if (!errDetails && !validatorFailed) {
826 dispatch();
827 return;
828 }
829
830 ErrorState *anErr = NULL;
831 if (validatorFailed) {
832 anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request);
833 } else {
834
835 // Check the list error with
836 if (errDetails && request->clientConnectionManager.valid()) {
837 // remember the server certificate from the ErrorDetail object
838 if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
839 // remember validation errors, if any
840 if (errs) {
841 if (serverBump->sslErrors)
842 cbdataReferenceDone(serverBump->sslErrors);
843 serverBump->sslErrors = cbdataReference(errs);
844 }
845 }
846 }
847
848 anErr = makeConnectingError(ERR_SECURE_CONNECT_FAIL);
849 anErr->detail = errDetails;
850 /*anErr->xerrno= Should preserved*/
851 }
852
853 fail(anErr);
854 if (serverConnection()->getPeer()) {
855 peerConnectFailed(serverConnection()->getPeer());
856 }
857 serverConn->close();
858 self = NULL;
859 return;
860 }
861
862 /// Checks errors in the cert. validator response against sslproxy_cert_error.
863 /// The first honored error, if any, is returned via errDetails parameter.
864 /// The method returns all seen errors except SSL_ERROR_NONE as Ssl::CertErrors.
865 Ssl::CertErrors *
866 FwdState::sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &resp, Ssl::ErrorDetail *& errDetails)
867 {
868 Ssl::CertErrors *errs = NULL;
869
870 ACLFilledChecklist *check = NULL;
871 if (acl_access *acl = Config.ssl_client.cert_error)
872 check = new ACLFilledChecklist(acl, request, dash_str);
873
874 SSL *ssl = fd_table[serverConnection()->fd].ssl;
875 typedef Ssl::CertValidationResponse::RecvdErrors::const_iterator SVCRECI;
876 for (SVCRECI i = resp.errors.begin(); i != resp.errors.end(); ++i) {
877 debugs(83, 7, "Error item: " << i->error_no << " " << i->error_reason);
878
879 assert(i->error_no != SSL_ERROR_NONE);
880
881 if (!errDetails) {
882 bool allowed = false;
883 if (check) {
884 check->sslErrors = new Ssl::CertErrors(Ssl::CertError(i->error_no, i->cert.get()));
885 if (check->fastCheck() == ACCESS_ALLOWED)
886 allowed = true;
887 }
888 // else the Config.ssl_client.cert_error access list is not defined
889 // and the first error will cause the error page
890
891 if (allowed) {
892 debugs(83, 3, "bypassing SSL error " << i->error_no << " in " << "buffer");
893 } else {
894 debugs(83, 5, "confirming SSL error " << i->error_no);
895 X509 *brokenCert = i->cert.get();
896 Ssl::X509_Pointer peerCert(SSL_get_peer_certificate(ssl));
897 const char *aReason = i->error_reason.empty() ? NULL : i->error_reason.c_str();
898 errDetails = new Ssl::ErrorDetail(i->error_no, peerCert.get(), brokenCert, aReason);
899 }
900 if (check) {
901 delete check->sslErrors;
902 check->sslErrors = NULL;
903 }
904 }
905
906 if (!errs)
907 errs = new Ssl::CertErrors(Ssl::CertError(i->error_no, i->cert.get()));
908 else
909 errs->push_back_unique(Ssl::CertError(i->error_no, i->cert.get()));
910 }
911 if (check)
912 delete check;
913
914 return errs;
915 }
916
917 void
918 FwdState::initiateSSL()
919 {
920 SSL *ssl;
921 SSL_CTX *sslContext = NULL;
922 const CachePeer *peer = serverConnection()->getPeer();
923 int fd = serverConnection()->fd;
924
925 if (peer) {
926 assert(peer->use_ssl);
927 sslContext = peer->sslContext;
928 } else {
929 sslContext = Config.ssl_client.sslContext;
930 }
931
932 assert(sslContext);
933
934 if ((ssl = SSL_new(sslContext)) == NULL) {
935 debugs(83, DBG_IMPORTANT, "fwdInitiateSSL: Error allocating handle: " << ERR_error_string(ERR_get_error(), NULL) );
936 ErrorState *anErr = new ErrorState(ERR_SOCKET_FAILURE, Http::scInternalServerError, request);
937 // TODO: create Ssl::ErrorDetail with OpenSSL-supplied error code
938 fail(anErr);
939 self = NULL; // refcounted
940 return;
941 }
942
943 SSL_set_fd(ssl, fd);
944
945 if (peer) {
946 if (peer->ssldomain)
947 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->ssldomain);
948
949 #if NOT_YET
950
951 else if (peer->name)
952 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->name);
953
954 #endif
955
956 else
957 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->host);
958
959 if (peer->sslSession)
960 SSL_set_session(ssl, peer->sslSession);
961
962 } else {
963 // While we are peeking at the certificate, we may not know the server
964 // name that the client will request (after interception or CONNECT)
965 // unless it was the CONNECT request with a user-typed address.
966 const char *hostname = request->GetHost();
967 const bool hostnameIsIp = request->GetHostIsNumeric();
968 const bool isConnectRequest = !request->clientConnectionManager->port->flags.isIntercepted();
969 if (!request->flags.sslPeek || isConnectRequest)
970 SSL_set_ex_data(ssl, ssl_ex_index_server, (void*)hostname);
971
972 // Use SNI TLS extension only when we connect directly
973 // to the origin server and we know the server host name.
974 if (!hostnameIsIp)
975 Ssl::setClientSNI(ssl, hostname);
976 }
977
978 // If CertValidation Helper used do not lookup checklist for errors,
979 // but keep a list of errors to send it to CertValidator
980 if (!Ssl::TheConfig.ssl_crt_validator) {
981 // Create the ACL check list now, while we have access to more info.
982 // The list is used in ssl_verify_cb() and is freed in ssl_free().
983 if (acl_access *acl = Config.ssl_client.cert_error) {
984 ACLFilledChecklist *check = new ACLFilledChecklist(acl, request, dash_str);
985 SSL_set_ex_data(ssl, ssl_ex_index_cert_error_check, check);
986 }
987 }
988
989 // store peeked cert to check SQUID_X509_V_ERR_CERT_CHANGE
990 X509 *peeked_cert;
991 if (request->clientConnectionManager.valid() &&
992 request->clientConnectionManager->serverBump() &&
993 (peeked_cert = request->clientConnectionManager->serverBump()->serverCert.get())) {
994 CRYPTO_add(&(peeked_cert->references),1,CRYPTO_LOCK_X509);
995 SSL_set_ex_data(ssl, ssl_ex_index_ssl_peeked_cert, peeked_cert);
996 }
997
998 fd_table[fd].ssl = ssl;
999 fd_table[fd].read_method = &ssl_read_method;
1000 fd_table[fd].write_method = &ssl_write_method;
1001 negotiateSSL(fd);
1002 }
1003
1004 #endif
1005
1006 void
1007 FwdState::connectDone(const Comm::ConnectionPointer &conn, comm_err_t status, int xerrno)
1008 {
1009 if (status != COMM_OK) {
1010 ErrorState *const anErr = makeConnectingError(ERR_CONNECT_FAIL);
1011 anErr->xerrno = xerrno;
1012 fail(anErr);
1013
1014 /* it might have been a timeout with a partially open link */
1015 if (conn != NULL) {
1016 if (conn->getPeer())
1017 peerConnectFailed(conn->getPeer());
1018
1019 conn->close();
1020 }
1021 retryOrBail();
1022 return;
1023 }
1024
1025 serverConn = conn;
1026 flags.connected_okay = true;
1027
1028 debugs(17, 3, HERE << serverConnection() << ": '" << entry->url() << "'" );
1029
1030 comm_add_close_handler(serverConnection()->fd, fwdServerClosedWrapper, this);
1031
1032 if (serverConnection()->getPeer())
1033 peerConnectSucceded(serverConnection()->getPeer());
1034
1035 #if USE_SSL
1036 if (!request->flags.pinned) {
1037 if ((serverConnection()->getPeer() && serverConnection()->getPeer()->use_ssl) ||
1038 (!serverConnection()->getPeer() && request->protocol == AnyP::PROTO_HTTPS) ||
1039 request->flags.sslPeek) {
1040 initiateSSL();
1041 return;
1042 }
1043 }
1044 #endif
1045
1046 const CbcPointer<ConnStateData> &clientConnState =
1047 request->clientConnectionManager;
1048 if (clientConnState->isFtp) {
1049 clientConnState->pinConnection(serverConnection(), request,
1050 serverConnection()->getPeer(), false);
1051 }
1052
1053 dispatch();
1054 }
1055
1056 void
1057 FwdState::connectTimeout(int fd)
1058 {
1059 debugs(17, 2, "fwdConnectTimeout: FD " << fd << ": '" << entry->url() << "'" );
1060 assert(serverDestinations[0] != NULL);
1061 assert(fd == serverDestinations[0]->fd);
1062
1063 if (entry->isEmpty()) {
1064 ErrorState *anErr = new ErrorState(ERR_CONNECT_FAIL, Http::scGateway_Timeout, request);
1065 anErr->xerrno = ETIMEDOUT;
1066 fail(anErr);
1067
1068 /* This marks the peer DOWN ... */
1069 if (serverDestinations[0]->getPeer())
1070 peerConnectFailed(serverDestinations[0]->getPeer());
1071 }
1072
1073 if (Comm::IsConnOpen(serverDestinations[0])) {
1074 serverDestinations[0]->close();
1075 }
1076 }
1077
1078 /**
1079 * Called after Forwarding path selection (via peer select) has taken place.
1080 * And whenever forwarding needs to attempt a new connection (routing failover)
1081 * We have a vector of possible localIP->remoteIP paths now ready to start being connected.
1082 */
1083 void
1084 FwdState::connectStart()
1085 {
1086 assert(serverDestinations.size() > 0);
1087
1088 debugs(17, 3, "fwdConnectStart: " << entry->url());
1089
1090 if (!request->hier.first_conn_start.tv_sec) // first attempt
1091 request->hier.first_conn_start = current_time;
1092
1093 /* connection timeout */
1094 int ctimeout;
1095 if (serverDestinations[0]->getPeer()) {
1096 ctimeout = serverDestinations[0]->getPeer()->connect_timeout > 0 ?
1097 serverDestinations[0]->getPeer()->connect_timeout : Config.Timeout.peer_connect;
1098 } else {
1099 ctimeout = Config.Timeout.connect;
1100 }
1101
1102 /* calculate total forwarding timeout ??? */
1103 int ftimeout = Config.Timeout.forward - (squid_curtime - start_t);
1104 if (ftimeout < 0)
1105 ftimeout = 5;
1106
1107 if (ftimeout < ctimeout)
1108 ctimeout = ftimeout;
1109
1110 if (serverDestinations[0]->getPeer() && request->flags.sslBumped) {
1111 debugs(50, 4, "fwdConnectStart: Ssl bumped connections through parrent proxy are not allowed");
1112 ErrorState *anErr = new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request);
1113 fail(anErr);
1114 self = NULL; // refcounted
1115 return;
1116 }
1117
1118 request->flags.pinned = false; // XXX: what if the ConnStateData set this to flag existing credentials?
1119 // XXX: answer: the peer selection *should* catch it and give us only the pinned peer. so we reverse the =0 step below.
1120 // XXX: also, logs will now lie if pinning is broken and leads to an error message.
1121 if (serverDestinations[0]->peerType == PINNED) {
1122 ConnStateData *pinned_connection = request->pinnedConnection();
1123 debugs(17,7, "pinned peer connection: " << pinned_connection);
1124 // pinned_connection may become nil after a pconn race
1125 if (pinned_connection)
1126 serverConn = pinned_connection->validatePinnedConnection(request, serverDestinations[0]->getPeer());
1127 else
1128 serverConn = NULL;
1129 if (Comm::IsConnOpen(serverConn)) {
1130 flags.connected_okay = true;
1131 ++n_tries;
1132 request->hier.note(serverConn, request->GetHost());
1133 request->flags.pinned = true;
1134 if (pinned_connection->pinnedAuth())
1135 request->flags.auth = true;
1136 comm_add_close_handler(serverConn->fd, fwdServerClosedWrapper, this);
1137 // the server may close the pinned connection before this request
1138 pconnRace = racePossible;
1139 dispatch();
1140 return;
1141 }
1142 // Pinned connection failure.
1143 debugs(17,2,HERE << "Pinned connection failed: " << pinned_connection);
1144 ErrorState *anErr = new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, request);
1145 fail(anErr);
1146 self = NULL; // refcounted
1147 return;
1148 }
1149
1150 // Use pconn to avoid opening a new connection.
1151 const char *host = NULL;
1152 if (!serverDestinations[0]->getPeer())
1153 host = request->GetHost();
1154
1155 Comm::ConnectionPointer temp;
1156 // Avoid pconns after races so that the same client does not suffer twice.
1157 // This does not increase the total number of connections because we just
1158 // closed the connection that failed the race. And re-pinning assumes this.
1159 if (pconnRace != raceHappened)
1160 temp = fwdPconnPool->pop(serverDestinations[0], host, checkRetriable());
1161
1162 const bool openedPconn = Comm::IsConnOpen(temp);
1163 pconnRace = openedPconn ? racePossible : raceImpossible;
1164
1165 // if we found an open persistent connection to use. use it.
1166 if (openedPconn) {
1167 serverConn = temp;
1168 flags.connected_okay = true;
1169 debugs(17, 3, HERE << "reusing pconn " << serverConnection());
1170 ++n_tries;
1171
1172 comm_add_close_handler(serverConnection()->fd, fwdServerClosedWrapper, this);
1173
1174 /* Update server side TOS and Netfilter mark on the connection. */
1175 if (Ip::Qos::TheConfig.isAclTosActive()) {
1176 const tos_t tos = GetTosToServer(request);
1177 Ip::Qos::setSockTos(temp, tos);
1178 }
1179 #if SO_MARK
1180 if (Ip::Qos::TheConfig.isAclNfmarkActive()) {
1181 const nfmark_t nfmark = GetNfmarkToServer(request);
1182 Ip::Qos::setSockNfmark(temp, nfmark);
1183 }
1184 #endif
1185
1186 dispatch();
1187 return;
1188 }
1189
1190 // We will try to open a new connection, possibly to the same destination.
1191 // We reset serverDestinations[0] in case we are using it again because
1192 // ConnOpener modifies its destination argument.
1193 serverDestinations[0]->local.port(0);
1194 serverConn = NULL;
1195
1196 #if URL_CHECKSUM_DEBUG
1197 entry->mem_obj->checkUrlChecksum();
1198 #endif
1199
1200 /* Get the server side TOS and Netfilter mark to be set on the connection. */
1201 if (Ip::Qos::TheConfig.isAclTosActive()) {
1202 serverDestinations[0]->tos = GetTosToServer(request);
1203 }
1204 #if SO_MARK && USE_LIBCAP
1205 serverDestinations[0]->nfmark = GetNfmarkToServer(request);
1206 debugs(17, 3, "fwdConnectStart: got outgoing addr " << serverDestinations[0]->local << ", tos " << int(serverDestinations[0]->tos)
1207 << ", netfilter mark " << serverDestinations[0]->nfmark);
1208 #else
1209 serverDestinations[0]->nfmark = 0;
1210 debugs(17, 3, "fwdConnectStart: got outgoing addr " << serverDestinations[0]->local << ", tos " << int(serverDestinations[0]->tos));
1211 #endif
1212
1213 calls.connector = commCbCall(17,3, "fwdConnectDoneWrapper", CommConnectCbPtrFun(fwdConnectDoneWrapper, this));
1214 Comm::ConnOpener *cs = new Comm::ConnOpener(serverDestinations[0], calls.connector, ctimeout);
1215 if (host)
1216 cs->setHost(host);
1217 AsyncJob::Start(cs);
1218 }
1219
1220 void
1221 FwdState::dispatch()
1222 {
1223 debugs(17, 3, HERE << clientConn << ": Fetching '" << RequestMethodStr(request->method) << " " << entry->url() << "'");
1224 /*
1225 * Assert that server_fd is set. This is to guarantee that fwdState
1226 * is attached to something and will be deallocated when server_fd
1227 * is closed.
1228 */
1229 assert(Comm::IsConnOpen(serverConn));
1230
1231 fd_note(serverConnection()->fd, entry->url());
1232
1233 fd_table[serverConnection()->fd].noteUse(fwdPconnPool);
1234
1235 /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
1236 assert(entry->ping_status != PING_WAITING);
1237
1238 assert(entry->lock_count);
1239
1240 EBIT_SET(entry->flags, ENTRY_DISPATCHED);
1241
1242 netdbPingSite(request->GetHost());
1243
1244 /* Retrieves remote server TOS or MARK value, and stores it as part of the
1245 * original client request FD object. It is later used to forward
1246 * remote server's TOS/MARK in the response to the client in case of a MISS.
1247 */
1248 if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
1249 if (Comm::IsConnOpen(clientConn) && Comm::IsConnOpen(serverConnection())) {
1250 fde * clientFde = &fd_table[clientConn->fd]; // XXX: move the fd_table access into Ip::Qos
1251 /* Get the netfilter mark for the connection */
1252 Ip::Qos::getNfmarkFromServer(serverConnection(), clientFde);
1253 }
1254 }
1255
1256 #if _SQUID_LINUX_
1257 /* Bug 2537: The TOS forward part of QOS only applies to patched Linux kernels. */
1258 if (Ip::Qos::TheConfig.isHitTosActive()) {
1259 if (Comm::IsConnOpen(clientConn)) {
1260 fde * clientFde = &fd_table[clientConn->fd]; // XXX: move the fd_table access into Ip::Qos
1261 /* Get the TOS value for the packet */
1262 Ip::Qos::getTosFromServer(serverConnection(), clientFde);
1263 }
1264 }
1265 #endif
1266
1267 #if USE_SSL
1268 if (request->flags.sslPeek) {
1269 CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
1270 ConnStateData::httpsPeeked, serverConnection());
1271 unregister(serverConn); // async call owns it now
1272 complete(); // destroys us
1273 return;
1274 }
1275 #endif
1276
1277 if (serverConnection()->getPeer() != NULL) {
1278 ++ serverConnection()->getPeer()->stats.fetches;
1279 request->peer_login = serverConnection()->getPeer()->login;
1280 request->peer_domain = serverConnection()->getPeer()->domain;
1281 httpStart(this);
1282 } else {
1283 assert(!request->flags.sslPeek);
1284 request->peer_login = NULL;
1285 request->peer_domain = NULL;
1286
1287 switch (request->protocol) {
1288 #if USE_SSL
1289
1290 case AnyP::PROTO_HTTPS:
1291 httpStart(this);
1292 break;
1293 #endif
1294
1295 case AnyP::PROTO_HTTP:
1296 httpStart(this);
1297 break;
1298
1299 case AnyP::PROTO_GOPHER:
1300 gopherStart(this);
1301 break;
1302
1303 case AnyP::PROTO_FTP:
1304 if (request->clientConnectionManager->isFtp)
1305 ftpGatewayServerStart(this);
1306 else
1307 ftpStart(this);
1308 break;
1309
1310 case AnyP::PROTO_CACHE_OBJECT:
1311
1312 case AnyP::PROTO_INTERNAL:
1313
1314 case AnyP::PROTO_URN:
1315 fatal_dump("Should never get here");
1316 break;
1317
1318 case AnyP::PROTO_WHOIS:
1319 whoisStart(this);
1320 break;
1321
1322 case AnyP::PROTO_WAIS: /* Not implemented */
1323
1324 default:
1325 debugs(17, DBG_IMPORTANT, "WARNING: Cannot retrieve '" << entry->url() << "'.");
1326 ErrorState *anErr = new ErrorState(ERR_UNSUP_REQ, Http::scBadRequest, request);
1327 fail(anErr);
1328 // Set the dont_retry flag because this is not a transient (network) error.
1329 flags.dont_retry = true;
1330 if (Comm::IsConnOpen(serverConn)) {
1331 serverConn->close();
1332 }
1333 break;
1334 }
1335 }
1336 }
1337
1338 /*
1339 * FwdState::reforward
1340 *
1341 * returns TRUE if the transaction SHOULD be re-forwarded to the
1342 * next choice in the serverDestinations list. This method is called when
1343 * server-side communication completes normally, or experiences
1344 * some error after receiving the end of HTTP headers.
1345 */
1346 int
1347 FwdState::reforward()
1348 {
1349 StoreEntry *e = entry;
1350
1351 if (EBIT_TEST(e->flags, ENTRY_ABORTED)) {
1352 debugs(17, 3, HERE << "entry aborted");
1353 return 0;
1354 }
1355
1356 assert(e->store_status == STORE_PENDING);
1357 assert(e->mem_obj);
1358 #if URL_CHECKSUM_DEBUG
1359
1360 e->mem_obj->checkUrlChecksum();
1361 #endif
1362
1363 debugs(17, 3, HERE << e->url() << "?" );
1364
1365 if (!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) {
1366 debugs(17, 3, HERE << "No, ENTRY_FWD_HDR_WAIT isn't set");
1367 return 0;
1368 }
1369
1370 if (n_tries > Config.forward_max_tries)
1371 return 0;
1372
1373 if (request->bodyNibbled())
1374 return 0;
1375
1376 if (serverDestinations.size() <= 1) {
1377 // NP: <= 1 since total count includes the recently failed one.
1378 debugs(17, 3, HERE << "No alternative forwarding paths left");
1379 return 0;
1380 }
1381
1382 const Http::StatusCode s = e->getReply()->sline.status();
1383 debugs(17, 3, HERE << "status " << s);
1384 return reforwardableStatus(s);
1385 }
1386
1387 /**
1388 * Create "503 Service Unavailable" or "504 Gateway Timeout" error depending
1389 * on whether this is a validation request. RFC 2616 says that we MUST reply
1390 * with "504 Gateway Timeout" if validation fails and cached reply has
1391 * proxy-revalidate, must-revalidate or s-maxage Cache-Control directive.
1392 */
1393 ErrorState *
1394 FwdState::makeConnectingError(const err_type type) const
1395 {
1396 return new ErrorState(type, request->flags.needValidation ?
1397 Http::scGateway_Timeout : Http::scServiceUnavailable, request);
1398 }
1399
1400 static void
1401 fwdStats(StoreEntry * s)
1402 {
1403 int i;
1404 int j;
1405 storeAppendPrintf(s, "Status");
1406
1407 for (j = 1; j < MAX_FWD_STATS_IDX; ++j) {
1408 storeAppendPrintf(s, "\ttry#%d", j);
1409 }
1410
1411 storeAppendPrintf(s, "\n");
1412
1413 for (i = 0; i <= (int) Http::scInvalidHeader; ++i) {
1414 if (FwdReplyCodes[0][i] == 0)
1415 continue;
1416
1417 storeAppendPrintf(s, "%3d", i);
1418
1419 for (j = 0; j <= MAX_FWD_STATS_IDX; ++j) {
1420 storeAppendPrintf(s, "\t%d", FwdReplyCodes[j][i]);
1421 }
1422
1423 storeAppendPrintf(s, "\n");
1424 }
1425 }
1426
1427 /**** STATIC MEMBER FUNCTIONS *************************************************/
1428
1429 bool
1430 FwdState::reforwardableStatus(const Http::StatusCode s) const
1431 {
1432 switch (s) {
1433
1434 case Http::scBadGateway:
1435
1436 case Http::scGateway_Timeout:
1437 return true;
1438
1439 case Http::scForbidden:
1440
1441 case Http::scInternalServerError:
1442
1443 case Http::scNotImplemented:
1444
1445 case Http::scServiceUnavailable:
1446 return Config.retry.onerror;
1447
1448 default:
1449 return false;
1450 }
1451
1452 /* NOTREACHED */
1453 }
1454
1455 /**
1456 * Decide where details need to be gathered to correctly describe a persistent connection.
1457 * What is needed:
1458 * - the address/port details about this link
1459 * - domain name of server at other end of this link (either peer or requested host)
1460 */
1461 void
1462 FwdState::pconnPush(Comm::ConnectionPointer &conn, const char *domain)
1463 {
1464 if (conn->getPeer()) {
1465 fwdPconnPool->push(conn, NULL);
1466 } else {
1467 fwdPconnPool->push(conn, domain);
1468 }
1469 }
1470
1471 void
1472 FwdState::initModule()
1473 {
1474 RegisterWithCacheManager();
1475 }
1476
1477 void
1478 FwdState::RegisterWithCacheManager(void)
1479 {
1480 Mgr::RegisterAction("forward", "Request Forwarding Statistics", fwdStats, 0, 1);
1481 }
1482
1483 void
1484 FwdState::logReplyStatus(int tries, const Http::StatusCode status)
1485 {
1486 if (status > Http::scInvalidHeader)
1487 return;
1488
1489 assert(tries >= 0);
1490
1491 if (tries > MAX_FWD_STATS_IDX)
1492 tries = MAX_FWD_STATS_IDX;
1493
1494 ++ FwdReplyCodes[tries][status];
1495 }
1496
1497 /**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
1498
1499 /*
1500 * DPW 2007-05-19
1501 * Formerly static, but now used by client_side_request.cc
1502 */
1503 /// Checks for a TOS value to apply depending on the ACL
1504 tos_t
1505 aclMapTOS(acl_tos * head, ACLChecklist * ch)
1506 {
1507 acl_tos *l;
1508
1509 for (l = head; l; l = l->next) {
1510 if (!l->aclList || ch->fastCheck(l->aclList) == ACCESS_ALLOWED)
1511 return l->tos;
1512 }
1513
1514 return 0;
1515 }
1516
1517 /// Checks for a netfilter mark value to apply depending on the ACL
1518 nfmark_t
1519 aclMapNfmark(acl_nfmark * head, ACLChecklist * ch)
1520 {
1521 acl_nfmark *l;
1522
1523 for (l = head; l; l = l->next) {
1524 if (!l->aclList || ch->fastCheck(l->aclList) == ACCESS_ALLOWED)
1525 return l->nfmark;
1526 }
1527
1528 return 0;
1529 }
1530
1531 void
1532 getOutgoingAddress(HttpRequest * request, Comm::ConnectionPointer conn)
1533 {
1534 // skip if an outgoing address is already set.
1535 if (!conn->local.isAnyAddr()) return;
1536
1537 // ensure that at minimum the wildcard local matches remote protocol
1538 if (conn->remote.isIPv4())
1539 conn->local.setIPv4();
1540
1541 // maybe use TPROXY client address
1542 if (request && request->flags.spoofClientIp) {
1543 if (!conn->getPeer() || !conn->getPeer()->options.no_tproxy) {
1544 #if FOLLOW_X_FORWARDED_FOR && LINUX_NETFILTER
1545 if (Config.onoff.tproxy_uses_indirect_client)
1546 conn->local = request->indirect_client_addr;
1547 else
1548 #endif
1549 conn->local = request->client_addr;
1550 // some flags need setting on the socket to use this address
1551 conn->flags |= COMM_DOBIND;
1552 conn->flags |= COMM_TRANSPARENT;
1553 return;
1554 }
1555 // else no tproxy today ...
1556 }
1557
1558 if (!Config.accessList.outgoing_address) {
1559 return; // anything will do.
1560 }
1561
1562 ACLFilledChecklist ch(NULL, request, NULL);
1563 ch.dst_peer = conn->getPeer();
1564 ch.dst_addr = conn->remote;
1565
1566 // TODO use the connection details in ACL.
1567 // needs a bit of rework in ACLFilledChecklist to use Comm::Connection instead of ConnStateData
1568
1569 AclAddress *l;
1570 for (l = Config.accessList.outgoing_address; l; l = l->next) {
1571
1572 /* check if the outgoing address is usable to the destination */
1573 if (conn->remote.isIPv4() != l->addr.isIPv4()) continue;
1574
1575 /* check ACLs for this outgoing address */
1576 if (!l->aclList || ch.fastCheck(l->aclList) == ACCESS_ALLOWED) {
1577 conn->local = l->addr;
1578 return;
1579 }
1580 }
1581 }
1582
1583 tos_t
1584 GetTosToServer(HttpRequest * request)
1585 {
1586 ACLFilledChecklist ch(NULL, request, NULL);
1587 return aclMapTOS(Ip::Qos::TheConfig.tosToServer, &ch);
1588 }
1589
1590 nfmark_t
1591 GetNfmarkToServer(HttpRequest * request)
1592 {
1593 ACLFilledChecklist ch(NULL, request, NULL);
1594 return aclMapNfmark(Ip::Qos::TheConfig.nfmarkToServer, &ch);
1595 }