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