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