]>
Commit | Line | Data |
---|---|---|
983061ed | 1 | /* |
bf95c10a | 2 | * Copyright (C) 1996-2022 The Squid Software Foundation and contributors |
e25c139f | 3 | * |
bbc27441 AJ |
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. | |
983061ed | 7 | */ |
983061ed | 8 | |
bbc27441 AJ |
9 | /* DEBUG: section 26 Secure Sockets Layer Proxy */ |
10 | ||
582c2af2 | 11 | #include "squid.h" |
a011edee | 12 | #include "acl/FilledChecklist.h" |
6c420975 | 13 | #include "base/CbcPointer.h" |
2b6b1bcb | 14 | #include "base/JobWait.h" |
675b8408 | 15 | #include "base/Raw.h" |
a011edee | 16 | #include "CachePeer.h" |
9fe3d316 | 17 | #include "cbdata.h" |
a011edee | 18 | #include "client_side.h" |
602d9612 | 19 | #include "client_side_request.h" |
f5e17947 | 20 | #include "clients/HttpTunneler.h" |
9f518b4a | 21 | #include "comm.h" |
cfd66529 | 22 | #include "comm/Connection.h" |
aed188fd | 23 | #include "comm/ConnOpener.h" |
7e66d5e2 | 24 | #include "comm/Read.h" |
ec41b64c | 25 | #include "comm/Write.h" |
a011edee | 26 | #include "errorpage.h" |
6821c276 | 27 | #include "fd.h" |
a011edee | 28 | #include "fde.h" |
4f3c27a1 | 29 | #include "FwdState.h" |
40f1e76d | 30 | #include "globals.h" |
55622953 | 31 | #include "HappyConnOpener.h" |
e5ee81f0 | 32 | #include "http.h" |
d3dddfb5 | 33 | #include "http/Stream.h" |
a011edee | 34 | #include "HttpRequest.h" |
9710965e | 35 | #include "icmp/net_db.h" |
6dc6127b | 36 | #include "ip/QosConfig.h" |
8a713747 | 37 | #include "LogTags.h" |
a011edee | 38 | #include "MemBuf.h" |
0ce8e93b | 39 | #include "neighbors.h" |
cfd66529 | 40 | #include "PeerSelectState.h" |
55622953 | 41 | #include "ResolvedPeers.h" |
65e41a45 | 42 | #include "sbuf/SBuf.h" |
a72b6e88 | 43 | #include "security/BlindPeerConnector.h" |
4d5904f7 | 44 | #include "SquidConfig.h" |
765afa31 | 45 | #include "StatCounters.h" |
a23223bf | 46 | #if USE_OPENSSL |
93ead3fd | 47 | #include "ssl/bio.h" |
5d65362c | 48 | #include "ssl/ServerBump.h" |
a23223bf | 49 | #endif |
4e540555 | 50 | #include "tools.h" |
8b082ed9 | 51 | #include "tunnel.h" |
582c2af2 FC |
52 | #if USE_DELAY_POOLS |
53 | #include "DelayId.h" | |
54 | #endif | |
55 | ||
074d6a40 AJ |
56 | #include <climits> |
57 | #include <cerrno> | |
582c2af2 | 58 | |
8ca1d33d AJ |
59 | /** |
60 | * TunnelStateData is the state engine performing the tasks for | |
61 | * setup of a TCP tunnel from an existing open client FD to a server | |
62 | * then shuffling binary data between the resulting FD pair. | |
63 | */ | |
64 | /* | |
65 | * TODO 1: implement a read/write API on ConnStateData to send/receive blocks | |
66 | * of pre-formatted data. Then we can use that as the client side of the tunnel | |
67 | * instead of re-implementing it here and occasionally getting the ConnStateData | |
68 | * read/write state wrong. | |
69 | * | |
70 | * TODO 2: then convert this into a AsyncJob, possibly a child of 'Server' | |
71 | */ | |
6043e368 | 72 | class TunnelStateData: public PeerSelectionInitiator |
62e76326 | 73 | { |
6043e368 | 74 | CBDATA_CHILD(TunnelStateData); |
a46d2c0e | 75 | |
76 | public: | |
6b2b6cfe | 77 | TunnelStateData(ClientHttpRequest *); |
6043e368 | 78 | virtual ~TunnelStateData(); |
8ca1d33d AJ |
79 | TunnelStateData(const TunnelStateData &); // do not implement |
80 | TunnelStateData &operator =(const TunnelStateData &); // do not implement | |
a46d2c0e | 81 | |
82 | class Connection; | |
c8407295 AJ |
83 | static void ReadClient(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data); |
84 | static void ReadServer(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data); | |
85 | static void WriteClientDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data); | |
86 | static void WriteServerDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data); | |
a46d2c0e | 87 | |
88 | bool noConnections() const; | |
25d2603f EB |
89 | /// closes both client and server connections |
90 | void closeConnections(); | |
91 | ||
983061ed | 92 | char *url; |
6c420975 | 93 | CbcPointer<ClientHttpRequest> http; |
8a70cdbb | 94 | HttpRequest::Pointer request; |
06521a10 | 95 | AccessLogEntryPointer al; |
62e76326 | 96 | |
fb046c1b | 97 | const char * getHost() const { |
5c51bffb | 98 | return (server.conn != NULL && server.conn->getPeer() ? server.conn->getPeer()->host : request->url.host()); |
fb046c1b AJ |
99 | }; |
100 | ||
e2bbd3b3 AR |
101 | /// store the given to-server connection; prohibit retries and do not look |
102 | /// for any other destinations | |
103 | void commitToServer(const Comm::ConnectionPointer &); | |
104 | ||
3ed5793b | 105 | /// Whether the client sent a CONNECT request to us. |
1c8fc082 | 106 | bool clientExpectsConnectResponse() const { |
aff439e0 | 107 | // If we are forcing a tunnel after receiving a client CONNECT, then we |
6b2b6cfe CT |
108 | // have already responded to that CONNECT before tunnel.cc started. |
109 | if (request && request->flags.forceTunnel) | |
110 | return false; | |
5d65362c CT |
111 | #if USE_OPENSSL |
112 | // We are bumping and we had already send "OK CONNECTED" | |
090f1d3c | 113 | if (http.valid() && http->getConn() && http->getConn()->serverBump() && http->getConn()->serverBump()->at(XactionStep::tlsBump2, XactionStep::tlsBump3)) |
5d65362c CT |
114 | return false; |
115 | #endif | |
1c8fc082 A |
116 | return !(request != NULL && |
117 | (request->flags.interceptTproxy || request->flags.intercepted)); | |
118 | } | |
3ed5793b | 119 | |
0ce8e93b EB |
120 | /// starts connecting to the next hop, either for the first time or while |
121 | /// recovering from the previous connect failure | |
122 | void startConnecting(); | |
25b0ce45 CT |
123 | void closePendingConnection(const Comm::ConnectionPointer &conn, const char *reason); |
124 | ||
f5e17947 | 125 | /// called when negotiations with the peer have been successfully completed |
25b0ce45 | 126 | void notePeerReadyToShovel(const Comm::ConnectionPointer &); |
f5e17947 | 127 | |
a46d2c0e | 128 | class Connection |
62e76326 | 129 | { |
a46d2c0e | 130 | |
131 | public: | |
215c41fa | 132 | Connection() : len (0), buf ((char *)xmalloc(SQUID_TCP_SO_RCVBUF)), size_ptr(NULL), delayedLoops(0), |
25d2603f | 133 | dirty(false), |
be75380c | 134 | readPending(NULL), readPendingFunc(NULL) {} |
a46d2c0e | 135 | |
8a467c4b AJ |
136 | ~Connection(); |
137 | ||
25d2603f EB |
138 | /// initiates Comm::Connection ownership, including closure monitoring |
139 | template <typename Method> | |
140 | void initConnection(const Comm::ConnectionPointer &aConn, Method method, const char *name, TunnelStateData *tunnelState); | |
141 | ||
142 | /// reacts to the external closure of our connection | |
143 | void noteClosure(); | |
144 | ||
8a467c4b | 145 | int bytesWanted(int lower=0, int upper = INT_MAX) const; |
a46d2c0e | 146 | void bytesIn(int const &); |
9a0a18de | 147 | #if USE_DELAY_POOLS |
a46d2c0e | 148 | |
149 | void setDelayId(DelayId const &); | |
150 | #endif | |
151 | ||
152 | void error(int const xerrno); | |
5c926411 | 153 | int debugLevelForError(int const xerrno) const; |
25d2603f | 154 | |
8a467c4b | 155 | void dataSent (size_t amount); |
74f35ca8 AR |
156 | /// writes 'b' buffer, setting the 'writer' member to 'callback'. |
157 | void write(const char *b, int size, AsyncCall::Pointer &callback, FREE * free_func); | |
62e76326 | 158 | int len; |
8a467c4b | 159 | char *buf; |
74f35ca8 | 160 | AsyncCall::Pointer writer; ///< pending Comm::Write callback |
ac9f46af | 161 | uint64_t *size_ptr; /* pointer to size in an ConnStateData for logging */ |
62e76326 | 162 | |
fb046c1b | 163 | Comm::ConnectionPointer conn; ///< The currently connected connection. |
9fe3d316 | 164 | uint8_t delayedLoops; ///< how many times a read on this connection has been postponed. |
fb046c1b | 165 | |
25d2603f EB |
166 | bool dirty; ///< whether write() has been called (at least once) |
167 | ||
215c41fa AJ |
168 | // XXX: make these an AsyncCall when event API can handle them |
169 | TunnelStateData *readPending; | |
170 | EVH *readPendingFunc; | |
f5e17947 | 171 | |
9a0a18de | 172 | #if USE_DELAY_POOLS |
62e76326 | 173 | |
a46d2c0e | 174 | DelayId delayId; |
59715b38 | 175 | #endif |
62e76326 | 176 | |
25d2603f EB |
177 | private: |
178 | /// the registered close handler for the connection | |
179 | AsyncCall::Pointer closer; | |
a46d2c0e | 180 | }; |
181 | ||
182 | Connection client, server; | |
8a713747 | 183 | int *status_ptr; ///< pointer for logging HTTP status |
f5e17947 | 184 | |
3248e962 | 185 | SBuf preReadClientData; |
6821c276 | 186 | SBuf preReadServerData; |
0ce8e93b | 187 | time_t startTime; ///< object creation time, before any peer selection/connection attempts |
55622953 CT |
188 | ResolvedPeersPointer destinations; ///< paths for forwarding the request |
189 | bool destinationsFound; ///< At least one candidate path found | |
25d2603f EB |
190 | /// whether another destination may be still attempted if the TCP connection |
191 | /// was unexpectedly closed | |
192 | bool retriable; | |
e2bbd3b3 AR |
193 | |
194 | /// whether the decision to tunnel to a particular destination was final | |
195 | bool committedToServer; | |
196 | ||
5106d639 EB |
197 | // TODO: remove after fixing deferred reads in TunnelStateData::copyRead() |
198 | CodeContext::Pointer codeContext; ///< our creator context | |
55622953 | 199 | |
2b6b1bcb AR |
200 | /// waits for a transport connection to the peer to be established/opened |
201 | JobWait<HappyConnOpener> transportWait; | |
202 | ||
203 | /// waits for the established transport connection to be secured/encrypted | |
204 | JobWait<Security::PeerConnector> encryptionWait; | |
205 | ||
206 | /// waits for an HTTP CONNECT tunnel through a cache_peer to be negotiated | |
207 | /// over the (encrypted, if needed) transport connection to that cache_peer | |
208 | JobWait<Http::Tunneler> peerWait; | |
3ed5793b | 209 | |
a46d2c0e | 210 | void copyRead(Connection &from, IOCB *completion); |
211 | ||
a23223bf | 212 | /// continue to set up connection to a peer, going async for SSL peers |
25b0ce45 CT |
213 | void connectToPeer(const Comm::ConnectionPointer &); |
214 | void secureConnectionToPeer(const Comm::ConnectionPointer &); | |
a23223bf | 215 | |
6043e368 AR |
216 | /* PeerSelectionInitiator API */ |
217 | virtual void noteDestination(Comm::ConnectionPointer conn) override; | |
218 | virtual void noteDestinationsEnd(ErrorState *selectionError) override; | |
219 | ||
55622953 CT |
220 | void syncHierNote(const Comm::ConnectionPointer &server, const char *origin); |
221 | ||
222 | /// called when a connection has been successfully established or | |
223 | /// when all candidate destinations have been tried and all have failed | |
224 | void noteConnection(HappyConnOpenerAnswer &); | |
225 | ||
55622953 CT |
226 | /// Start using an established connection |
227 | void connectDone(const Comm::ConnectionPointer &conn, const char *origin, const bool reused); | |
228 | ||
229 | void notifyConnOpener(); | |
230 | ||
6043e368 AR |
231 | void saveError(ErrorState *finalError); |
232 | void sendError(ErrorState *finalError, const char *reason); | |
233 | ||
a46d2c0e | 234 | private: |
0166128b | 235 | /// Gives Security::PeerConnector access to Answer in the TunnelStateData callback dialer. |
a72b6e88 | 236 | class MyAnswerDialer: public CallDialer, public Security::PeerConnector::CbDialer |
a23223bf CT |
237 | { |
238 | public: | |
fcfdf7f9 | 239 | typedef void (TunnelStateData::*Method)(Security::EncryptorAnswer &); |
a23223bf CT |
240 | |
241 | MyAnswerDialer(Method method, TunnelStateData *tunnel): | |
f53969cc | 242 | method_(method), tunnel_(tunnel), answer_() {} |
a23223bf CT |
243 | |
244 | /* CallDialer API */ | |
8b082ed9 FC |
245 | virtual bool canDial(AsyncCall &) { return tunnel_.valid(); } |
246 | void dial(AsyncCall &) { ((&(*tunnel_))->*method_)(answer_); } | |
a23223bf | 247 | virtual void print(std::ostream &os) const { |
e2849af8 A |
248 | os << '(' << tunnel_.get() << ", " << answer_ << ')'; |
249 | } | |
a23223bf | 250 | |
a72b6e88 | 251 | /* Security::PeerConnector::CbDialer API */ |
fcfdf7f9 | 252 | virtual Security::EncryptorAnswer &answer() { return answer_; } |
a23223bf CT |
253 | |
254 | private: | |
255 | Method method_; | |
256 | CbcPointer<TunnelStateData> tunnel_; | |
fcfdf7f9 | 257 | Security::EncryptorAnswer answer_; |
a23223bf | 258 | }; |
a23223bf | 259 | |
3dde9e52 CT |
260 | void usePinned(); |
261 | ||
25b0ce45 CT |
262 | /// callback handler for the Security::PeerConnector encryptor |
263 | void noteSecurityPeerConnectorAnswer(Security::EncryptorAnswer &); | |
264 | ||
265 | /// called after connection setup (including any encryption) | |
266 | void connectedToPeer(const Comm::ConnectionPointer &); | |
267 | void establishTunnelThruProxy(const Comm::ConnectionPointer &); | |
268 | ||
269 | template <typename StepStart> | |
270 | void advanceDestination(const char *stepDescription, const Comm::ConnectionPointer &conn, const StepStart &startStep); | |
fcfdf7f9 | 271 | |
25d2603f EB |
272 | /// \returns whether the request should be retried (nil) or the description why it should not |
273 | const char *checkRetry(); | |
e2bbd3b3 AR |
274 | |
275 | bool transporting() const; | |
25d2603f | 276 | |
6043e368 AR |
277 | /// details of the "last tunneling attempt" failure (if it failed) |
278 | ErrorState *savedError = nullptr; | |
279 | ||
f5e17947 CT |
280 | /// resumes operations after the (possibly failed) HTTP CONNECT exchange |
281 | void tunnelEstablishmentDone(Http::TunnelerAnswer &answer); | |
282 | ||
25d2603f EB |
283 | void deleteThis(); |
284 | ||
2b6b1bcb AR |
285 | void cancelStep(const char *reason); |
286 | ||
5c2f68b7 | 287 | public: |
c8407295 | 288 | bool keepGoingAfterRead(size_t len, Comm::Flag errcode, int xerrno, Connection &from, Connection &to); |
3ed5793b | 289 | void copy(size_t len, Connection &from, Connection &to, IOCB *); |
c8407295 AJ |
290 | void readServer(char *buf, size_t len, Comm::Flag errcode, int xerrno); |
291 | void readClient(char *buf, size_t len, Comm::Flag errcode, int xerrno); | |
292 | void writeClientDone(char *buf, size_t len, Comm::Flag flag, int xerrno); | |
293 | void writeServerDone(char *buf, size_t len, Comm::Flag flag, int xerrno); | |
3ed5793b | 294 | |
3248e962 | 295 | void copyClientBytes(); |
6821c276 | 296 | void copyServerBytes(); |
25d2603f EB |
297 | |
298 | /// handles client-to-Squid connection closure; may destroy us | |
299 | void clientClosed(); | |
300 | ||
301 | /// handles Squid-to-server connection closure; may destroy us | |
302 | void serverClosed(); | |
303 | ||
304 | /// tries connecting to another destination, if available, | |
305 | /// otherwise, initiates the transaction termination | |
306 | void retryOrBail(const char *context); | |
a46d2c0e | 307 | }; |
983061ed | 308 | |
11007d4b | 309 | static ERCB tunnelErrorComplete; |
575d05c4 AJ |
310 | static CLCB tunnelServerClosed; |
311 | static CLCB tunnelClientClosed; | |
8d77a37c | 312 | static CTCB tunnelTimeout; |
215c41fa AJ |
313 | static EVH tunnelDelayedClientRead; |
314 | static EVH tunnelDelayedServerRead; | |
30a4f2a8 | 315 | |
25d2603f | 316 | /// TunnelStateData::serverClosed() wrapper |
b8d8561b | 317 | static void |
575d05c4 | 318 | tunnelServerClosed(const CommCloseCbParams ¶ms) |
30a4f2a8 | 319 | { |
25d2603f EB |
320 | const auto tunnelState = reinterpret_cast<TunnelStateData *>(params.data); |
321 | tunnelState->serverClosed(); | |
322 | } | |
26ac0430 | 323 | |
25d2603f EB |
324 | void |
325 | TunnelStateData::serverClosed() | |
326 | { | |
327 | server.noteClosure(); | |
752fa208 AR |
328 | |
329 | retryOrBail(__FUNCTION__); | |
30a4f2a8 | 330 | } |
331 | ||
25d2603f | 332 | /// TunnelStateData::clientClosed() wrapper |
b177367b | 333 | static void |
575d05c4 | 334 | tunnelClientClosed(const CommCloseCbParams ¶ms) |
30a4f2a8 | 335 | { |
25d2603f EB |
336 | const auto tunnelState = reinterpret_cast<TunnelStateData *>(params.data); |
337 | tunnelState->clientClosed(); | |
338 | } | |
62e76326 | 339 | |
25d2603f EB |
340 | void |
341 | TunnelStateData::clientClosed() | |
342 | { | |
343 | client.noteClosure(); | |
26ac0430 | 344 | |
25d2603f EB |
345 | if (noConnections()) |
346 | return deleteThis(); | |
347 | ||
348 | if (!server.writer) | |
349 | server.conn->close(); | |
350 | } | |
351 | ||
352 | /// destroys the tunnel (after performing potentially-throwing cleanup) | |
353 | void | |
354 | TunnelStateData::deleteThis() | |
355 | { | |
356 | assert(noConnections()); | |
357 | // ConnStateData pipeline should contain the CONNECT we are performing | |
358 | // but it may be invalid already (bug 4392) | |
359 | if (const auto h = http.valid()) { | |
360 | if (const auto c = h->getConn()) | |
361 | if (const auto ctx = c->pipeline.front()) | |
362 | ctx->finished(); | |
71a2ced6 | 363 | } |
25d2603f | 364 | delete this; |
30a4f2a8 | 365 | } |
983061ed | 366 | |
6b2b6cfe | 367 | TunnelStateData::TunnelStateData(ClientHttpRequest *clientRequest) : |
f5e17947 | 368 | startTime(squid_curtime), |
55622953 | 369 | destinations(new ResolvedPeers()), |
5106d639 | 370 | destinationsFound(false), |
25d2603f | 371 | retriable(true), |
e2bbd3b3 | 372 | committedToServer(false), |
5106d639 | 373 | codeContext(CodeContext::Current()) |
8ca1d33d AJ |
374 | { |
375 | debugs(26, 3, "TunnelStateData constructed this=" << this); | |
215c41fa AJ |
376 | client.readPendingFunc = &tunnelDelayedClientRead; |
377 | server.readPendingFunc = &tunnelDelayedServerRead; | |
6b2b6cfe CT |
378 | |
379 | assert(clientRequest); | |
380 | url = xstrdup(clientRequest->uri); | |
381 | request = clientRequest->request; | |
55622953 | 382 | Must(request); |
6b2b6cfe CT |
383 | server.size_ptr = &clientRequest->out.size; |
384 | client.size_ptr = &clientRequest->al->http.clientRequestSz.payloadData; | |
385 | status_ptr = &clientRequest->al->http.code; | |
6b2b6cfe CT |
386 | al = clientRequest->al; |
387 | http = clientRequest; | |
388 | ||
25d2603f | 389 | client.initConnection(clientRequest->getConn()->clientConnection, tunnelClientClosed, "tunnelClientClosed", this); |
6b2b6cfe CT |
390 | |
391 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", | |
392 | CommTimeoutCbPtrFun(tunnelTimeout, this)); | |
393 | commSetConnTimeout(client.conn, Config.Timeout.lifetime, timeoutCall); | |
8ca1d33d AJ |
394 | } |
395 | ||
396 | TunnelStateData::~TunnelStateData() | |
983061ed | 397 | { |
8ca1d33d AJ |
398 | debugs(26, 3, "TunnelStateData destructed this=" << this); |
399 | assert(noConnections()); | |
400 | xfree(url); | |
2b6b1bcb | 401 | cancelStep("~TunnelStateData"); |
6043e368 | 402 | delete savedError; |
983061ed | 403 | } |
404 | ||
8a467c4b AJ |
405 | TunnelStateData::Connection::~Connection() |
406 | { | |
215c41fa AJ |
407 | if (readPending) |
408 | eventDelete(readPendingFunc, readPending); | |
409 | ||
8a467c4b AJ |
410 | safe_free(buf); |
411 | } | |
412 | ||
25d2603f EB |
413 | const char * |
414 | TunnelStateData::checkRetry() | |
415 | { | |
416 | if (shutting_down) | |
417 | return "shutting down"; | |
418 | if (!FwdState::EnoughTimeToReForward(startTime)) | |
419 | return "forwarding timeout"; | |
420 | if (!retriable) | |
421 | return "not retriable"; | |
422 | if (noConnections()) | |
423 | return "no connections"; | |
424 | return nullptr; | |
425 | } | |
426 | ||
427 | void | |
428 | TunnelStateData::retryOrBail(const char *context) | |
429 | { | |
25d2603f EB |
430 | assert(!server.conn); |
431 | ||
432 | const auto *bailDescription = checkRetry(); | |
433 | if (!bailDescription) { | |
434 | if (!destinations->empty()) | |
435 | return startConnecting(); // try connecting to another destination | |
436 | ||
437 | if (subscribed) { | |
438 | debugs(26, 4, "wait for more destinations to try"); | |
439 | return; // expect a noteDestination*() call | |
440 | } | |
441 | ||
442 | // fall through to bail | |
443 | } | |
444 | ||
445 | /* bail */ | |
446 | ||
447 | if (request) | |
448 | request->hier.stopPeerClock(false); | |
449 | ||
450 | // TODO: Add sendSavedErrorOr(err_type type, Http::StatusCode, context). | |
451 | // Then, the remaining method code (below) should become the common part of | |
452 | // sendNewError() and sendSavedErrorOr(), used in "error detected" cases. | |
453 | if (!savedError) | |
454 | saveError(new ErrorState(ERR_CANNOT_FORWARD, Http::scInternalServerError, request.getRaw(), al)); | |
455 | const auto canSendError = Comm::IsConnOpen(client.conn) && !client.dirty && | |
456 | clientExpectsConnectResponse(); | |
457 | if (canSendError) | |
458 | return sendError(savedError, bailDescription ? bailDescription : context); | |
459 | *status_ptr = savedError->httpStatus; | |
460 | ||
461 | if (noConnections()) | |
462 | return deleteThis(); | |
463 | ||
464 | // This is a "Comm::IsConnOpen(client.conn) but !canSendError" case. | |
465 | // Closing the connection (after finishing writing) is the best we can do. | |
466 | if (!client.writer) | |
467 | client.conn->close(); | |
468 | // else writeClientDone() must notice a closed server and close the client | |
469 | } | |
470 | ||
a46d2c0e | 471 | int |
8a467c4b | 472 | TunnelStateData::Connection::bytesWanted(int lowerbound, int upperbound) const |
447e176b | 473 | { |
9a0a18de | 474 | #if USE_DELAY_POOLS |
8a467c4b | 475 | return delayId.bytesWanted(lowerbound, upperbound); |
a46d2c0e | 476 | #else |
8b082ed9 | 477 | (void)lowerbound; |
8a467c4b | 478 | return upperbound; |
a46d2c0e | 479 | #endif |
480 | } | |
62e76326 | 481 | |
a46d2c0e | 482 | void |
fa34dd97 | 483 | TunnelStateData::Connection::bytesIn(int const &count) |
a46d2c0e | 484 | { |
bf95c10a | 485 | debugs(26, 3, "len=" << len << " + count=" << count); |
9a0a18de | 486 | #if USE_DELAY_POOLS |
a46d2c0e | 487 | delayId.bytesIn(count); |
488 | #endif | |
62e76326 | 489 | |
a46d2c0e | 490 | len += count; |
447e176b | 491 | } |
62e76326 | 492 | |
55622953 CT |
493 | /// update "hierarchy" annotations with a new (possibly failed) destination |
494 | /// \param origin the name of the origin server we were trying to reach | |
495 | void | |
496 | TunnelStateData::syncHierNote(const Comm::ConnectionPointer &conn, const char *origin) | |
497 | { | |
498 | request->hier.resetPeerNotes(conn, origin); | |
12f5a662 | 499 | al->hier.resetPeerNotes(conn, origin); |
55622953 CT |
500 | } |
501 | ||
a46d2c0e | 502 | int |
fa34dd97 | 503 | TunnelStateData::Connection::debugLevelForError(int const xerrno) const |
a46d2c0e | 504 | { |
505 | #ifdef ECONNRESET | |
506 | ||
507 | if (xerrno == ECONNRESET) | |
508 | return 2; | |
509 | ||
447e176b | 510 | #endif |
511 | ||
a46d2c0e | 512 | if (ignoreErrno(xerrno)) |
513 | return 3; | |
514 | ||
515 | return 1; | |
516 | } | |
adb78bd4 | 517 | |
983061ed | 518 | /* Read from server side and queue it for writing to the client */ |
a46d2c0e | 519 | void |
c8407295 | 520 | TunnelStateData::ReadServer(const Comm::ConnectionPointer &c, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data) |
983061ed | 521 | { |
fa34dd97 | 522 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
e0d28505 | 523 | assert(cbdataReferenceValid(tunnelState)); |
bf95c10a | 524 | debugs(26, 3, c); |
c4b7a5a9 | 525 | |
11007d4b | 526 | tunnelState->readServer(buf, len, errcode, xerrno); |
a46d2c0e | 527 | } |
62e76326 | 528 | |
a46d2c0e | 529 | void |
ced8def3 | 530 | TunnelStateData::readServer(char *, size_t len, Comm::Flag errcode, int xerrno) |
a46d2c0e | 531 | { |
bf95c10a | 532 | debugs(26, 3, server.conn << ", read " << len << " bytes, err=" << errcode); |
9fe3d316 | 533 | server.delayedLoops=0; |
d01053a2 | 534 | |
a46d2c0e | 535 | /* |
c8407295 | 536 | * Bail out early on Comm::ERR_CLOSING |
26ac0430 | 537 | * - close handlers will tidy up for us |
a46d2c0e | 538 | */ |
a55f4cea | 539 | |
c8407295 | 540 | if (errcode == Comm::ERR_CLOSING) |
a46d2c0e | 541 | return; |
62e76326 | 542 | |
ee1679df | 543 | if (len > 0) { |
a46d2c0e | 544 | server.bytesIn(len); |
a0864754 AJ |
545 | statCounter.server.all.kbytes_in += len; |
546 | statCounter.server.other.kbytes_in += len; | |
d8165775 | 547 | request->hier.notePeerRead(); |
ee1679df | 548 | } |
62e76326 | 549 | |
3ed5793b AR |
550 | if (keepGoingAfterRead(len, errcode, xerrno, server, client)) |
551 | copy(len, server, client, WriteClientDone); | |
552 | } | |
553 | ||
a46d2c0e | 554 | void |
fa34dd97 | 555 | TunnelStateData::Connection::error(int const xerrno) |
a46d2c0e | 556 | { |
bf95c10a | 557 | debugs(50, debugLevelForError(xerrno), conn << ": read/write failure: " << xstrerr(xerrno)); |
62e76326 | 558 | |
a46d2c0e | 559 | if (!ignoreErrno(xerrno)) |
fb046c1b | 560 | conn->close(); |
983061ed | 561 | } |
562 | ||
563 | /* Read from client side and queue it for writing to the server */ | |
a46d2c0e | 564 | void |
c8407295 | 565 | TunnelStateData::ReadClient(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data) |
983061ed | 566 | { |
fa34dd97 | 567 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 568 | assert (cbdataReferenceValid (tunnelState)); |
62e76326 | 569 | |
11007d4b | 570 | tunnelState->readClient(buf, len, errcode, xerrno); |
a46d2c0e | 571 | } |
62e76326 | 572 | |
a46d2c0e | 573 | void |
ced8def3 | 574 | TunnelStateData::readClient(char *, size_t len, Comm::Flag errcode, int xerrno) |
a46d2c0e | 575 | { |
bf95c10a | 576 | debugs(26, 3, client.conn << ", read " << len << " bytes, err=" << errcode); |
9fe3d316 | 577 | client.delayedLoops=0; |
d01053a2 | 578 | |
a46d2c0e | 579 | /* |
c8407295 | 580 | * Bail out early on Comm::ERR_CLOSING |
26ac0430 | 581 | * - close handlers will tidy up for us |
a46d2c0e | 582 | */ |
a55f4cea | 583 | |
c8407295 | 584 | if (errcode == Comm::ERR_CLOSING) |
a46d2c0e | 585 | return; |
a55f4cea | 586 | |
a46d2c0e | 587 | if (len > 0) { |
588 | client.bytesIn(len); | |
a0864754 | 589 | statCounter.client_http.kbytes_in += len; |
a46d2c0e | 590 | } |
62e76326 | 591 | |
3ed5793b AR |
592 | if (keepGoingAfterRead(len, errcode, xerrno, client, server)) |
593 | copy(len, client, server, WriteServerDone); | |
a46d2c0e | 594 | } |
62e76326 | 595 | |
3ed5793b AR |
596 | /// Updates state after reading from client or server. |
597 | /// Returns whether the caller should use the data just read. | |
598 | bool | |
c8407295 | 599 | TunnelStateData::keepGoingAfterRead(size_t len, Comm::Flag errcode, int xerrno, Connection &from, Connection &to) |
a46d2c0e | 600 | { |
bf95c10a | 601 | debugs(26, 3, "from={" << from.conn << "}, to={" << to.conn << "}"); |
fd54d9b2 | 602 | |
a46d2c0e | 603 | /* I think this is to prevent free-while-in-a-callback behaviour |
26ac0430 | 604 | * - RBC 20030229 |
fb046c1b | 605 | * from.conn->close() / to.conn->close() done here trigger close callbacks which may free TunnelStateData |
a46d2c0e | 606 | */ |
83564ee7 | 607 | const CbcPointer<TunnelStateData> safetyLock(this); |
62e76326 | 608 | |
60dafd5e | 609 | /* Bump the source connection read timeout on any activity */ |
8d77a37c AJ |
610 | if (Comm::IsConnOpen(from.conn)) { |
611 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", | |
dc49061a | 612 | CommTimeoutCbPtrFun(tunnelTimeout, this)); |
8d77a37c AJ |
613 | commSetConnTimeout(from.conn, Config.Timeout.read, timeoutCall); |
614 | } | |
99c02c10 | 615 | |
2677c3b1 JPM |
616 | /* Bump the dest connection read timeout on any activity */ |
617 | /* see Bug 3659: tunnels can be weird, with very long one-way transfers */ | |
618 | if (Comm::IsConnOpen(to.conn)) { | |
619 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", | |
620 | CommTimeoutCbPtrFun(tunnelTimeout, this)); | |
621 | commSetConnTimeout(to.conn, Config.Timeout.read, timeoutCall); | |
622 | } | |
623 | ||
58c0b17d | 624 | if (errcode) |
a46d2c0e | 625 | from.error (xerrno); |
60dafd5e | 626 | else if (len == 0 || !Comm::IsConnOpen(to.conn)) { |
bf95c10a | 627 | debugs(26, 3, "Nothing to write or client gone. Terminate the tunnel."); |
fb046c1b | 628 | from.conn->close(); |
62e76326 | 629 | |
60dafd5e | 630 | /* Only close the remote end if we've finished queueing data to it */ |
97c81191 | 631 | if (from.len == 0 && Comm::IsConnOpen(to.conn) ) { |
fb046c1b | 632 | to.conn->close(); |
c4b7a5a9 | 633 | } |
ec41b64c | 634 | } else if (cbdataReferenceValid(this)) { |
3ed5793b AR |
635 | return true; |
636 | } | |
637 | ||
638 | return false; | |
639 | } | |
640 | ||
641 | void | |
642 | TunnelStateData::copy(size_t len, Connection &from, Connection &to, IOCB *completion) | |
643 | { | |
bf95c10a | 644 | debugs(26, 3, "Schedule Write"); |
1c8fc082 A |
645 | AsyncCall::Pointer call = commCbCall(5,5, "TunnelBlindCopyWriteHandler", |
646 | CommIoCbPtrFun(completion, this)); | |
74f35ca8 | 647 | to.write(from.buf, len, call, NULL); |
983061ed | 648 | } |
649 | ||
650 | /* Writes data from the client buffer to the server side */ | |
a46d2c0e | 651 | void |
c8407295 | 652 | TunnelStateData::WriteServerDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data) |
983061ed | 653 | { |
fa34dd97 | 654 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 655 | assert (cbdataReferenceValid (tunnelState)); |
74f35ca8 | 656 | tunnelState->server.writer = NULL; |
a46d2c0e | 657 | |
11007d4b | 658 | tunnelState->writeServerDone(buf, len, flag, xerrno); |
a46d2c0e | 659 | } |
a55f4cea | 660 | |
a46d2c0e | 661 | void |
ced8def3 | 662 | TunnelStateData::writeServerDone(char *, size_t len, Comm::Flag flag, int xerrno) |
a46d2c0e | 663 | { |
bf95c10a | 664 | debugs(26, 3, server.conn << ", " << len << " bytes written, flag=" << flag); |
62e76326 | 665 | |
d8165775 AR |
666 | if (flag == Comm::ERR_CLOSING) |
667 | return; | |
668 | ||
669 | request->hier.notePeerWrite(); | |
670 | ||
5dacdf3f | 671 | /* Error? */ |
c8407295 | 672 | if (flag != Comm::OK) { |
d8165775 AR |
673 | debugs(26, 4, "to-server write failed: " << xerrno); |
674 | server.error(xerrno); // may call comm_close | |
5dacdf3f | 675 | return; |
c4b7a5a9 | 676 | } |
62e76326 | 677 | |
5dacdf3f | 678 | /* EOF? */ |
a46d2c0e | 679 | if (len == 0) { |
bf95c10a | 680 | debugs(26, 4, "No read input. Closing server connection."); |
fb046c1b | 681 | server.conn->close(); |
a46d2c0e | 682 | return; |
683 | } | |
62e76326 | 684 | |
5dacdf3f | 685 | /* Valid data */ |
a0864754 AJ |
686 | statCounter.server.all.kbytes_out += len; |
687 | statCounter.server.other.kbytes_out += len; | |
5dacdf3f | 688 | client.dataSent(len); |
689 | ||
a46d2c0e | 690 | /* If the other end has closed, so should we */ |
97c81191 | 691 | if (!Comm::IsConnOpen(client.conn)) { |
bf95c10a | 692 | debugs(26, 4, "Client gone away. Shutting down server connection."); |
fb046c1b | 693 | server.conn->close(); |
a55f4cea | 694 | return; |
c4b7a5a9 | 695 | } |
62e76326 | 696 | |
f53969cc | 697 | const CbcPointer<TunnelStateData> safetyLock(this); /* ??? should be locked by the caller... */ |
a46d2c0e | 698 | |
5dacdf3f | 699 | if (cbdataReferenceValid(this)) |
3248e962 | 700 | copyClientBytes(); |
983061ed | 701 | } |
702 | ||
703 | /* Writes data from the server buffer to the client side */ | |
a46d2c0e | 704 | void |
c8407295 | 705 | TunnelStateData::WriteClientDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data) |
983061ed | 706 | { |
fa34dd97 | 707 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 708 | assert (cbdataReferenceValid (tunnelState)); |
74f35ca8 | 709 | tunnelState->client.writer = NULL; |
a46d2c0e | 710 | |
11007d4b | 711 | tunnelState->writeClientDone(buf, len, flag, xerrno); |
a46d2c0e | 712 | } |
713 | ||
714 | void | |
e0d28505 | 715 | TunnelStateData::Connection::dataSent(size_t amount) |
a46d2c0e | 716 | { |
bf95c10a | 717 | debugs(26, 3, "len=" << len << " - amount=" << amount); |
a46d2c0e | 718 | assert(amount == (size_t)len); |
719 | len =0; | |
720 | /* increment total object size */ | |
721 | ||
722 | if (size_ptr) | |
723 | *size_ptr += amount; | |
d8165775 | 724 | |
a46d2c0e | 725 | } |
726 | ||
74f35ca8 AR |
727 | void |
728 | TunnelStateData::Connection::write(const char *b, int size, AsyncCall::Pointer &callback, FREE * free_func) | |
729 | { | |
730 | writer = callback; | |
25d2603f | 731 | dirty = true; |
74f35ca8 AR |
732 | Comm::Write(conn, b, size, callback, free_func); |
733 | } | |
734 | ||
25d2603f EB |
735 | template <typename Method> |
736 | void | |
737 | TunnelStateData::Connection::initConnection(const Comm::ConnectionPointer &aConn, Method method, const char *name, TunnelStateData *tunnelState) | |
738 | { | |
739 | Must(!Comm::IsConnOpen(conn)); | |
740 | Must(!closer); | |
741 | Must(Comm::IsConnOpen(aConn)); | |
742 | conn = aConn; | |
743 | closer = commCbCall(5, 4, name, CommCloseCbPtrFun(method, tunnelState)); | |
744 | comm_add_close_handler(conn->fd, closer); | |
745 | } | |
746 | ||
747 | void | |
748 | TunnelStateData::Connection::noteClosure() | |
749 | { | |
750 | debugs(26, 3, conn); | |
751 | conn = nullptr; | |
752 | closer = nullptr; | |
753 | writer = nullptr; // may already be nil | |
754 | } | |
755 | ||
a46d2c0e | 756 | void |
ced8def3 | 757 | TunnelStateData::writeClientDone(char *, size_t len, Comm::Flag flag, int xerrno) |
a46d2c0e | 758 | { |
bf95c10a | 759 | debugs(26, 3, client.conn << ", " << len << " bytes written, flag=" << flag); |
62e76326 | 760 | |
d8165775 AR |
761 | if (flag == Comm::ERR_CLOSING) |
762 | return; | |
763 | ||
5dacdf3f | 764 | /* Error? */ |
c8407295 | 765 | if (flag != Comm::OK) { |
d8165775 AR |
766 | debugs(26, 4, "from-client read failed: " << xerrno); |
767 | client.error(xerrno); // may call comm_close | |
5dacdf3f | 768 | return; |
c4b7a5a9 | 769 | } |
62e76326 | 770 | |
5dacdf3f | 771 | /* EOF? */ |
a46d2c0e | 772 | if (len == 0) { |
bf95c10a | 773 | debugs(26, 4, "Closing client connection due to 0 byte read."); |
fb046c1b | 774 | client.conn->close(); |
62e76326 | 775 | return; |
983061ed | 776 | } |
62e76326 | 777 | |
5dacdf3f | 778 | /* Valid data */ |
a0864754 | 779 | statCounter.client_http.kbytes_out += len; |
5dacdf3f | 780 | server.dataSent(len); |
781 | ||
a46d2c0e | 782 | /* If the other end has closed, so should we */ |
97c81191 | 783 | if (!Comm::IsConnOpen(server.conn)) { |
bf95c10a | 784 | debugs(26, 4, "Server has gone away. Terminating client connection."); |
fb046c1b | 785 | client.conn->close(); |
a55f4cea | 786 | return; |
983061ed | 787 | } |
62e76326 | 788 | |
f53969cc | 789 | CbcPointer<TunnelStateData> safetyLock(this); /* ??? should be locked by the caller... */ |
a55f4cea | 790 | |
5dacdf3f | 791 | if (cbdataReferenceValid(this)) |
6821c276 | 792 | copyServerBytes(); |
983061ed | 793 | } |
794 | ||
b8d8561b | 795 | static void |
8d77a37c | 796 | tunnelTimeout(const CommTimeoutCbParams &io) |
983061ed | 797 | { |
8d77a37c | 798 | TunnelStateData *tunnelState = static_cast<TunnelStateData *>(io.data); |
bf95c10a | 799 | debugs(26, 3, io.conn); |
11007d4b | 800 | /* Temporary lock to protect our own feets (comm_close -> tunnelClientClosed -> Free) */ |
83564ee7 | 801 | CbcPointer<TunnelStateData> safetyLock(tunnelState); |
a55f4cea | 802 | |
25d2603f | 803 | tunnelState->closeConnections(); |
a46d2c0e | 804 | } |
62e76326 | 805 | |
25b0ce45 CT |
806 | void |
807 | TunnelStateData::closePendingConnection(const Comm::ConnectionPointer &conn, const char *reason) | |
808 | { | |
809 | debugs(26, 3, "because " << reason << "; " << conn); | |
810 | assert(!server.conn); | |
811 | if (IsConnOpen(conn)) | |
812 | conn->close(); | |
813 | } | |
814 | ||
a46d2c0e | 815 | void |
25d2603f | 816 | TunnelStateData::closeConnections() |
a46d2c0e | 817 | { |
25d2603f EB |
818 | if (Comm::IsConnOpen(server.conn)) |
819 | server.conn->close(); | |
820 | if (Comm::IsConnOpen(client.conn)) | |
821 | client.conn->close(); | |
a46d2c0e | 822 | } |
823 | ||
9fe3d316 AJ |
824 | static void |
825 | tunnelDelayedClientRead(void *data) | |
826 | { | |
827 | if (!data) | |
828 | return; | |
4846303f | 829 | |
215c41fa | 830 | TunnelStateData *tunnel = static_cast<TunnelStateData*>(data); |
5106d639 EB |
831 | const auto savedContext = CodeContext::Current(); |
832 | CodeContext::Reset(tunnel->codeContext); | |
215c41fa | 833 | tunnel->client.readPending = NULL; |
9fe3d316 | 834 | static uint64_t counter=0; |
215c41fa | 835 | debugs(26, 7, "Client read(2) delayed " << ++counter << " times"); |
9fe3d316 | 836 | tunnel->copyRead(tunnel->client, TunnelStateData::ReadClient); |
5106d639 | 837 | CodeContext::Reset(savedContext); |
9fe3d316 AJ |
838 | } |
839 | ||
840 | static void | |
841 | tunnelDelayedServerRead(void *data) | |
842 | { | |
843 | if (!data) | |
844 | return; | |
4846303f | 845 | |
215c41fa | 846 | TunnelStateData *tunnel = static_cast<TunnelStateData*>(data); |
5106d639 EB |
847 | const auto savedContext = CodeContext::Current(); |
848 | CodeContext::Reset(tunnel->codeContext); | |
215c41fa | 849 | tunnel->server.readPending = NULL; |
9fe3d316 | 850 | static uint64_t counter=0; |
215c41fa | 851 | debugs(26, 7, "Server read(2) delayed " << ++counter << " times"); |
9fe3d316 | 852 | tunnel->copyRead(tunnel->server, TunnelStateData::ReadServer); |
5106d639 | 853 | CodeContext::Reset(savedContext); |
9fe3d316 AJ |
854 | } |
855 | ||
a46d2c0e | 856 | void |
fa34dd97 | 857 | TunnelStateData::copyRead(Connection &from, IOCB *completion) |
a46d2c0e | 858 | { |
859 | assert(from.len == 0); | |
9fe3d316 AJ |
860 | // If only the minimum permitted read size is going to be attempted |
861 | // then we schedule an event to try again in a few I/O cycles. | |
862 | // Allow at least 1 byte to be read every (0.3*10) seconds. | |
863 | int bw = from.bytesWanted(1, SQUID_TCP_SO_RCVBUF); | |
5106d639 EB |
864 | // XXX: Delay pools must not delay client-to-Squid traffic (i.e. when |
865 | // from.readPendingFunc is tunnelDelayedClientRead()). | |
a928fdfd | 866 | // XXX: Bug #4913: For delay pools, use delayRead() API instead. |
9fe3d316 | 867 | if (bw == 1 && ++from.delayedLoops < 10) { |
215c41fa AJ |
868 | from.readPending = this; |
869 | eventAdd("tunnelDelayedServerRead", from.readPendingFunc, from.readPending, 0.3, true); | |
9fe3d316 AJ |
870 | return; |
871 | } | |
872 | ||
fd54d9b2 | 873 | AsyncCall::Pointer call = commCbCall(5,4, "TunnelBlindCopyReadHandler", |
abd8f140 | 874 | CommIoCbPtrFun(completion, this)); |
9fe3d316 | 875 | comm_read(from.conn, from.buf, bw, call); |
983061ed | 876 | } |
877 | ||
3248e962 CT |
878 | void |
879 | TunnelStateData::copyClientBytes() | |
880 | { | |
881 | if (preReadClientData.length()) { | |
882 | size_t copyBytes = preReadClientData.length() > SQUID_TCP_SO_RCVBUF ? SQUID_TCP_SO_RCVBUF : preReadClientData.length(); | |
883 | memcpy(client.buf, preReadClientData.rawContent(), copyBytes); | |
884 | preReadClientData.consume(copyBytes); | |
885 | client.bytesIn(copyBytes); | |
886 | if (keepGoingAfterRead(copyBytes, Comm::OK, 0, client, server)) | |
887 | copy(copyBytes, client, server, TunnelStateData::WriteServerDone); | |
888 | } else | |
889 | copyRead(client, ReadClient); | |
890 | } | |
891 | ||
6821c276 CT |
892 | void |
893 | TunnelStateData::copyServerBytes() | |
894 | { | |
895 | if (preReadServerData.length()) { | |
896 | size_t copyBytes = preReadServerData.length() > SQUID_TCP_SO_RCVBUF ? SQUID_TCP_SO_RCVBUF : preReadServerData.length(); | |
897 | memcpy(server.buf, preReadServerData.rawContent(), copyBytes); | |
898 | preReadServerData.consume(copyBytes); | |
899 | server.bytesIn(copyBytes); | |
900 | if (keepGoingAfterRead(copyBytes, Comm::OK, 0, server, client)) | |
901 | copy(copyBytes, server, client, TunnelStateData::WriteClientDone); | |
902 | } else | |
903 | copyRead(server, ReadServer); | |
904 | } | |
905 | ||
379e8c1c | 906 | /** |
87f237a9 | 907 | * Set the HTTP status for this request and sets the read handlers for client |
379e8c1c AR |
908 | * and server side connections. |
909 | */ | |
910 | static void | |
911 | tunnelStartShoveling(TunnelStateData *tunnelState) | |
912 | { | |
2b6b1bcb AR |
913 | assert(!tunnelState->transportWait); |
914 | assert(!tunnelState->encryptionWait); | |
915 | assert(!tunnelState->peerWait); | |
916 | ||
25b0ce45 CT |
917 | assert(tunnelState->server.conn); |
918 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", | |
919 | CommTimeoutCbPtrFun(tunnelTimeout, tunnelState)); | |
920 | commSetConnTimeout(tunnelState->server.conn, Config.Timeout.read, timeoutCall); | |
921 | ||
955394ce | 922 | *tunnelState->status_ptr = Http::scOkay; |
12f5a662 | 923 | tunnelState->al->cache.code.update(LOG_TCP_TUNNEL); |
379e8c1c | 924 | if (cbdataReferenceValid(tunnelState)) { |
6c420975 | 925 | |
5eedd56a | 926 | // Shovel any payload already pushed into reply buffer by the server response |
3ed5793b | 927 | if (!tunnelState->server.len) |
6821c276 | 928 | tunnelState->copyServerBytes(); |
6c420975 AJ |
929 | else { |
930 | debugs(26, DBG_DATA, "Tunnel server PUSH Payload: \n" << Raw("", tunnelState->server.buf, tunnelState->server.len) << "\n----------"); | |
3ed5793b | 931 | tunnelState->copy(tunnelState->server.len, tunnelState->server, tunnelState->client, TunnelStateData::WriteClientDone); |
6c420975 AJ |
932 | } |
933 | ||
fcc444e3 AJ |
934 | if (tunnelState->http.valid() && tunnelState->http->getConn() && !tunnelState->http->getConn()->inBuf.isEmpty()) { |
935 | SBuf * const in = &tunnelState->http->getConn()->inBuf; | |
936 | debugs(26, DBG_DATA, "Tunnel client PUSH Payload: \n" << *in << "\n----------"); | |
937 | tunnelState->preReadClientData.append(*in); | |
938 | in->consume(); // ConnStateData buffer accounting after the shuffle. | |
3248e962 CT |
939 | } |
940 | tunnelState->copyClientBytes(); | |
379e8c1c AR |
941 | } |
942 | } | |
943 | ||
b0388924 AJ |
944 | /** |
945 | * All the pieces we need to write to client and/or server connection | |
1b76e6c1 | 946 | * have been written. |
379e8c1c | 947 | * Call the tunnelStartShoveling to start the blind pump. |
b0388924 | 948 | */ |
c4b7a5a9 | 949 | static void |
c6f9a0ff | 950 | tunnelConnectedWriteDone(const Comm::ConnectionPointer &conn, char *, size_t len, Comm::Flag flag, int, void *data) |
c4b7a5a9 | 951 | { |
fa34dd97 | 952 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
bf95c10a | 953 | debugs(26, 3, conn << ", flag=" << flag); |
74f35ca8 | 954 | tunnelState->client.writer = NULL; |
62e76326 | 955 | |
c8407295 | 956 | if (flag != Comm::OK) { |
955394ce | 957 | *tunnelState->status_ptr = Http::scInternalServerError; |
e0d28505 | 958 | tunnelErrorComplete(conn->fd, data, 0); |
62e76326 | 959 | return; |
960 | } | |
961 | ||
c6f9a0ff CT |
962 | if (auto http = tunnelState->http.get()) { |
963 | http->out.headers_sz += len; | |
964 | http->out.size += len; | |
965 | } | |
966 | ||
379e8c1c | 967 | tunnelStartShoveling(tunnelState); |
c4b7a5a9 | 968 | } |
969 | ||
f5e17947 CT |
970 | void |
971 | TunnelStateData::tunnelEstablishmentDone(Http::TunnelerAnswer &answer) | |
3ed5793b | 972 | { |
2b6b1bcb | 973 | peerWait.finish(); |
f5e17947 | 974 | server.len = 0; |
3ed5793b | 975 | |
12f5a662 | 976 | al->cache.code.update(LOG_TCP_TUNNEL); |
d8165775 | 977 | |
f5e17947 CT |
978 | if (answer.peerResponseStatus != Http::scNone) |
979 | *status_ptr = answer.peerResponseStatus; | |
980 | ||
25b0ce45 CT |
981 | auto sawProblem = false; |
982 | ||
983 | if (!answer.positive()) { | |
984 | sawProblem = true; | |
2b6b1bcb | 985 | assert(!answer.conn); |
25b0ce45 CT |
986 | } else if (!Comm::IsConnOpen(answer.conn) || fd_table[answer.conn->fd].closing()) { |
987 | sawProblem = true; | |
988 | closePendingConnection(answer.conn, "conn was closed while waiting for tunnelEstablishmentDone"); | |
989 | } | |
990 | ||
991 | if (!sawProblem) { | |
992 | assert(answer.positive()); // paranoid | |
f5e17947 CT |
993 | // copy any post-200 OK bytes to our buffer |
994 | preReadServerData = answer.leftovers; | |
25b0ce45 | 995 | notePeerReadyToShovel(answer.conn); |
3ed5793b AR |
996 | return; |
997 | } | |
998 | ||
25b0ce45 CT |
999 | ErrorState *error = nullptr; |
1000 | if (answer.positive()) { | |
1001 | error = new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request.getRaw(), al); | |
1002 | } else { | |
1003 | error = answer.squidError.get(); | |
1004 | Must(error); | |
1005 | answer.squidError.clear(); // preserve error for errorSendComplete() | |
1006 | } | |
1007 | assert(error); | |
1008 | saveError(error); | |
25d2603f | 1009 | retryOrBail("tunneler error"); |
3ed5793b AR |
1010 | } |
1011 | ||
f5e17947 | 1012 | void |
25b0ce45 | 1013 | TunnelStateData::notePeerReadyToShovel(const Comm::ConnectionPointer &conn) |
983061ed | 1014 | { |
25d2603f | 1015 | assert(!client.dirty); |
e2bbd3b3 | 1016 | commitToServer(conn); |
25b0ce45 | 1017 | |
f5e17947 CT |
1018 | if (!clientExpectsConnectResponse()) |
1019 | tunnelStartShoveling(this); // ssl-bumped connection, be quiet | |
379e8c1c | 1020 | else { |
f5e17947 | 1021 | *status_ptr = Http::scOkay; |
379e8c1c | 1022 | AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone", |
f5e17947 | 1023 | CommIoCbPtrFun(tunnelConnectedWriteDone, this)); |
ea55799d EB |
1024 | al->reply = HttpReply::MakeConnectionEstablished(); |
1025 | const auto mb = al->reply->pack(); | |
1026 | client.write(mb->content(), mb->contentSize(), call, mb->freeFunc()); | |
1027 | delete mb; | |
379e8c1c | 1028 | } |
983061ed | 1029 | } |
1030 | ||
e2bbd3b3 AR |
1031 | void |
1032 | TunnelStateData::commitToServer(const Comm::ConnectionPointer &conn) | |
1033 | { | |
1034 | committedToServer = true; | |
1035 | retriable = false; // may already be false | |
1036 | PeerSelectionInitiator::subscribed = false; // may already be false | |
1037 | server.initConnection(conn, tunnelServerClosed, "tunnelServerClosed", this); | |
1038 | } | |
1039 | ||
b8d8561b | 1040 | static void |
fd54d9b2 | 1041 | tunnelErrorComplete(int fd/*const Comm::ConnectionPointer &*/, void *data, size_t) |
30a4f2a8 | 1042 | { |
fa34dd97 | 1043 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
bf95c10a | 1044 | debugs(26, 3, "FD " << fd); |
11007d4b | 1045 | assert(tunnelState != NULL); |
1046 | /* temporary lock to save our own feets (comm_close -> tunnelClientClosed -> Free) */ | |
83564ee7 | 1047 | CbcPointer<TunnelStateData> safetyLock(tunnelState); |
62e76326 | 1048 | |
97c81191 | 1049 | if (Comm::IsConnOpen(tunnelState->client.conn)) |
fb046c1b | 1050 | tunnelState->client.conn->close(); |
62e76326 | 1051 | |
97c81191 | 1052 | if (Comm::IsConnOpen(tunnelState->server.conn)) |
fb046c1b | 1053 | tunnelState->server.conn->close(); |
30a4f2a8 | 1054 | } |
1055 | ||
6043e368 | 1056 | void |
55622953 | 1057 | TunnelStateData::noteConnection(HappyConnOpener::Answer &answer) |
6043e368 | 1058 | { |
2b6b1bcb | 1059 | transportWait.finish(); |
55622953 | 1060 | |
25b0ce45 CT |
1061 | ErrorState *error = nullptr; |
1062 | if ((error = answer.error.get())) { | |
1063 | Must(!Comm::IsConnOpen(answer.conn)); | |
55622953 | 1064 | syncHierNote(answer.conn, request->url.host()); |
25b0ce45 CT |
1065 | answer.error.clear(); |
1066 | } else if (!Comm::IsConnOpen(answer.conn) || fd_table[answer.conn->fd].closing()) { | |
1067 | error = new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request.getRaw(), al); | |
1068 | closePendingConnection(answer.conn, "conn was closed while waiting for noteConnection"); | |
1069 | } | |
1070 | ||
1071 | if (error) { | |
55622953 | 1072 | saveError(error); |
25d2603f | 1073 | retryOrBail("tried all destinations"); |
55622953 CT |
1074 | return; |
1075 | } | |
6043e368 | 1076 | |
55622953 | 1077 | connectDone(answer.conn, request->url.host(), answer.reused); |
6043e368 AR |
1078 | } |
1079 | ||
55622953 CT |
1080 | void |
1081 | TunnelStateData::connectDone(const Comm::ConnectionPointer &conn, const char *origin, const bool reused) | |
983061ed | 1082 | { |
55622953 | 1083 | Must(Comm::IsConnOpen(conn)); |
cfd66529 | 1084 | |
55622953 CT |
1085 | if (reused) |
1086 | ResetMarkingsToServer(request.getRaw(), *conn); | |
1087 | // else Comm::ConnOpener already applied proper/current markings | |
1088 | ||
25b0ce45 | 1089 | syncHierNote(conn, origin); |
cfd66529 | 1090 | |
4beb4bab AJ |
1091 | #if USE_DELAY_POOLS |
1092 | /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */ | |
1093 | if (conn->getPeer() && conn->getPeer()->options.no_delay) | |
55622953 | 1094 | server.setDelayId(DelayId()); |
4beb4bab AJ |
1095 | #endif |
1096 | ||
55622953 | 1097 | netdbPingSite(request->url.host()); |
4beb4bab | 1098 | |
55622953 | 1099 | request->peer_host = conn->getPeer() ? conn->getPeer()->host : nullptr; |
cfd66529 | 1100 | |
f5e17947 CT |
1101 | bool toOrigin = false; // same semantics as StateFlags::toOrigin |
1102 | if (const auto * const peer = conn->getPeer()) { | |
55622953 | 1103 | request->prepForPeering(*peer); |
f5e17947 | 1104 | toOrigin = peer->options.originserver; |
fe40a877 | 1105 | } else { |
55622953 | 1106 | request->prepForDirect(); |
f5e17947 | 1107 | toOrigin = true; |
cfd66529 | 1108 | } |
62e76326 | 1109 | |
f5e17947 | 1110 | if (!toOrigin) |
25b0ce45 | 1111 | connectToPeer(conn); |
cfd66529 | 1112 | else { |
25b0ce45 | 1113 | notePeerReadyToShovel(conn); |
983061ed | 1114 | } |
983061ed | 1115 | } |
30a4f2a8 | 1116 | |
770f051d | 1117 | void |
ac9f46af | 1118 | tunnelStart(ClientHttpRequest * http) |
30a4f2a8 | 1119 | { |
bf95c10a | 1120 | debugs(26, 3, MYNAME); |
30a4f2a8 | 1121 | /* Create state structure. */ |
fa34dd97 | 1122 | TunnelStateData *tunnelState = NULL; |
9b312a19 | 1123 | ErrorState *err = NULL; |
190154cf | 1124 | HttpRequest *request = http->request; |
d5964d58 | 1125 | char *url = http->uri; |
fb046c1b | 1126 | |
f1003989 | 1127 | /* |
4dd643d5 | 1128 | * client_addr.isNoAddr() indicates this is an "internal" request |
a4b8110e | 1129 | * from peer_digest.c, asn.c, netdb.c, etc and should always |
1130 | * be allowed. yuck, I know. | |
1131 | */ | |
62e76326 | 1132 | |
4dd643d5 | 1133 | if (Config.accessList.miss && !request->client_addr.isNoAddr()) { |
62e76326 | 1134 | /* |
1135 | * Check if this host is allowed to fetch MISSES from us (miss_access) | |
b50e327b | 1136 | * default is to allow. |
62e76326 | 1137 | */ |
c0941a6a | 1138 | ACLFilledChecklist ch(Config.accessList.miss, request, NULL); |
cb365059 | 1139 | ch.al = http->al; |
62e76326 | 1140 | ch.src_addr = request->client_addr; |
1141 | ch.my_addr = request->my_addr; | |
cb365059 | 1142 | ch.syncAle(request, http->log_uri); |
06bf5384 | 1143 | if (ch.fastCheck().denied()) { |
bf95c10a | 1144 | debugs(26, 4, "MISS access forbidden."); |
7e6eabbc | 1145 | err = new ErrorState(ERR_FORWARDING_DENIED, Http::scForbidden, request, http->al); |
ac9f46af | 1146 | http->al->http.code = Http::scForbidden; |
73c36fd9 | 1147 | errorSend(http->getConn()->clientConnection, err); |
62e76326 | 1148 | return; |
1149 | } | |
f1003989 | 1150 | } |
62e76326 | 1151 | |
7f06a3d8 | 1152 | debugs(26, 3, request->method << ' ' << url << ' ' << request->http_ver); |
5db6bf73 FC |
1153 | ++statCounter.server.all.requests; |
1154 | ++statCounter.server.other.requests; | |
62e76326 | 1155 | |
6b2b6cfe | 1156 | tunnelState = new TunnelStateData(http); |
9a0a18de | 1157 | #if USE_DELAY_POOLS |
f5e17947 | 1158 | tunnelState->server.setDelayId(DelayId::DelayClient(http)); |
59715b38 | 1159 | #endif |
6043e368 | 1160 | tunnelState->startSelectingDestinations(request, http->al, nullptr); |
30a4f2a8 | 1161 | } |
98ffb7e4 | 1162 | |
a23223bf | 1163 | void |
25b0ce45 | 1164 | TunnelStateData::connectToPeer(const Comm::ConnectionPointer &conn) |
e2849af8 | 1165 | { |
25b0ce45 CT |
1166 | if (const auto p = conn->getPeer()) { |
1167 | if (p->secure.encryptTransport) | |
1168 | return advanceDestination("secure connection to peer", conn, [this,&conn] { | |
1c2b4465 CT |
1169 | secureConnectionToPeer(conn); |
1170 | }); | |
a23223bf | 1171 | } |
a23223bf | 1172 | |
25b0ce45 | 1173 | connectedToPeer(conn); |
a23223bf CT |
1174 | } |
1175 | ||
25b0ce45 | 1176 | /// encrypts an established TCP connection to peer |
a23223bf | 1177 | void |
25b0ce45 CT |
1178 | TunnelStateData::secureConnectionToPeer(const Comm::ConnectionPointer &conn) |
1179 | { | |
1180 | AsyncCall::Pointer callback = asyncCall(5,4, "TunnelStateData::noteSecurityPeerConnectorAnswer", | |
1181 | MyAnswerDialer(&TunnelStateData::noteSecurityPeerConnectorAnswer, this)); | |
1182 | const auto connector = new Security::BlindPeerConnector(request, conn, callback, al); | |
2b6b1bcb | 1183 | encryptionWait.start(connector, callback); |
25b0ce45 CT |
1184 | } |
1185 | ||
1186 | /// starts a preparation step for an established connection; retries on failures | |
1187 | template <typename StepStart> | |
1188 | void | |
1189 | TunnelStateData::advanceDestination(const char *stepDescription, const Comm::ConnectionPointer &conn, const StepStart &startStep) | |
1190 | { | |
1191 | // TODO: Extract destination-specific handling from TunnelStateData so that | |
1192 | // all the awkward, limited-scope advanceDestination() calls can be replaced | |
1193 | // with a single simple try/catch,retry block. | |
1194 | try { | |
1195 | startStep(); | |
1196 | // now wait for the step callback | |
1197 | } catch (...) { | |
1198 | debugs (26, 2, "exception while trying to " << stepDescription << ": " << CurrentException); | |
1199 | closePendingConnection(conn, "connection preparation exception"); | |
1200 | if (!savedError) | |
1201 | saveError(new ErrorState(ERR_CANNOT_FORWARD, Http::scInternalServerError, request.getRaw(), al)); | |
25d2603f | 1202 | retryOrBail(stepDescription); |
25b0ce45 CT |
1203 | } |
1204 | } | |
1205 | ||
1206 | /// callback handler for the connection encryptor | |
1207 | void | |
1208 | TunnelStateData::noteSecurityPeerConnectorAnswer(Security::EncryptorAnswer &answer) | |
a23223bf | 1209 | { |
2b6b1bcb AR |
1210 | encryptionWait.finish(); |
1211 | ||
25d2603f | 1212 | ErrorState *error = nullptr; |
2b6b1bcb | 1213 | assert(!answer.tunneled); |
25d2603f | 1214 | if ((error = answer.error.get())) { |
2b6b1bcb | 1215 | assert(!answer.conn); |
25d2603f EB |
1216 | answer.error.clear(); |
1217 | } else if (!Comm::IsConnOpen(answer.conn) || fd_table[answer.conn->fd].closing()) { | |
1218 | error = new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request.getRaw(), al); | |
1219 | closePendingConnection(answer.conn, "conn was closed while waiting for noteSecurityPeerConnectorAnswer"); | |
a23223bf CT |
1220 | } |
1221 | ||
25d2603f EB |
1222 | if (error) { |
1223 | saveError(error); | |
1224 | retryOrBail("TLS peer connection error"); | |
25b0ce45 CT |
1225 | return; |
1226 | } | |
1227 | ||
1228 | connectedToPeer(answer.conn); | |
1229 | } | |
1230 | ||
1231 | void | |
1232 | TunnelStateData::connectedToPeer(const Comm::ConnectionPointer &conn) | |
1233 | { | |
1234 | advanceDestination("establish tunnel through proxy", conn, [this,&conn] { | |
1235 | establishTunnelThruProxy(conn); | |
1236 | }); | |
1237 | } | |
1238 | ||
1239 | void | |
1240 | TunnelStateData::establishTunnelThruProxy(const Comm::ConnectionPointer &conn) | |
1241 | { | |
f5e17947 CT |
1242 | AsyncCall::Pointer callback = asyncCall(5,4, |
1243 | "TunnelStateData::tunnelEstablishmentDone", | |
1244 | Http::Tunneler::CbDialer<TunnelStateData>(&TunnelStateData::tunnelEstablishmentDone, this)); | |
25b0ce45 | 1245 | const auto tunneler = new Http::Tunneler(conn, request, callback, Config.Timeout.lifetime, al); |
f5e17947 CT |
1246 | #if USE_DELAY_POOLS |
1247 | tunneler->setDelayId(server.delayId); | |
1248 | #endif | |
2b6b1bcb | 1249 | peerWait.start(tunneler, callback); |
98ffb7e4 | 1250 | } |
33ea9fff | 1251 | |
6043e368 AR |
1252 | void |
1253 | TunnelStateData::noteDestination(Comm::ConnectionPointer path) | |
33ea9fff | 1254 | { |
55622953 | 1255 | destinationsFound = true; |
3dde9e52 CT |
1256 | |
1257 | if (!path) { // decided to use a pinned connection | |
1258 | // We can call usePinned() without fear of clashing with an earlier | |
1259 | // forwarding attempt because PINNED must be the first destination. | |
55622953 | 1260 | assert(destinations->empty()); |
3dde9e52 CT |
1261 | usePinned(); |
1262 | return; | |
1263 | } | |
1264 | ||
55622953 CT |
1265 | destinations->addPath(path); |
1266 | ||
2b6b1bcb | 1267 | if (transportWait) { |
e2bbd3b3 | 1268 | assert(!transporting()); |
55622953 CT |
1269 | notifyConnOpener(); |
1270 | return; // and continue to wait for tunnelConnectDone() callback | |
1271 | } | |
1272 | ||
e2bbd3b3 AR |
1273 | if (transporting()) |
1274 | return; // and continue to receive destinations for backup | |
1275 | ||
55622953 | 1276 | startConnecting(); |
6043e368 | 1277 | } |
62e76326 | 1278 | |
6043e368 AR |
1279 | void |
1280 | TunnelStateData::noteDestinationsEnd(ErrorState *selectionError) | |
1281 | { | |
1282 | PeerSelectionInitiator::subscribed = false; | |
55622953 CT |
1283 | destinations->destinationsFinalized = true; |
1284 | if (!destinationsFound) { | |
6b2b6cfe | 1285 | |
25d2603f EB |
1286 | // XXX: Honor clientExpectsConnectResponse() before replying. |
1287 | ||
6043e368 AR |
1288 | if (selectionError) |
1289 | return sendError(selectionError, "path selection has failed"); | |
6b2b6cfe | 1290 | |
e2bbd3b3 | 1291 | // TODO: Merge with FwdState and remove this likely unnecessary check. |
6043e368 | 1292 | if (savedError) |
e2bbd3b3 | 1293 | return sendError(savedError, "path selection found no paths (with an impossible early error)"); |
6043e368 | 1294 | |
0d55f01c | 1295 | return sendError(new ErrorState(ERR_CANNOT_FORWARD, Http::scInternalServerError, request.getRaw(), al), |
6043e368 | 1296 | "path selection found no paths"); |
db1cd23c | 1297 | } |
6043e368 AR |
1298 | // else continue to use one of the previously noted destinations; |
1299 | // if all of them fail, tunneling as whole will fail | |
1300 | Must(!selectionError); // finding at least one path means selection succeeded | |
55622953 | 1301 | |
e2bbd3b3 AR |
1302 | if (transportWait) { |
1303 | assert(!transporting()); | |
1304 | notifyConnOpener(); | |
1305 | return; // and continue to wait for the noteConnection() callback | |
1306 | } | |
1307 | ||
1308 | if (transporting()) { | |
1309 | // We are already using a previously opened connection (but were also | |
1310 | // receiving more destinations in case we need to re-forward). | |
1311 | debugs(17, 7, "keep transporting"); | |
55622953 CT |
1312 | return; |
1313 | } | |
1314 | ||
e2bbd3b3 AR |
1315 | // destinationsFound, but none of them worked, and we were waiting for more |
1316 | assert(savedError); | |
1317 | // XXX: Honor clientExpectsConnectResponse() before replying. | |
1318 | sendError(savedError, "all found paths have failed"); | |
6043e368 | 1319 | } |
a37fdd8a | 1320 | |
e2bbd3b3 AR |
1321 | /// Whether a tunneling attempt to some selected destination X is in progress |
1322 | /// (after successfully opening/reusing a transport connection to X). | |
1323 | /// \sa transportWait | |
2b6b1bcb | 1324 | bool |
e2bbd3b3 | 1325 | TunnelStateData::transporting() const |
2b6b1bcb | 1326 | { |
e2bbd3b3 | 1327 | return encryptionWait || peerWait || committedToServer; |
2b6b1bcb AR |
1328 | } |
1329 | ||
6043e368 AR |
1330 | /// remembers an error to be used if there will be no more connection attempts |
1331 | void | |
1332 | TunnelStateData::saveError(ErrorState *error) | |
1333 | { | |
1334 | debugs(26, 4, savedError << " ? " << error); | |
1335 | assert(error); | |
1336 | delete savedError; // may be nil | |
1337 | savedError = error; | |
1338 | } | |
1339 | ||
1340 | /// Starts sending the given error message to the client, leading to the | |
1341 | /// eventual transaction termination. Call with savedError to send savedError. | |
1342 | void | |
1343 | TunnelStateData::sendError(ErrorState *finalError, const char *reason) | |
1344 | { | |
1345 | debugs(26, 3, "aborting transaction for " << reason); | |
1346 | ||
1347 | if (request) | |
1348 | request->hier.stopPeerClock(false); | |
1349 | ||
2b6b1bcb | 1350 | cancelStep(reason); |
55622953 | 1351 | |
6043e368 | 1352 | assert(finalError); |
16b70e2a | 1353 | |
6043e368 AR |
1354 | // get rid of any cached error unless that is what the caller is sending |
1355 | if (savedError != finalError) | |
1356 | delete savedError; // may be nil | |
1357 | savedError = nullptr; | |
62e76326 | 1358 | |
6043e368 AR |
1359 | // we cannot try other destinations after responding with an error |
1360 | PeerSelectionInitiator::subscribed = false; // may already be false | |
1361 | ||
1362 | *status_ptr = finalError->httpStatus; | |
1363 | finalError->callback = tunnelErrorComplete; | |
1364 | finalError->callback_data = this; | |
1365 | errorSend(client.conn, finalError); | |
0ce8e93b EB |
1366 | } |
1367 | ||
2b6b1bcb AR |
1368 | /// Notify a pending subtask, if any, that we no longer need its help. We do not |
1369 | /// have to do this -- the subtask job will eventually end -- but ending it | |
1370 | /// earlier reduces waste and may reduce DoS attack surface. | |
55622953 | 1371 | void |
2b6b1bcb | 1372 | TunnelStateData::cancelStep(const char *reason) |
55622953 | 1373 | { |
2b6b1bcb AR |
1374 | transportWait.cancel(reason); |
1375 | encryptionWait.cancel(reason); | |
1376 | peerWait.cancel(reason); | |
55622953 CT |
1377 | } |
1378 | ||
0ce8e93b EB |
1379 | void |
1380 | TunnelStateData::startConnecting() | |
1381 | { | |
6043e368 AR |
1382 | if (request) |
1383 | request->hier.startPeerClock(); | |
1384 | ||
55622953 | 1385 | assert(!destinations->empty()); |
e2bbd3b3 | 1386 | assert(!transporting()); |
2b6b1bcb AR |
1387 | AsyncCall::Pointer callback = asyncCall(17, 5, "TunnelStateData::noteConnection", HappyConnOpener::CbDialer<TunnelStateData>(&TunnelStateData::noteConnection, this)); |
1388 | const auto cs = new HappyConnOpener(destinations, callback, request, startTime, 0, al); | |
55622953 CT |
1389 | cs->setHost(request->url.host()); |
1390 | cs->setRetriable(false); | |
1391 | cs->allowPersistent(false); | |
1392 | destinations->notificationPending = true; // start() is async | |
2b6b1bcb | 1393 | transportWait.start(cs, callback); |
33ea9fff | 1394 | } |
a46d2c0e | 1395 | |
3dde9e52 CT |
1396 | /// send request on an existing connection dedicated to the requesting client |
1397 | void | |
1398 | TunnelStateData::usePinned() | |
1399 | { | |
55622953 CT |
1400 | Must(request); |
1401 | const auto connManager = request->pinnedConnection(); | |
daf80700 CT |
1402 | try { |
1403 | const auto serverConn = ConnStateData::BorrowPinnedConnection(request.getRaw(), al); | |
1404 | debugs(26, 7, "pinned peer connection: " << serverConn); | |
1405 | ||
1406 | // Set HttpRequest pinned related flags for consistency even if | |
1407 | // they are not really used by tunnel.cc code. | |
1408 | request->flags.pinned = true; | |
1409 | if (connManager->pinnedAuth()) | |
1410 | request->flags.auth = true; | |
1411 | ||
1412 | // the server may close the pinned connection before this request | |
1413 | const auto reused = true; | |
1414 | connectDone(serverConn, connManager->pinning.host, reused); | |
1415 | } catch (ErrorState * const error) { | |
1416 | syncHierNote(nullptr, connManager ? connManager->pinning.host : request->url.host()); | |
25d2603f | 1417 | // XXX: Honor clientExpectsConnectResponse() before replying. |
3dde9e52 | 1418 | // a PINNED path failure is fatal; do not wait for more paths |
daf80700 | 1419 | sendError(error, "pinned path failure"); |
3dde9e52 CT |
1420 | return; |
1421 | } | |
1422 | ||
3dde9e52 CT |
1423 | } |
1424 | ||
fa34dd97 | 1425 | CBDATA_CLASS_INIT(TunnelStateData); |
a46d2c0e | 1426 | |
a46d2c0e | 1427 | bool |
fa34dd97 | 1428 | TunnelStateData::noConnections() const |
a46d2c0e | 1429 | { |
97c81191 | 1430 | return !Comm::IsConnOpen(server.conn) && !Comm::IsConnOpen(client.conn); |
a46d2c0e | 1431 | } |
1432 | ||
9a0a18de | 1433 | #if USE_DELAY_POOLS |
a46d2c0e | 1434 | void |
fa34dd97 | 1435 | TunnelStateData::Connection::setDelayId(DelayId const &newDelay) |
a46d2c0e | 1436 | { |
1437 | delayId = newDelay; | |
1438 | } | |
1439 | ||
1440 | #endif | |
93ead3fd | 1441 | |
2b6b1bcb | 1442 | /// makes sure connection opener knows that the destinations have changed |
55622953 CT |
1443 | void |
1444 | TunnelStateData::notifyConnOpener() | |
1445 | { | |
1446 | if (destinations->notificationPending) { | |
1447 | debugs(17, 7, "reusing pending notification"); | |
1448 | } else { | |
1449 | destinations->notificationPending = true; | |
2b6b1bcb | 1450 | CallJobHere(17, 5, transportWait.job(), HappyConnOpener, noteCandidatesChange); |
55622953 CT |
1451 | } |
1452 | } | |
1453 | ||
1c2b4465 CT |
1454 | /** |
1455 | * Sets up a TCP tunnel through Squid and starts shoveling traffic. | |
1456 | * \param request the request that initiated/caused this tunnel | |
1457 | * \param clientConn the already accepted client-to-Squid TCP connection | |
1458 | * \param srvConn the already established Squid-to-server TCP connection | |
1459 | * \param preReadServerData server-sent bytes to be forwarded to the client | |
1460 | */ | |
93ead3fd | 1461 | void |
1c2b4465 | 1462 | switchToTunnel(HttpRequest *request, const Comm::ConnectionPointer &clientConn, const Comm::ConnectionPointer &srvConn, const SBuf &preReadServerData) |
93ead3fd | 1463 | { |
25b0ce45 CT |
1464 | Must(Comm::IsConnOpen(clientConn)); |
1465 | Must(Comm::IsConnOpen(srvConn)); | |
1466 | ||
c91d4d4e | 1467 | debugs(26,5, "Revert to tunnel FD " << clientConn->fd << " with FD " << srvConn->fd); |
93ead3fd | 1468 | |
6b2b6cfe | 1469 | /* Create state structure. */ |
93ead3fd CT |
1470 | ++statCounter.server.all.requests; |
1471 | ++statCounter.server.other.requests; | |
1472 | ||
6b2b6cfe CT |
1473 | auto conn = request->clientConnectionManager.get(); |
1474 | Must(conn); | |
1475 | Http::StreamPointer context = conn->pipeline.front(); | |
1476 | Must(context && context->http); | |
a95989ed | 1477 | |
6b2b6cfe | 1478 | debugs(26, 3, request->method << " " << context->http->uri << " " << request->http_ver); |
a95989ed | 1479 | |
6b2b6cfe | 1480 | TunnelStateData *tunnelState = new TunnelStateData(context->http); |
e2bbd3b3 | 1481 | tunnelState->commitToServer(srvConn); |
93ead3fd | 1482 | |
d8165775 | 1483 | request->hier.resetPeerNotes(srvConn, tunnelState->getHost()); |
93ead3fd | 1484 | |
6b2b6cfe CT |
1485 | #if USE_DELAY_POOLS |
1486 | /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */ | |
f5e17947 | 1487 | if (!srvConn->getPeer() || !srvConn->getPeer()->options.no_delay) |
6b2b6cfe CT |
1488 | tunnelState->server.setDelayId(DelayId::DelayClient(context->http)); |
1489 | #endif | |
1490 | ||
f53e6e84 | 1491 | request->peer_host = srvConn->getPeer() ? srvConn->getPeer()->host : nullptr; |
93ead3fd | 1492 | |
e4f14091 | 1493 | debugs(26, 4, "determine post-connect handling pathway."); |
f5e17947 CT |
1494 | if (const auto peer = srvConn->getPeer()) |
1495 | request->prepForPeering(*peer); | |
1496 | else | |
1497 | request->prepForDirect(); | |
93ead3fd | 1498 | |
1c2b4465 | 1499 | tunnelState->preReadServerData = preReadServerData; |
93ead3fd | 1500 | |
6821c276 | 1501 | tunnelStartShoveling(tunnelState); |
93ead3fd | 1502 | } |
f53969cc | 1503 |