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