]>
Commit | Line | Data |
---|---|---|
983061ed | 1 | /* |
bde978a6 | 2 | * Copyright (C) 1996-2015 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" |
9f518b4a | 18 | #include "comm.h" |
cfd66529 | 19 | #include "comm/Connection.h" |
aed188fd | 20 | #include "comm/ConnOpener.h" |
7e66d5e2 | 21 | #include "comm/Read.h" |
ec41b64c | 22 | #include "comm/Write.h" |
a011edee FC |
23 | #include "errorpage.h" |
24 | #include "fde.h" | |
4f3c27a1 | 25 | #include "FwdState.h" |
40f1e76d | 26 | #include "globals.h" |
e5ee81f0 | 27 | #include "http.h" |
a011edee | 28 | #include "HttpRequest.h" |
46f4b111 | 29 | #include "HttpStateFlags.h" |
6dc6127b | 30 | #include "ip/QosConfig.h" |
8a713747 | 31 | #include "LogTags.h" |
a011edee | 32 | #include "MemBuf.h" |
cfd66529 | 33 | #include "PeerSelectState.h" |
3248e962 | 34 | #include "SBuf.h" |
4d5904f7 | 35 | #include "SquidConfig.h" |
16b70e2a | 36 | #include "SquidTime.h" |
e4f1fdae | 37 | #include "StatCounters.h" |
a23223bf | 38 | #if USE_OPENSSL |
93ead3fd | 39 | #include "ssl/bio.h" |
a23223bf | 40 | #include "ssl/PeerConnector.h" |
5d65362c | 41 | #include "ssl/ServerBump.h" |
fcfdf7f9 AJ |
42 | #else |
43 | #include "security/EncryptorAnswer.h" | |
a23223bf | 44 | #endif |
4e540555 | 45 | #include "tools.h" |
582c2af2 FC |
46 | #if USE_DELAY_POOLS |
47 | #include "DelayId.h" | |
48 | #endif | |
49 | ||
074d6a40 AJ |
50 | #include <climits> |
51 | #include <cerrno> | |
582c2af2 | 52 | |
8ca1d33d AJ |
53 | /** |
54 | * TunnelStateData is the state engine performing the tasks for | |
55 | * setup of a TCP tunnel from an existing open client FD to a server | |
56 | * then shuffling binary data between the resulting FD pair. | |
57 | */ | |
58 | /* | |
59 | * TODO 1: implement a read/write API on ConnStateData to send/receive blocks | |
60 | * of pre-formatted data. Then we can use that as the client side of the tunnel | |
61 | * instead of re-implementing it here and occasionally getting the ConnStateData | |
62 | * read/write state wrong. | |
63 | * | |
64 | * TODO 2: then convert this into a AsyncJob, possibly a child of 'Server' | |
65 | */ | |
fa34dd97 | 66 | class TunnelStateData |
62e76326 | 67 | { |
5c2f68b7 | 68 | CBDATA_CLASS(TunnelStateData); |
a46d2c0e | 69 | |
70 | public: | |
8ca1d33d AJ |
71 | TunnelStateData(); |
72 | ~TunnelStateData(); | |
73 | TunnelStateData(const TunnelStateData &); // do not implement | |
74 | TunnelStateData &operator =(const TunnelStateData &); // do not implement | |
a46d2c0e | 75 | |
76 | class Connection; | |
c8407295 AJ |
77 | static void ReadClient(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data); |
78 | static void ReadServer(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data); | |
79 | static void WriteClientDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data); | |
80 | static void WriteServerDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data); | |
a46d2c0e | 81 | |
3ed5793b AR |
82 | /// Starts reading peer response to our CONNECT request. |
83 | void readConnectResponse(); | |
84 | ||
85 | /// Called when we may be done handling a CONNECT exchange with the peer. | |
86 | void connectExchangeCheckpoint(); | |
87 | ||
a46d2c0e | 88 | bool noConnections() const; |
983061ed | 89 | char *url; |
6c420975 | 90 | CbcPointer<ClientHttpRequest> http; |
8a70cdbb | 91 | HttpRequest::Pointer request; |
06521a10 | 92 | AccessLogEntryPointer al; |
00ae51e4 | 93 | Comm::ConnectionList serverDestinations; |
62e76326 | 94 | |
fb046c1b AJ |
95 | const char * getHost() const { |
96 | return (server.conn != NULL && server.conn->getPeer() ? server.conn->getPeer()->host : request->GetHost()); | |
97 | }; | |
98 | ||
3ed5793b AR |
99 | /// Whether we are writing a CONNECT request to a peer. |
100 | bool waitingForConnectRequest() const { return connectReqWriting; } | |
101 | /// Whether we are reading a CONNECT response from a peer. | |
102 | bool waitingForConnectResponse() const { return connectRespBuf; } | |
103 | /// Whether we are waiting for the CONNECT request/response exchange with the peer. | |
104 | bool waitingForConnectExchange() const { return waitingForConnectRequest() || waitingForConnectResponse(); } | |
105 | ||
106 | /// Whether the client sent a CONNECT request to us. | |
1c8fc082 | 107 | bool clientExpectsConnectResponse() const { |
5d65362c CT |
108 | #if USE_OPENSSL |
109 | // We are bumping and we had already send "OK CONNECTED" | |
110 | if (http.valid() && http->getConn() && http->getConn()->serverBump() && http->getConn()->serverBump()->step > Ssl::bumpStep1) | |
111 | return false; | |
112 | #endif | |
1c8fc082 A |
113 | return !(request != NULL && |
114 | (request->flags.interceptTproxy || request->flags.intercepted)); | |
115 | } | |
3ed5793b | 116 | |
a46d2c0e | 117 | class Connection |
62e76326 | 118 | { |
a46d2c0e | 119 | |
120 | public: | |
215c41fa | 121 | Connection() : len (0), buf ((char *)xmalloc(SQUID_TCP_SO_RCVBUF)), size_ptr(NULL), delayedLoops(0), |
be75380c | 122 | readPending(NULL), readPendingFunc(NULL) {} |
a46d2c0e | 123 | |
8a467c4b AJ |
124 | ~Connection(); |
125 | ||
126 | int bytesWanted(int lower=0, int upper = INT_MAX) const; | |
a46d2c0e | 127 | void bytesIn(int const &); |
9a0a18de | 128 | #if USE_DELAY_POOLS |
a46d2c0e | 129 | |
130 | void setDelayId(DelayId const &); | |
131 | #endif | |
132 | ||
133 | void error(int const xerrno); | |
5c926411 | 134 | int debugLevelForError(int const xerrno) const; |
3ed5793b AR |
135 | /// handles a non-I/O error associated with this Connection |
136 | void logicError(const char *errMsg); | |
a46d2c0e | 137 | void closeIfOpen(); |
8a467c4b | 138 | void dataSent (size_t amount); |
62e76326 | 139 | int len; |
8a467c4b | 140 | char *buf; |
f53969cc | 141 | int64_t *size_ptr; /* pointer to size in an ConnStateData for logging */ |
62e76326 | 142 | |
fb046c1b | 143 | Comm::ConnectionPointer conn; ///< The currently connected connection. |
9fe3d316 | 144 | uint8_t delayedLoops; ///< how many times a read on this connection has been postponed. |
fb046c1b | 145 | |
215c41fa AJ |
146 | // XXX: make these an AsyncCall when event API can handle them |
147 | TunnelStateData *readPending; | |
148 | EVH *readPendingFunc; | |
a46d2c0e | 149 | private: |
9a0a18de | 150 | #if USE_DELAY_POOLS |
62e76326 | 151 | |
a46d2c0e | 152 | DelayId delayId; |
59715b38 | 153 | #endif |
62e76326 | 154 | |
a46d2c0e | 155 | }; |
156 | ||
157 | Connection client, server; | |
8a713747 AJ |
158 | int *status_ptr; ///< pointer for logging HTTP status |
159 | LogTags *logTag_ptr; ///< pointer for logging Squid processing code | |
3ed5793b AR |
160 | MemBuf *connectRespBuf; ///< accumulates peer CONNECT response when we need it |
161 | bool connectReqWriting; ///< whether we are writing a CONNECT request to a peer | |
3248e962 | 162 | SBuf preReadClientData; |
3ed5793b | 163 | |
a46d2c0e | 164 | void copyRead(Connection &from, IOCB *completion); |
165 | ||
a23223bf CT |
166 | /// continue to set up connection to a peer, going async for SSL peers |
167 | void connectToPeer(); | |
168 | ||
a46d2c0e | 169 | private: |
a23223bf CT |
170 | #if USE_OPENSSL |
171 | /// Gives PeerConnector access to Answer in the TunnelStateData callback dialer. | |
172 | class MyAnswerDialer: public CallDialer, public Ssl::PeerConnector::CbDialer | |
173 | { | |
174 | public: | |
fcfdf7f9 | 175 | typedef void (TunnelStateData::*Method)(Security::EncryptorAnswer &); |
a23223bf CT |
176 | |
177 | MyAnswerDialer(Method method, TunnelStateData *tunnel): | |
f53969cc | 178 | method_(method), tunnel_(tunnel), answer_() {} |
a23223bf CT |
179 | |
180 | /* CallDialer API */ | |
181 | virtual bool canDial(AsyncCall &call) { return tunnel_.valid(); } | |
182 | void dial(AsyncCall &call) { ((&(*tunnel_))->*method_)(answer_); } | |
183 | virtual void print(std::ostream &os) const { | |
e2849af8 A |
184 | os << '(' << tunnel_.get() << ", " << answer_ << ')'; |
185 | } | |
a23223bf CT |
186 | |
187 | /* Ssl::PeerConnector::CbDialer API */ | |
fcfdf7f9 | 188 | virtual Security::EncryptorAnswer &answer() { return answer_; } |
a23223bf CT |
189 | |
190 | private: | |
191 | Method method_; | |
192 | CbcPointer<TunnelStateData> tunnel_; | |
fcfdf7f9 | 193 | Security::EncryptorAnswer answer_; |
a23223bf | 194 | }; |
a23223bf CT |
195 | #endif |
196 | ||
fcfdf7f9 AJ |
197 | /// callback handler after connection setup (including any encryption) |
198 | void connectedToPeer(Security::EncryptorAnswer &answer); | |
199 | ||
5c2f68b7 | 200 | public: |
c8407295 | 201 | bool keepGoingAfterRead(size_t len, Comm::Flag errcode, int xerrno, Connection &from, Connection &to); |
3ed5793b AR |
202 | void copy(size_t len, Connection &from, Connection &to, IOCB *); |
203 | void handleConnectResponse(const size_t chunkSize); | |
c8407295 AJ |
204 | void readServer(char *buf, size_t len, Comm::Flag errcode, int xerrno); |
205 | void readClient(char *buf, size_t len, Comm::Flag errcode, int xerrno); | |
206 | void writeClientDone(char *buf, size_t len, Comm::Flag flag, int xerrno); | |
207 | void writeServerDone(char *buf, size_t len, Comm::Flag flag, int xerrno); | |
3ed5793b | 208 | |
c8407295 AJ |
209 | static void ReadConnectResponseDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data); |
210 | void readConnectResponseDone(char *buf, size_t len, Comm::Flag errcode, int xerrno); | |
3248e962 | 211 | void copyClientBytes(); |
a46d2c0e | 212 | }; |
983061ed | 213 | |
3c4fcf0f | 214 | static const char *const conn_established = "HTTP/1.1 200 Connection established\r\n\r\n"; |
983061ed | 215 | |
11007d4b | 216 | static CNCB tunnelConnectDone; |
217 | static ERCB tunnelErrorComplete; | |
575d05c4 AJ |
218 | static CLCB tunnelServerClosed; |
219 | static CLCB tunnelClientClosed; | |
8d77a37c | 220 | static CTCB tunnelTimeout; |
11007d4b | 221 | static PSC tunnelPeerSelectComplete; |
215c41fa AJ |
222 | static EVH tunnelDelayedClientRead; |
223 | static EVH tunnelDelayedServerRead; | |
f01d4b80 AJ |
224 | static void tunnelConnected(const Comm::ConnectionPointer &server, void *); |
225 | static void tunnelRelayConnectRequest(const Comm::ConnectionPointer &server, void *); | |
30a4f2a8 | 226 | |
b8d8561b | 227 | static void |
575d05c4 | 228 | tunnelServerClosed(const CommCloseCbParams ¶ms) |
30a4f2a8 | 229 | { |
575d05c4 AJ |
230 | TunnelStateData *tunnelState = (TunnelStateData *)params.data; |
231 | debugs(26, 3, HERE << tunnelState->server.conn); | |
fb046c1b | 232 | tunnelState->server.conn = NULL; |
62e76326 | 233 | |
16b70e2a CT |
234 | if (tunnelState->request != NULL) |
235 | tunnelState->request->hier.stopPeerClock(false); | |
236 | ||
71a2ced6 | 237 | if (tunnelState->noConnections()) { |
8ca1d33d | 238 | delete tunnelState; |
26ac0430 | 239 | return; |
71a2ced6 | 240 | } |
26ac0430 | 241 | |
71a2ced6 | 242 | if (!tunnelState->server.len) { |
fb046c1b | 243 | tunnelState->client.conn->close(); |
26ac0430 | 244 | return; |
71a2ced6 | 245 | } |
30a4f2a8 | 246 | } |
247 | ||
b177367b | 248 | static void |
575d05c4 | 249 | tunnelClientClosed(const CommCloseCbParams ¶ms) |
30a4f2a8 | 250 | { |
575d05c4 AJ |
251 | TunnelStateData *tunnelState = (TunnelStateData *)params.data; |
252 | debugs(26, 3, HERE << tunnelState->client.conn); | |
fb046c1b | 253 | tunnelState->client.conn = NULL; |
62e76326 | 254 | |
71a2ced6 | 255 | if (tunnelState->noConnections()) { |
8ca1d33d | 256 | delete tunnelState; |
26ac0430 | 257 | return; |
71a2ced6 | 258 | } |
26ac0430 | 259 | |
71a2ced6 | 260 | if (!tunnelState->client.len) { |
fb046c1b | 261 | tunnelState->server.conn->close(); |
26ac0430 | 262 | return; |
71a2ced6 | 263 | } |
30a4f2a8 | 264 | } |
983061ed | 265 | |
8ca1d33d | 266 | TunnelStateData::TunnelStateData() : |
f53969cc SM |
267 | url(NULL), |
268 | http(), | |
269 | request(NULL), | |
270 | status_ptr(NULL), | |
271 | logTag_ptr(NULL), | |
272 | connectRespBuf(NULL), | |
273 | connectReqWriting(false) | |
8ca1d33d AJ |
274 | { |
275 | debugs(26, 3, "TunnelStateData constructed this=" << this); | |
215c41fa AJ |
276 | client.readPendingFunc = &tunnelDelayedClientRead; |
277 | server.readPendingFunc = &tunnelDelayedServerRead; | |
8ca1d33d AJ |
278 | } |
279 | ||
280 | TunnelStateData::~TunnelStateData() | |
983061ed | 281 | { |
8ca1d33d AJ |
282 | debugs(26, 3, "TunnelStateData destructed this=" << this); |
283 | assert(noConnections()); | |
284 | xfree(url); | |
c33a88ca | 285 | serverDestinations.clear(); |
3ed5793b | 286 | delete connectRespBuf; |
983061ed | 287 | } |
288 | ||
8a467c4b AJ |
289 | TunnelStateData::Connection::~Connection() |
290 | { | |
215c41fa AJ |
291 | if (readPending) |
292 | eventDelete(readPendingFunc, readPending); | |
293 | ||
8a467c4b AJ |
294 | safe_free(buf); |
295 | } | |
296 | ||
a46d2c0e | 297 | int |
8a467c4b | 298 | TunnelStateData::Connection::bytesWanted(int lowerbound, int upperbound) const |
447e176b | 299 | { |
9a0a18de | 300 | #if USE_DELAY_POOLS |
8a467c4b | 301 | return delayId.bytesWanted(lowerbound, upperbound); |
a46d2c0e | 302 | #else |
8a467c4b AJ |
303 | |
304 | return upperbound; | |
a46d2c0e | 305 | #endif |
306 | } | |
62e76326 | 307 | |
a46d2c0e | 308 | void |
fa34dd97 | 309 | TunnelStateData::Connection::bytesIn(int const &count) |
a46d2c0e | 310 | { |
fd54d9b2 | 311 | debugs(26, 3, HERE << "len=" << len << " + count=" << count); |
9a0a18de | 312 | #if USE_DELAY_POOLS |
a46d2c0e | 313 | delayId.bytesIn(count); |
314 | #endif | |
62e76326 | 315 | |
a46d2c0e | 316 | len += count; |
447e176b | 317 | } |
62e76326 | 318 | |
a46d2c0e | 319 | int |
fa34dd97 | 320 | TunnelStateData::Connection::debugLevelForError(int const xerrno) const |
a46d2c0e | 321 | { |
322 | #ifdef ECONNRESET | |
323 | ||
324 | if (xerrno == ECONNRESET) | |
325 | return 2; | |
326 | ||
447e176b | 327 | #endif |
328 | ||
a46d2c0e | 329 | if (ignoreErrno(xerrno)) |
330 | return 3; | |
331 | ||
332 | return 1; | |
333 | } | |
adb78bd4 | 334 | |
983061ed | 335 | /* Read from server side and queue it for writing to the client */ |
a46d2c0e | 336 | void |
c8407295 | 337 | TunnelStateData::ReadServer(const Comm::ConnectionPointer &c, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data) |
983061ed | 338 | { |
fa34dd97 | 339 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
e0d28505 | 340 | assert(cbdataReferenceValid(tunnelState)); |
fd54d9b2 | 341 | debugs(26, 3, HERE << c); |
c4b7a5a9 | 342 | |
11007d4b | 343 | tunnelState->readServer(buf, len, errcode, xerrno); |
a46d2c0e | 344 | } |
62e76326 | 345 | |
a46d2c0e | 346 | void |
ced8def3 | 347 | TunnelStateData::readServer(char *, size_t len, Comm::Flag errcode, int xerrno) |
a46d2c0e | 348 | { |
fd54d9b2 | 349 | debugs(26, 3, HERE << server.conn << ", read " << len << " bytes, err=" << errcode); |
9fe3d316 | 350 | server.delayedLoops=0; |
d01053a2 | 351 | |
a46d2c0e | 352 | /* |
c8407295 | 353 | * Bail out early on Comm::ERR_CLOSING |
26ac0430 | 354 | * - close handlers will tidy up for us |
a46d2c0e | 355 | */ |
a55f4cea | 356 | |
c8407295 | 357 | if (errcode == Comm::ERR_CLOSING) |
a46d2c0e | 358 | return; |
62e76326 | 359 | |
ee1679df | 360 | if (len > 0) { |
a46d2c0e | 361 | server.bytesIn(len); |
e4f1fdae FC |
362 | kb_incr(&(statCounter.server.all.kbytes_in), len); |
363 | kb_incr(&(statCounter.server.other.kbytes_in), len); | |
ee1679df | 364 | } |
62e76326 | 365 | |
3ed5793b AR |
366 | if (keepGoingAfterRead(len, errcode, xerrno, server, client)) |
367 | copy(len, server, client, WriteClientDone); | |
368 | } | |
369 | ||
370 | /// Called when we read [a part of] CONNECT response from the peer | |
371 | void | |
ced8def3 | 372 | TunnelStateData::readConnectResponseDone(char *, size_t len, Comm::Flag errcode, int xerrno) |
3ed5793b AR |
373 | { |
374 | debugs(26, 3, server.conn << ", read " << len << " bytes, err=" << errcode); | |
375 | assert(waitingForConnectResponse()); | |
376 | ||
c8407295 | 377 | if (errcode == Comm::ERR_CLOSING) |
3ed5793b AR |
378 | return; |
379 | ||
380 | if (len > 0) { | |
381 | connectRespBuf->appended(len); | |
382 | server.bytesIn(len); | |
383 | kb_incr(&(statCounter.server.all.kbytes_in), len); | |
384 | kb_incr(&(statCounter.server.other.kbytes_in), len); | |
385 | } | |
386 | ||
387 | if (keepGoingAfterRead(len, errcode, xerrno, server, client)) | |
388 | handleConnectResponse(len); | |
389 | } | |
390 | ||
391 | /* Read from client side and queue it for writing to the server */ | |
392 | void | |
c8407295 | 393 | TunnelStateData::ReadConnectResponseDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data) |
3ed5793b AR |
394 | { |
395 | TunnelStateData *tunnelState = (TunnelStateData *)data; | |
396 | assert (cbdataReferenceValid (tunnelState)); | |
397 | ||
398 | tunnelState->readConnectResponseDone(buf, len, errcode, xerrno); | |
399 | } | |
400 | ||
401 | /// Parses [possibly incomplete] CONNECT response and reacts to it. | |
402 | /// If the tunnel is being closed or more response data is needed, returns false. | |
403 | /// Otherwise, the caller should handle the remaining read data, if any. | |
404 | void | |
405 | TunnelStateData::handleConnectResponse(const size_t chunkSize) | |
406 | { | |
407 | assert(waitingForConnectResponse()); | |
408 | ||
409 | // Ideally, client and server should use MemBuf or better, but current code | |
410 | // never accumulates more than one read when shoveling data (XXX) so it does | |
1c8fc082 | 411 | // not need to deal with MemBuf complexity. To keep it simple, we use a |
3ed5793b AR |
412 | // dedicated MemBuf for accumulating CONNECT responses. TODO: When shoveling |
413 | // is optimized, reuse server.buf for CONNEC response accumulation instead. | |
414 | ||
415 | /* mimic the basic parts of HttpStateData::processReplyHeader() */ | |
416 | HttpReply rep; | |
417 | Http::StatusCode parseErr = Http::scNone; | |
418 | const bool eof = !chunkSize; | |
84ae6223 AJ |
419 | connectRespBuf->terminate(); // HttpMsg::parse requires terminated string |
420 | const bool parsed = rep.parse(connectRespBuf->content(), connectRespBuf->contentSize(), eof, &parseErr); | |
3ed5793b AR |
421 | if (!parsed) { |
422 | if (parseErr > 0) { // unrecoverable parsing error | |
423 | server.logicError("malformed CONNECT response from peer"); | |
424 | return; | |
425 | } | |
426 | ||
427 | // need more data | |
428 | assert(!eof); | |
429 | assert(!parseErr); | |
430 | ||
431 | if (!connectRespBuf->hasSpace()) { | |
432 | server.logicError("huge CONNECT response from peer"); | |
433 | return; | |
434 | } | |
435 | ||
436 | // keep reading | |
437 | readConnectResponse(); | |
438 | return; | |
439 | } | |
440 | ||
441 | // CONNECT response was successfully parsed | |
442 | *status_ptr = rep.sline.status(); | |
443 | ||
444 | // bail if we did not get an HTTP 200 (Connection Established) response | |
445 | if (rep.sline.status() != Http::scOkay) { | |
446 | server.logicError("unsupported CONNECT response status code"); | |
447 | return; | |
448 | } | |
449 | ||
450 | if (rep.hdr_sz < connectRespBuf->contentSize()) { | |
451 | // preserve bytes that the server already sent after the CONNECT response | |
452 | server.len = connectRespBuf->contentSize() - rep.hdr_sz; | |
453 | memcpy(server.buf, connectRespBuf->content()+rep.hdr_sz, server.len); | |
454 | } else { | |
455 | // reset; delay pools were using this field to throttle CONNECT response | |
456 | server.len = 0; | |
457 | } | |
458 | ||
459 | delete connectRespBuf; | |
460 | connectRespBuf = NULL; | |
461 | connectExchangeCheckpoint(); | |
462 | } | |
463 | ||
464 | void | |
465 | TunnelStateData::Connection::logicError(const char *errMsg) | |
466 | { | |
467 | debugs(50, 3, conn << " closing on error: " << errMsg); | |
468 | conn->close(); | |
a46d2c0e | 469 | } |
470 | ||
471 | void | |
fa34dd97 | 472 | TunnelStateData::Connection::error(int const xerrno) |
a46d2c0e | 473 | { |
474 | /* XXX fixme xstrerror and xerrno... */ | |
475 | errno = xerrno; | |
476 | ||
e0d28505 | 477 | debugs(50, debugLevelForError(xerrno), HERE << conn << ": read/write failure: " << xstrerror()); |
62e76326 | 478 | |
a46d2c0e | 479 | if (!ignoreErrno(xerrno)) |
fb046c1b | 480 | conn->close(); |
983061ed | 481 | } |
482 | ||
483 | /* Read from client side and queue it for writing to the server */ | |
a46d2c0e | 484 | void |
c8407295 | 485 | TunnelStateData::ReadClient(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data) |
983061ed | 486 | { |
fa34dd97 | 487 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 488 | assert (cbdataReferenceValid (tunnelState)); |
62e76326 | 489 | |
11007d4b | 490 | tunnelState->readClient(buf, len, errcode, xerrno); |
a46d2c0e | 491 | } |
62e76326 | 492 | |
a46d2c0e | 493 | void |
ced8def3 | 494 | TunnelStateData::readClient(char *, size_t len, Comm::Flag errcode, int xerrno) |
a46d2c0e | 495 | { |
fd54d9b2 | 496 | debugs(26, 3, HERE << client.conn << ", read " << len << " bytes, err=" << errcode); |
9fe3d316 | 497 | client.delayedLoops=0; |
d01053a2 | 498 | |
a46d2c0e | 499 | /* |
c8407295 | 500 | * Bail out early on Comm::ERR_CLOSING |
26ac0430 | 501 | * - close handlers will tidy up for us |
a46d2c0e | 502 | */ |
a55f4cea | 503 | |
c8407295 | 504 | if (errcode == Comm::ERR_CLOSING) |
a46d2c0e | 505 | return; |
a55f4cea | 506 | |
a46d2c0e | 507 | if (len > 0) { |
508 | client.bytesIn(len); | |
e4f1fdae | 509 | kb_incr(&(statCounter.client_http.kbytes_in), len); |
a46d2c0e | 510 | } |
62e76326 | 511 | |
3ed5793b AR |
512 | if (keepGoingAfterRead(len, errcode, xerrno, client, server)) |
513 | copy(len, client, server, WriteServerDone); | |
a46d2c0e | 514 | } |
62e76326 | 515 | |
3ed5793b AR |
516 | /// Updates state after reading from client or server. |
517 | /// Returns whether the caller should use the data just read. | |
518 | bool | |
c8407295 | 519 | TunnelStateData::keepGoingAfterRead(size_t len, Comm::Flag errcode, int xerrno, Connection &from, Connection &to) |
a46d2c0e | 520 | { |
fd54d9b2 AJ |
521 | debugs(26, 3, HERE << "from={" << from.conn << "}, to={" << to.conn << "}"); |
522 | ||
a46d2c0e | 523 | /* I think this is to prevent free-while-in-a-callback behaviour |
26ac0430 | 524 | * - RBC 20030229 |
fb046c1b | 525 | * from.conn->close() / to.conn->close() done here trigger close callbacks which may free TunnelStateData |
a46d2c0e | 526 | */ |
83564ee7 | 527 | const CbcPointer<TunnelStateData> safetyLock(this); |
62e76326 | 528 | |
60dafd5e | 529 | /* Bump the source connection read timeout on any activity */ |
8d77a37c AJ |
530 | if (Comm::IsConnOpen(from.conn)) { |
531 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", | |
dc49061a | 532 | CommTimeoutCbPtrFun(tunnelTimeout, this)); |
8d77a37c AJ |
533 | commSetConnTimeout(from.conn, Config.Timeout.read, timeoutCall); |
534 | } | |
99c02c10 | 535 | |
2677c3b1 JPM |
536 | /* Bump the dest connection read timeout on any activity */ |
537 | /* see Bug 3659: tunnels can be weird, with very long one-way transfers */ | |
538 | if (Comm::IsConnOpen(to.conn)) { | |
539 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", | |
540 | CommTimeoutCbPtrFun(tunnelTimeout, this)); | |
541 | commSetConnTimeout(to.conn, Config.Timeout.read, timeoutCall); | |
542 | } | |
543 | ||
58c0b17d | 544 | if (errcode) |
a46d2c0e | 545 | from.error (xerrno); |
60dafd5e | 546 | else if (len == 0 || !Comm::IsConnOpen(to.conn)) { |
fd54d9b2 | 547 | debugs(26, 3, HERE << "Nothing to write or client gone. Terminate the tunnel."); |
fb046c1b | 548 | from.conn->close(); |
62e76326 | 549 | |
60dafd5e | 550 | /* Only close the remote end if we've finished queueing data to it */ |
97c81191 | 551 | if (from.len == 0 && Comm::IsConnOpen(to.conn) ) { |
fb046c1b | 552 | to.conn->close(); |
c4b7a5a9 | 553 | } |
ec41b64c | 554 | } else if (cbdataReferenceValid(this)) { |
3ed5793b AR |
555 | return true; |
556 | } | |
557 | ||
558 | return false; | |
559 | } | |
560 | ||
561 | void | |
562 | TunnelStateData::copy(size_t len, Connection &from, Connection &to, IOCB *completion) | |
563 | { | |
1c8fc082 A |
564 | debugs(26, 3, HERE << "Schedule Write"); |
565 | AsyncCall::Pointer call = commCbCall(5,5, "TunnelBlindCopyWriteHandler", | |
566 | CommIoCbPtrFun(completion, this)); | |
567 | Comm::Write(to.conn, from.buf, len, call, NULL); | |
983061ed | 568 | } |
569 | ||
570 | /* Writes data from the client buffer to the server side */ | |
a46d2c0e | 571 | void |
c8407295 | 572 | TunnelStateData::WriteServerDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data) |
983061ed | 573 | { |
fa34dd97 | 574 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 575 | assert (cbdataReferenceValid (tunnelState)); |
a46d2c0e | 576 | |
11007d4b | 577 | tunnelState->writeServerDone(buf, len, flag, xerrno); |
a46d2c0e | 578 | } |
a55f4cea | 579 | |
a46d2c0e | 580 | void |
ced8def3 | 581 | TunnelStateData::writeServerDone(char *, size_t len, Comm::Flag flag, int xerrno) |
a46d2c0e | 582 | { |
fd54d9b2 | 583 | debugs(26, 3, HERE << server.conn << ", " << len << " bytes written, flag=" << flag); |
62e76326 | 584 | |
5dacdf3f | 585 | /* Error? */ |
c8407295 AJ |
586 | if (flag != Comm::OK) { |
587 | if (flag != Comm::ERR_CLOSING) { | |
fd54d9b2 | 588 | debugs(26, 4, HERE << "calling TunnelStateData::server.error(" << xerrno <<")"); |
58c0b17d | 589 | server.error(xerrno); // may call comm_close |
fd54d9b2 | 590 | } |
5dacdf3f | 591 | return; |
c4b7a5a9 | 592 | } |
62e76326 | 593 | |
5dacdf3f | 594 | /* EOF? */ |
a46d2c0e | 595 | if (len == 0) { |
fd54d9b2 | 596 | debugs(26, 4, HERE << "No read input. Closing server connection."); |
fb046c1b | 597 | server.conn->close(); |
a46d2c0e | 598 | return; |
599 | } | |
62e76326 | 600 | |
5dacdf3f | 601 | /* Valid data */ |
e4f1fdae FC |
602 | kb_incr(&(statCounter.server.all.kbytes_out), len); |
603 | kb_incr(&(statCounter.server.other.kbytes_out), len); | |
5dacdf3f | 604 | client.dataSent(len); |
605 | ||
a46d2c0e | 606 | /* If the other end has closed, so should we */ |
97c81191 | 607 | if (!Comm::IsConnOpen(client.conn)) { |
fd54d9b2 | 608 | debugs(26, 4, HERE << "Client gone away. Shutting down server connection."); |
fb046c1b | 609 | server.conn->close(); |
a55f4cea | 610 | return; |
c4b7a5a9 | 611 | } |
62e76326 | 612 | |
f53969cc | 613 | const CbcPointer<TunnelStateData> safetyLock(this); /* ??? should be locked by the caller... */ |
a46d2c0e | 614 | |
5dacdf3f | 615 | if (cbdataReferenceValid(this)) |
3248e962 | 616 | copyClientBytes(); |
983061ed | 617 | } |
618 | ||
619 | /* Writes data from the server buffer to the client side */ | |
a46d2c0e | 620 | void |
c8407295 | 621 | TunnelStateData::WriteClientDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data) |
983061ed | 622 | { |
fa34dd97 | 623 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 624 | assert (cbdataReferenceValid (tunnelState)); |
a46d2c0e | 625 | |
11007d4b | 626 | tunnelState->writeClientDone(buf, len, flag, xerrno); |
a46d2c0e | 627 | } |
628 | ||
629 | void | |
e0d28505 | 630 | TunnelStateData::Connection::dataSent(size_t amount) |
a46d2c0e | 631 | { |
fd54d9b2 | 632 | debugs(26, 3, HERE << "len=" << len << " - amount=" << amount); |
a46d2c0e | 633 | assert(amount == (size_t)len); |
634 | len =0; | |
635 | /* increment total object size */ | |
636 | ||
637 | if (size_ptr) | |
638 | *size_ptr += amount; | |
639 | } | |
640 | ||
641 | void | |
ced8def3 | 642 | TunnelStateData::writeClientDone(char *, size_t len, Comm::Flag flag, int xerrno) |
a46d2c0e | 643 | { |
fd54d9b2 | 644 | debugs(26, 3, HERE << client.conn << ", " << len << " bytes written, flag=" << flag); |
62e76326 | 645 | |
5dacdf3f | 646 | /* Error? */ |
c8407295 AJ |
647 | if (flag != Comm::OK) { |
648 | if (flag != Comm::ERR_CLOSING) { | |
fd54d9b2 | 649 | debugs(26, 4, HERE << "Closing client connection due to comm flags."); |
58c0b17d | 650 | client.error(xerrno); // may call comm_close |
fd54d9b2 | 651 | } |
5dacdf3f | 652 | return; |
c4b7a5a9 | 653 | } |
62e76326 | 654 | |
5dacdf3f | 655 | /* EOF? */ |
a46d2c0e | 656 | if (len == 0) { |
fd54d9b2 | 657 | debugs(26, 4, HERE << "Closing client connection due to 0 byte read."); |
fb046c1b | 658 | client.conn->close(); |
62e76326 | 659 | return; |
983061ed | 660 | } |
62e76326 | 661 | |
5dacdf3f | 662 | /* Valid data */ |
e4f1fdae | 663 | kb_incr(&(statCounter.client_http.kbytes_out), len); |
5dacdf3f | 664 | server.dataSent(len); |
665 | ||
a46d2c0e | 666 | /* If the other end has closed, so should we */ |
97c81191 | 667 | if (!Comm::IsConnOpen(server.conn)) { |
fd54d9b2 | 668 | debugs(26, 4, HERE << "Server has gone away. Terminating client connection."); |
fb046c1b | 669 | client.conn->close(); |
a55f4cea | 670 | return; |
983061ed | 671 | } |
62e76326 | 672 | |
f53969cc | 673 | CbcPointer<TunnelStateData> safetyLock(this); /* ??? should be locked by the caller... */ |
a55f4cea | 674 | |
5dacdf3f | 675 | if (cbdataReferenceValid(this)) |
a46d2c0e | 676 | copyRead(server, ReadServer); |
983061ed | 677 | } |
678 | ||
b8d8561b | 679 | static void |
8d77a37c | 680 | tunnelTimeout(const CommTimeoutCbParams &io) |
983061ed | 681 | { |
8d77a37c AJ |
682 | TunnelStateData *tunnelState = static_cast<TunnelStateData *>(io.data); |
683 | debugs(26, 3, HERE << io.conn); | |
11007d4b | 684 | /* Temporary lock to protect our own feets (comm_close -> tunnelClientClosed -> Free) */ |
83564ee7 | 685 | CbcPointer<TunnelStateData> safetyLock(tunnelState); |
a55f4cea | 686 | |
11007d4b | 687 | tunnelState->client.closeIfOpen(); |
688 | tunnelState->server.closeIfOpen(); | |
a46d2c0e | 689 | } |
62e76326 | 690 | |
a46d2c0e | 691 | void |
fa34dd97 | 692 | TunnelStateData::Connection::closeIfOpen() |
a46d2c0e | 693 | { |
97c81191 | 694 | if (Comm::IsConnOpen(conn)) |
fb046c1b | 695 | conn->close(); |
a46d2c0e | 696 | } |
697 | ||
9fe3d316 AJ |
698 | static void |
699 | tunnelDelayedClientRead(void *data) | |
700 | { | |
701 | if (!data) | |
702 | return; | |
4846303f | 703 | |
215c41fa | 704 | TunnelStateData *tunnel = static_cast<TunnelStateData*>(data); |
215c41fa | 705 | tunnel->client.readPending = NULL; |
9fe3d316 | 706 | static uint64_t counter=0; |
215c41fa | 707 | debugs(26, 7, "Client read(2) delayed " << ++counter << " times"); |
9fe3d316 AJ |
708 | tunnel->copyRead(tunnel->client, TunnelStateData::ReadClient); |
709 | } | |
710 | ||
711 | static void | |
712 | tunnelDelayedServerRead(void *data) | |
713 | { | |
714 | if (!data) | |
715 | return; | |
4846303f | 716 | |
215c41fa | 717 | TunnelStateData *tunnel = static_cast<TunnelStateData*>(data); |
215c41fa | 718 | tunnel->server.readPending = NULL; |
9fe3d316 | 719 | static uint64_t counter=0; |
215c41fa | 720 | debugs(26, 7, "Server read(2) delayed " << ++counter << " times"); |
9fe3d316 AJ |
721 | tunnel->copyRead(tunnel->server, TunnelStateData::ReadServer); |
722 | } | |
723 | ||
a46d2c0e | 724 | void |
fa34dd97 | 725 | TunnelStateData::copyRead(Connection &from, IOCB *completion) |
a46d2c0e | 726 | { |
727 | assert(from.len == 0); | |
9fe3d316 AJ |
728 | // If only the minimum permitted read size is going to be attempted |
729 | // then we schedule an event to try again in a few I/O cycles. | |
730 | // Allow at least 1 byte to be read every (0.3*10) seconds. | |
731 | int bw = from.bytesWanted(1, SQUID_TCP_SO_RCVBUF); | |
732 | if (bw == 1 && ++from.delayedLoops < 10) { | |
215c41fa AJ |
733 | from.readPending = this; |
734 | eventAdd("tunnelDelayedServerRead", from.readPendingFunc, from.readPending, 0.3, true); | |
9fe3d316 AJ |
735 | return; |
736 | } | |
737 | ||
fd54d9b2 | 738 | AsyncCall::Pointer call = commCbCall(5,4, "TunnelBlindCopyReadHandler", |
abd8f140 | 739 | CommIoCbPtrFun(completion, this)); |
9fe3d316 | 740 | comm_read(from.conn, from.buf, bw, call); |
983061ed | 741 | } |
742 | ||
3ed5793b AR |
743 | void |
744 | TunnelStateData::readConnectResponse() | |
745 | { | |
746 | assert(waitingForConnectResponse()); | |
747 | ||
748 | AsyncCall::Pointer call = commCbCall(5,4, "readConnectResponseDone", | |
749 | CommIoCbPtrFun(ReadConnectResponseDone, this)); | |
750 | comm_read(server.conn, connectRespBuf->space(), | |
751 | server.bytesWanted(1, connectRespBuf->spaceSize()), call); | |
752 | } | |
753 | ||
3248e962 CT |
754 | void |
755 | TunnelStateData::copyClientBytes() | |
756 | { | |
757 | if (preReadClientData.length()) { | |
758 | size_t copyBytes = preReadClientData.length() > SQUID_TCP_SO_RCVBUF ? SQUID_TCP_SO_RCVBUF : preReadClientData.length(); | |
759 | memcpy(client.buf, preReadClientData.rawContent(), copyBytes); | |
760 | preReadClientData.consume(copyBytes); | |
761 | client.bytesIn(copyBytes); | |
762 | if (keepGoingAfterRead(copyBytes, Comm::OK, 0, client, server)) | |
763 | copy(copyBytes, client, server, TunnelStateData::WriteServerDone); | |
764 | } else | |
765 | copyRead(client, ReadClient); | |
766 | } | |
767 | ||
379e8c1c | 768 | /** |
87f237a9 | 769 | * Set the HTTP status for this request and sets the read handlers for client |
379e8c1c AR |
770 | * and server side connections. |
771 | */ | |
772 | static void | |
773 | tunnelStartShoveling(TunnelStateData *tunnelState) | |
774 | { | |
3ed5793b | 775 | assert(!tunnelState->waitingForConnectExchange()); |
955394ce | 776 | *tunnelState->status_ptr = Http::scOkay; |
a95989ed CT |
777 | if (tunnelState->logTag_ptr) |
778 | *tunnelState->logTag_ptr = LOG_TCP_TUNNEL; | |
379e8c1c | 779 | if (cbdataReferenceValid(tunnelState)) { |
6c420975 | 780 | |
5eedd56a | 781 | // Shovel any payload already pushed into reply buffer by the server response |
3ed5793b | 782 | if (!tunnelState->server.len) |
1c8fc082 | 783 | tunnelState->copyRead(tunnelState->server, TunnelStateData::ReadServer); |
6c420975 AJ |
784 | else { |
785 | debugs(26, DBG_DATA, "Tunnel server PUSH Payload: \n" << Raw("", tunnelState->server.buf, tunnelState->server.len) << "\n----------"); | |
3ed5793b | 786 | tunnelState->copy(tunnelState->server.len, tunnelState->server, tunnelState->client, TunnelStateData::WriteClientDone); |
6c420975 AJ |
787 | } |
788 | ||
6c420975 AJ |
789 | if (tunnelState->http.valid() && tunnelState->http->getConn() && !tunnelState->http->getConn()->in.buf.isEmpty()) { |
790 | struct ConnStateData::In *in = &tunnelState->http->getConn()->in; | |
791 | debugs(26, DBG_DATA, "Tunnel client PUSH Payload: \n" << in->buf << "\n----------"); | |
3248e962 | 792 | tunnelState->preReadClientData.append(in->buf); |
6c420975 | 793 | in->buf.consume(); // ConnStateData buffer accounting after the shuffle. |
3248e962 CT |
794 | } |
795 | tunnelState->copyClientBytes(); | |
379e8c1c AR |
796 | } |
797 | } | |
798 | ||
b0388924 AJ |
799 | /** |
800 | * All the pieces we need to write to client and/or server connection | |
1b76e6c1 | 801 | * have been written. |
379e8c1c | 802 | * Call the tunnelStartShoveling to start the blind pump. |
b0388924 | 803 | */ |
c4b7a5a9 | 804 | static void |
ced8def3 | 805 | tunnelConnectedWriteDone(const Comm::ConnectionPointer &conn, char *, size_t, Comm::Flag flag, int, void *data) |
c4b7a5a9 | 806 | { |
fa34dd97 | 807 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
fd54d9b2 | 808 | debugs(26, 3, HERE << conn << ", flag=" << flag); |
62e76326 | 809 | |
c8407295 | 810 | if (flag != Comm::OK) { |
955394ce | 811 | *tunnelState->status_ptr = Http::scInternalServerError; |
e0d28505 | 812 | tunnelErrorComplete(conn->fd, data, 0); |
62e76326 | 813 | return; |
814 | } | |
815 | ||
379e8c1c | 816 | tunnelStartShoveling(tunnelState); |
c4b7a5a9 | 817 | } |
818 | ||
3ed5793b AR |
819 | /// Called when we are done writing CONNECT request to a peer. |
820 | static void | |
ced8def3 | 821 | tunnelConnectReqWriteDone(const Comm::ConnectionPointer &conn, char *, size_t, Comm::Flag flag, int, void *data) |
3ed5793b AR |
822 | { |
823 | TunnelStateData *tunnelState = (TunnelStateData *)data; | |
824 | debugs(26, 3, conn << ", flag=" << flag); | |
825 | assert(tunnelState->waitingForConnectRequest()); | |
826 | ||
c8407295 | 827 | if (flag != Comm::OK) { |
3ed5793b AR |
828 | *tunnelState->status_ptr = Http::scInternalServerError; |
829 | tunnelErrorComplete(conn->fd, data, 0); | |
830 | return; | |
831 | } | |
832 | ||
833 | tunnelState->connectReqWriting = false; | |
834 | tunnelState->connectExchangeCheckpoint(); | |
835 | } | |
836 | ||
837 | void | |
838 | TunnelStateData::connectExchangeCheckpoint() | |
839 | { | |
840 | if (waitingForConnectResponse()) { | |
841 | debugs(26, 5, "still reading CONNECT response on " << server.conn); | |
842 | } else if (waitingForConnectRequest()) { | |
843 | debugs(26, 5, "still writing CONNECT request on " << server.conn); | |
844 | } else { | |
845 | assert(!waitingForConnectExchange()); | |
846 | debugs(26, 3, "done with CONNECT exchange on " << server.conn); | |
847 | tunnelConnected(server.conn, this); | |
848 | } | |
849 | } | |
850 | ||
c4b7a5a9 | 851 | /* |
1b76e6c1 | 852 | * handle the write completion from a proxy request to an upstream origin |
c4b7a5a9 | 853 | */ |
b8d8561b | 854 | static void |
f01d4b80 | 855 | tunnelConnected(const Comm::ConnectionPointer &server, void *data) |
983061ed | 856 | { |
fa34dd97 | 857 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
f01d4b80 | 858 | debugs(26, 3, HERE << server << ", tunnelState=" << tunnelState); |
379e8c1c | 859 | |
3ed5793b | 860 | if (!tunnelState->clientExpectsConnectResponse()) |
379e8c1c AR |
861 | tunnelStartShoveling(tunnelState); // ssl-bumped connection, be quiet |
862 | else { | |
863 | AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone", | |
864 | CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState)); | |
865 | Comm::Write(tunnelState->client.conn, conn_established, strlen(conn_established), call, NULL); | |
866 | } | |
983061ed | 867 | } |
868 | ||
b8d8561b | 869 | static void |
fd54d9b2 | 870 | tunnelErrorComplete(int fd/*const Comm::ConnectionPointer &*/, void *data, size_t) |
30a4f2a8 | 871 | { |
fa34dd97 | 872 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
fd54d9b2 | 873 | debugs(26, 3, HERE << "FD " << fd); |
11007d4b | 874 | assert(tunnelState != NULL); |
875 | /* temporary lock to save our own feets (comm_close -> tunnelClientClosed -> Free) */ | |
83564ee7 | 876 | CbcPointer<TunnelStateData> safetyLock(tunnelState); |
62e76326 | 877 | |
97c81191 | 878 | if (Comm::IsConnOpen(tunnelState->client.conn)) |
fb046c1b | 879 | tunnelState->client.conn->close(); |
62e76326 | 880 | |
97c81191 | 881 | if (Comm::IsConnOpen(tunnelState->server.conn)) |
fb046c1b | 882 | tunnelState->server.conn->close(); |
30a4f2a8 | 883 | } |
884 | ||
b8d8561b | 885 | static void |
c8407295 | 886 | tunnelConnectDone(const Comm::ConnectionPointer &conn, Comm::Flag status, int xerrno, void *data) |
983061ed | 887 | { |
fa34dd97 | 888 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
cfd66529 | 889 | |
c8407295 | 890 | if (status != Comm::OK) { |
fd54d9b2 | 891 | debugs(26, 4, HERE << conn << ", comm failure recovery."); |
aed188fd AJ |
892 | /* At this point only the TCP handshake has failed. no data has been passed. |
893 | * we are allowed to re-try the TCP-level connection to alternate IPs for CONNECT. | |
894 | */ | |
b181c1df | 895 | tunnelState->serverDestinations.erase(tunnelState->serverDestinations.begin()); |
c8407295 | 896 | if (status != Comm::TIMEOUT && tunnelState->serverDestinations.size() > 0) { |
aed188fd | 897 | /* Try another IP of this destination host */ |
6ee88490 | 898 | GetMarkingsToServer(tunnelState->request.getRaw(), *tunnelState->serverDestinations[0]); |
fd54d9b2 | 899 | debugs(26, 4, HERE << "retry with : " << tunnelState->serverDestinations[0]); |
aed188fd | 900 | AsyncCall::Pointer call = commCbCall(26,3, "tunnelConnectDone", CommConnectCbPtrFun(tunnelConnectDone, tunnelState)); |
00ae51e4 | 901 | Comm::ConnOpener *cs = new Comm::ConnOpener(tunnelState->serverDestinations[0], call, Config.Timeout.connect); |
aed188fd | 902 | cs->setHost(tunnelState->url); |
855150a4 | 903 | AsyncJob::Start(cs); |
aed188fd | 904 | } else { |
fd54d9b2 | 905 | debugs(26, 4, HERE << "terminate with error."); |
8a70cdbb | 906 | ErrorState *err = new ErrorState(ERR_CONNECT_FAIL, Http::scServiceUnavailable, tunnelState->request.getRaw()); |
955394ce | 907 | *tunnelState->status_ptr = Http::scServiceUnavailable; |
aed188fd AJ |
908 | err->xerrno = xerrno; |
909 | // on timeout is this still: err->xerrno = ETIMEDOUT; | |
4dd643d5 | 910 | err->port = conn->remote.port(); |
aed188fd AJ |
911 | err->callback = tunnelErrorComplete; |
912 | err->callback_data = tunnelState; | |
e0d28505 | 913 | errorSend(tunnelState->client.conn, err); |
16b70e2a CT |
914 | if (tunnelState->request != NULL) |
915 | tunnelState->request->hier.stopPeerClock(false); | |
aed188fd | 916 | } |
cfd66529 AJ |
917 | return; |
918 | } | |
919 | ||
4beb4bab AJ |
920 | #if USE_DELAY_POOLS |
921 | /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */ | |
922 | if (conn->getPeer() && conn->getPeer()->options.no_delay) | |
923 | tunnelState->server.setDelayId(DelayId()); | |
924 | #endif | |
925 | ||
a14f38d0 | 926 | tunnelState->request->hier.note(conn, tunnelState->getHost()); |
4beb4bab | 927 | |
fb046c1b | 928 | tunnelState->server.conn = conn; |
4beb4bab | 929 | tunnelState->request->peer_host = conn->getPeer() ? conn->getPeer()->host : NULL; |
fb046c1b | 930 | comm_add_close_handler(conn->fd, tunnelServerClosed, tunnelState); |
cfd66529 | 931 | |
fd54d9b2 | 932 | debugs(26, 4, HERE << "determine post-connect handling pathway."); |
739b352a AJ |
933 | if (conn->getPeer()) { |
934 | tunnelState->request->peer_login = conn->getPeer()->login; | |
e857372a | 935 | tunnelState->request->flags.proxying = !(conn->getPeer()->options.originserver); |
fe40a877 | 936 | } else { |
cfd66529 | 937 | tunnelState->request->peer_login = NULL; |
e857372a | 938 | tunnelState->request->flags.proxying = false; |
cfd66529 | 939 | } |
62e76326 | 940 | |
45e5102d | 941 | if (tunnelState->request->flags.proxying) |
a23223bf | 942 | tunnelState->connectToPeer(); |
cfd66529 | 943 | else { |
fb046c1b | 944 | tunnelConnected(conn, tunnelState); |
983061ed | 945 | } |
cfd66529 | 946 | |
8d77a37c | 947 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", |
dc49061a | 948 | CommTimeoutCbPtrFun(tunnelTimeout, tunnelState)); |
8d77a37c | 949 | commSetConnTimeout(conn, Config.Timeout.read, timeoutCall); |
983061ed | 950 | } |
30a4f2a8 | 951 | |
770f051d | 952 | void |
06521a10 | 953 | tunnelStart(ClientHttpRequest * http, int64_t * size_ptr, int *status_ptr, const AccessLogEntryPointer &al) |
30a4f2a8 | 954 | { |
fd54d9b2 | 955 | debugs(26, 3, HERE); |
30a4f2a8 | 956 | /* Create state structure. */ |
fa34dd97 | 957 | TunnelStateData *tunnelState = NULL; |
9b312a19 | 958 | ErrorState *err = NULL; |
190154cf | 959 | HttpRequest *request = http->request; |
d5964d58 | 960 | char *url = http->uri; |
fb046c1b | 961 | |
f1003989 | 962 | /* |
4dd643d5 | 963 | * client_addr.isNoAddr() indicates this is an "internal" request |
a4b8110e | 964 | * from peer_digest.c, asn.c, netdb.c, etc and should always |
965 | * be allowed. yuck, I know. | |
966 | */ | |
62e76326 | 967 | |
4dd643d5 | 968 | if (Config.accessList.miss && !request->client_addr.isNoAddr()) { |
62e76326 | 969 | /* |
970 | * Check if this host is allowed to fetch MISSES from us (miss_access) | |
b50e327b | 971 | * default is to allow. |
62e76326 | 972 | */ |
c0941a6a | 973 | ACLFilledChecklist ch(Config.accessList.miss, request, NULL); |
62e76326 | 974 | ch.src_addr = request->client_addr; |
975 | ch.my_addr = request->my_addr; | |
2efeb0b7 | 976 | if (ch.fastCheck() == ACCESS_DENIED) { |
fd54d9b2 | 977 | debugs(26, 4, HERE << "MISS access forbidden."); |
955394ce AJ |
978 | err = new ErrorState(ERR_FORWARDING_DENIED, Http::scForbidden, request); |
979 | *status_ptr = 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 | |
fa34dd97 | 989 | tunnelState = new TunnelStateData; |
9a0a18de | 990 | #if USE_DELAY_POOLS |
11007d4b | 991 | tunnelState->server.setDelayId(DelayId::DelayClient(http)); |
59715b38 | 992 | #endif |
11007d4b | 993 | tunnelState->url = xstrdup(url); |
b248c2a3 | 994 | tunnelState->request = request; |
11007d4b | 995 | tunnelState->server.size_ptr = size_ptr; |
996 | tunnelState->status_ptr = status_ptr; | |
8a713747 | 997 | tunnelState->logTag_ptr = &http->logType; |
73c36fd9 | 998 | tunnelState->client.conn = http->getConn()->clientConnection; |
6c420975 | 999 | tunnelState->http = http; |
06521a10 | 1000 | tunnelState->al = al; |
fb046c1b AJ |
1001 | |
1002 | comm_add_close_handler(tunnelState->client.conn->fd, | |
11007d4b | 1003 | tunnelClientClosed, |
1004 | tunnelState); | |
8d77a37c AJ |
1005 | |
1006 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", | |
dc49061a | 1007 | CommTimeoutCbPtrFun(tunnelTimeout, tunnelState)); |
8d77a37c | 1008 | commSetConnTimeout(tunnelState->client.conn, Config.Timeout.lifetime, timeoutCall); |
cfd66529 | 1009 | |
d4806c91 | 1010 | peerSelect(&(tunnelState->serverDestinations), request, al, |
62e76326 | 1011 | NULL, |
11007d4b | 1012 | tunnelPeerSelectComplete, |
1013 | tunnelState); | |
30a4f2a8 | 1014 | } |
98ffb7e4 | 1015 | |
a23223bf | 1016 | void |
e2849af8 A |
1017 | TunnelStateData::connectToPeer() |
1018 | { | |
a23223bf | 1019 | #if USE_OPENSSL |
fcfdf7f9 | 1020 | if (CachePeer *p = server.conn->getPeer()) { |
1f1f29e8 | 1021 | if (p->secure.encryptTransport) { |
a23223bf | 1022 | AsyncCall::Pointer callback = asyncCall(5,4, |
e2849af8 A |
1023 | "TunnelStateData::ConnectedToPeer", |
1024 | MyAnswerDialer(&TunnelStateData::connectedToPeer, this)); | |
a23223bf | 1025 | Ssl::PeerConnector *connector = |
fcfdf7f9 | 1026 | new Ssl::PeerConnector(request, server.conn, client.conn, callback); |
a23223bf CT |
1027 | AsyncJob::Start(connector); // will call our callback |
1028 | return; | |
1029 | } | |
1030 | } | |
1031 | #endif | |
1032 | ||
fcfdf7f9 AJ |
1033 | Security::EncryptorAnswer nil; |
1034 | connectedToPeer(nil); | |
a23223bf CT |
1035 | } |
1036 | ||
a23223bf | 1037 | void |
fcfdf7f9 | 1038 | TunnelStateData::connectedToPeer(Security::EncryptorAnswer &answer) |
a23223bf CT |
1039 | { |
1040 | if (ErrorState *error = answer.error.get()) { | |
1041 | *status_ptr = error->httpStatus; | |
1042 | error->callback = tunnelErrorComplete; | |
1043 | error->callback_data = this; | |
1044 | errorSend(client.conn, error); | |
1045 | answer.error.clear(); // preserve error for errorSendComplete() | |
1046 | return; | |
1047 | } | |
1048 | ||
1049 | tunnelRelayConnectRequest(server.conn, this); | |
1050 | } | |
a23223bf | 1051 | |
b8d8561b | 1052 | static void |
b0388924 | 1053 | tunnelRelayConnectRequest(const Comm::ConnectionPointer &srv, void *data) |
98ffb7e4 | 1054 | { |
fa34dd97 | 1055 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
3ed5793b | 1056 | assert(!tunnelState->waitingForConnectExchange()); |
75faaa7a | 1057 | HttpHeader hdr_out(hoRequest); |
46f4b111 | 1058 | HttpStateFlags flags; |
b0388924 | 1059 | debugs(26, 3, HERE << srv << ", tunnelState=" << tunnelState); |
b4b5fd95 | 1060 | memset(&flags, '\0', sizeof(flags)); |
45e5102d | 1061 | flags.proxying = tunnelState->request->flags.proxying; |
032785bf | 1062 | MemBuf mb; |
2fe7eff9 | 1063 | mb.init(); |
3872be7c | 1064 | mb.Printf("CONNECT %s HTTP/1.1\r\n", tunnelState->url); |
8a70cdbb | 1065 | HttpStateData::httpBuildRequestHeader(tunnelState->request.getRaw(), |
f53969cc SM |
1066 | NULL, /* StoreEntry */ |
1067 | tunnelState->al, /* AccessLogEntry */ | |
e5ee81f0 | 1068 | &hdr_out, |
f53969cc | 1069 | flags); /* flags */ |
10201568 | 1070 | hdr_out.packInto(&mb); |
519e0948 | 1071 | hdr_out.clean(); |
2fe7eff9 | 1072 | mb.append("\r\n", 2); |
c4b7a5a9 | 1073 | |
6c420975 AJ |
1074 | debugs(11, 2, "Tunnel Server REQUEST: " << tunnelState->server.conn << ":\n----------\n" << |
1075 | Raw("tunnelRelayConnectRequest", mb.content(), mb.contentSize()) << "\n----------"); | |
1076 | ||
3ed5793b | 1077 | if (tunnelState->clientExpectsConnectResponse()) { |
1c8fc082 A |
1078 | // hack: blindly tunnel peer response (to our CONNECT request) to the client as ours. |
1079 | AsyncCall::Pointer writeCall = commCbCall(5,5, "tunnelConnectedWriteDone", | |
1080 | CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState)); | |
1081 | Comm::Write(srv, &mb, writeCall); | |
3ed5793b AR |
1082 | } else { |
1083 | // we have to eat the connect response from the peer (so that the client | |
1084 | // does not see it) and only then start shoveling data to the client | |
1085 | AsyncCall::Pointer writeCall = commCbCall(5,5, "tunnelConnectReqWriteDone", | |
1086 | CommIoCbPtrFun(tunnelConnectReqWriteDone, | |
f53969cc | 1087 | tunnelState)); |
3ed5793b AR |
1088 | Comm::Write(srv, &mb, writeCall); |
1089 | tunnelState->connectReqWriting = true; | |
1090 | ||
1091 | tunnelState->connectRespBuf = new MemBuf; | |
1092 | // SQUID_TCP_SO_RCVBUF: we should not accumulate more than regular I/O buffer | |
1093 | // can hold since any CONNECT response leftovers have to fit into server.buf. | |
1094 | // 2*SQUID_TCP_SO_RCVBUF: HttpMsg::parse() zero-terminates, which uses space. | |
1095 | tunnelState->connectRespBuf->init(SQUID_TCP_SO_RCVBUF, 2*SQUID_TCP_SO_RCVBUF); | |
1096 | tunnelState->readConnectResponse(); | |
1097 | ||
1098 | assert(tunnelState->waitingForConnectExchange()); | |
1099 | } | |
8d77a37c AJ |
1100 | |
1101 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", | |
dc49061a | 1102 | CommTimeoutCbPtrFun(tunnelTimeout, tunnelState)); |
8d77a37c | 1103 | commSetConnTimeout(srv, Config.Timeout.read, timeoutCall); |
98ffb7e4 | 1104 | } |
33ea9fff | 1105 | |
33ea9fff | 1106 | static void |
a37fdd8a | 1107 | tunnelPeerSelectComplete(Comm::ConnectionList *peer_paths, ErrorState *err, void *data) |
33ea9fff | 1108 | { |
fa34dd97 | 1109 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
62e76326 | 1110 | |
cfd66529 | 1111 | if (peer_paths == NULL || peer_paths->size() < 1) { |
fd54d9b2 | 1112 | debugs(26, 3, HERE << "No paths found. Aborting CONNECT"); |
a37fdd8a | 1113 | if (!err) { |
8a70cdbb | 1114 | err = new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, tunnelState->request.getRaw()); |
a37fdd8a AJ |
1115 | } |
1116 | *tunnelState->status_ptr = err->httpStatus; | |
11007d4b | 1117 | err->callback = tunnelErrorComplete; |
1118 | err->callback_data = tunnelState; | |
e0d28505 | 1119 | errorSend(tunnelState->client.conn, err); |
62e76326 | 1120 | return; |
db1cd23c | 1121 | } |
a37fdd8a AJ |
1122 | delete err; |
1123 | ||
6ee88490 | 1124 | GetMarkingsToServer(tunnelState->request.getRaw(), *tunnelState->serverDestinations[0]); |
6dc6127b | 1125 | |
16b70e2a CT |
1126 | if (tunnelState->request != NULL) |
1127 | tunnelState->request->hier.startPeerClock(); | |
1128 | ||
fd54d9b2 AJ |
1129 | debugs(26, 3, HERE << "paths=" << peer_paths->size() << ", p[0]={" << (*peer_paths)[0] << "}, serverDest[0]={" << |
1130 | tunnelState->serverDestinations[0] << "}"); | |
62e76326 | 1131 | |
cfd66529 | 1132 | AsyncCall::Pointer call = commCbCall(26,3, "tunnelConnectDone", CommConnectCbPtrFun(tunnelConnectDone, tunnelState)); |
00ae51e4 | 1133 | Comm::ConnOpener *cs = new Comm::ConnOpener(tunnelState->serverDestinations[0], call, Config.Timeout.connect); |
aed188fd | 1134 | cs->setHost(tunnelState->url); |
855150a4 | 1135 | AsyncJob::Start(cs); |
33ea9fff | 1136 | } |
a46d2c0e | 1137 | |
fa34dd97 | 1138 | CBDATA_CLASS_INIT(TunnelStateData); |
a46d2c0e | 1139 | |
a46d2c0e | 1140 | bool |
fa34dd97 | 1141 | TunnelStateData::noConnections() const |
a46d2c0e | 1142 | { |
97c81191 | 1143 | return !Comm::IsConnOpen(server.conn) && !Comm::IsConnOpen(client.conn); |
a46d2c0e | 1144 | } |
1145 | ||
9a0a18de | 1146 | #if USE_DELAY_POOLS |
a46d2c0e | 1147 | void |
fa34dd97 | 1148 | TunnelStateData::Connection::setDelayId(DelayId const &newDelay) |
a46d2c0e | 1149 | { |
1150 | delayId = newDelay; | |
1151 | } | |
1152 | ||
1153 | #endif | |
93ead3fd | 1154 | |
1110989a | 1155 | #if USE_OPENSSL |
93ead3fd | 1156 | void |
c91d4d4e | 1157 | switchToTunnel(HttpRequest *request, Comm::ConnectionPointer &clientConn, Comm::ConnectionPointer &srvConn) |
93ead3fd | 1158 | { |
c91d4d4e | 1159 | debugs(26,5, "Revert to tunnel FD " << clientConn->fd << " with FD " << srvConn->fd); |
93ead3fd CT |
1160 | /* Create state structure. */ |
1161 | TunnelStateData *tunnelState = NULL; | |
1162 | const char *url = urlCanonical(request); | |
1163 | ||
e4f14091 | 1164 | debugs(26, 3, request->method << " " << url << " " << request->http_ver); |
93ead3fd CT |
1165 | ++statCounter.server.all.requests; |
1166 | ++statCounter.server.other.requests; | |
1167 | ||
1168 | tunnelState = new TunnelStateData; | |
93ead3fd CT |
1169 | tunnelState->url = xstrdup(url); |
1170 | tunnelState->request = request; | |
8693472e | 1171 | tunnelState->server.size_ptr = NULL; //Set later if ClientSocketContext is available |
c91d4d4e | 1172 | |
f53969cc | 1173 | // Temporary static variable to store the unneeded for our case status code |
c91d4d4e CT |
1174 | static int status_code = 0; |
1175 | tunnelState->status_ptr = &status_code; | |
93ead3fd CT |
1176 | tunnelState->client.conn = clientConn; |
1177 | ||
a95989ed CT |
1178 | ConnStateData *conn; |
1179 | if ((conn = request->clientConnectionManager.get())) { | |
1180 | ClientSocketContext::Pointer context = conn->getCurrentContext(); | |
1181 | if (context != NULL && context->http != NULL) { | |
1182 | tunnelState->logTag_ptr = &context->http->logType; | |
8693472e | 1183 | tunnelState->server.size_ptr = &context->http->out.size; |
a95989ed CT |
1184 | |
1185 | #if USE_DELAY_POOLS | |
1186 | /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */ | |
1187 | if (srvConn->getPeer() && srvConn->getPeer()->options.no_delay) | |
1188 | tunnelState->server.setDelayId(DelayId::DelayClient(context->http)); | |
1189 | #endif | |
1190 | } | |
1191 | } | |
1192 | ||
93ead3fd CT |
1193 | comm_add_close_handler(tunnelState->client.conn->fd, |
1194 | tunnelClientClosed, | |
1195 | tunnelState); | |
1196 | ||
1197 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", | |
1198 | CommTimeoutCbPtrFun(tunnelTimeout, tunnelState)); | |
1199 | commSetConnTimeout(tunnelState->client.conn, Config.Timeout.lifetime, timeoutCall); | |
1200 | fd_table[clientConn->fd].read_method = &default_read_method; | |
1201 | fd_table[clientConn->fd].write_method = &default_write_method; | |
1202 | ||
93ead3fd CT |
1203 | tunnelState->request->hier.note(srvConn, tunnelState->getHost()); |
1204 | ||
1205 | tunnelState->server.conn = srvConn; | |
1206 | tunnelState->request->peer_host = srvConn->getPeer() ? srvConn->getPeer()->host : NULL; | |
1207 | comm_add_close_handler(srvConn->fd, tunnelServerClosed, tunnelState); | |
1208 | ||
e4f14091 | 1209 | debugs(26, 4, "determine post-connect handling pathway."); |
93ead3fd CT |
1210 | if (srvConn->getPeer()) { |
1211 | tunnelState->request->peer_login = srvConn->getPeer()->login; | |
1212 | tunnelState->request->flags.proxying = !(srvConn->getPeer()->options.originserver); | |
1213 | } else { | |
1214 | tunnelState->request->peer_login = NULL; | |
1215 | tunnelState->request->flags.proxying = false; | |
1216 | } | |
1217 | ||
1218 | timeoutCall = commCbCall(5, 4, "tunnelTimeout", | |
e1f72a8b | 1219 | CommTimeoutCbPtrFun(tunnelTimeout, tunnelState)); |
93ead3fd CT |
1220 | commSetConnTimeout(srvConn, Config.Timeout.read, timeoutCall); |
1221 | fd_table[srvConn->fd].read_method = &default_read_method; | |
1222 | fd_table[srvConn->fd].write_method = &default_write_method; | |
1223 | ||
a95989ed CT |
1224 | SSL *ssl = fd_table[srvConn->fd].ssl; |
1225 | assert(ssl); | |
1226 | BIO *b = SSL_get_rbio(ssl); | |
1227 | Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr); | |
1228 | const MemBuf &buf = srvBio->rBufData(); | |
93ead3fd | 1229 | |
a95989ed CT |
1230 | AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone", |
1231 | CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState)); | |
1232 | Comm::Write(tunnelState->client.conn, buf.content(), buf.contentSize(), call, NULL); | |
93ead3fd | 1233 | } |
1110989a | 1234 | #endif //USE_OPENSSL |
f53969cc | 1235 |