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