]>
Commit | Line | Data |
---|---|---|
95d659f0 | 1 | |
983061ed | 2 | /* |
30a4f2a8 | 3 | * DEBUG: section 26 Secure Sockets Layer Proxy |
4 | * AUTHOR: Duane Wessels | |
5 | * | |
2b6662ba | 6 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 7 | * ---------------------------------------------------------- |
30a4f2a8 | 8 | * |
2b6662ba | 9 | * Squid is the result of efforts by numerous individuals from |
10 | * the Internet community; see the CONTRIBUTORS file for full | |
11 | * details. Many organizations have provided support for Squid's | |
12 | * development; see the SPONSORS file for full details. Squid is | |
13 | * Copyrighted (C) 2001 by the Regents of the University of | |
14 | * California; see the COPYRIGHT file for full details. Squid | |
15 | * incorporates software developed and/or copyrighted by other | |
16 | * sources; see the CREDITS file for full details. | |
30a4f2a8 | 17 | * |
18 | * This program is free software; you can redistribute it and/or modify | |
19 | * it under the terms of the GNU General Public License as published by | |
20 | * the Free Software Foundation; either version 2 of the License, or | |
21 | * (at your option) any later version. | |
26ac0430 | 22 | * |
30a4f2a8 | 23 | * This program is distributed in the hope that it will be useful, |
24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
26 | * GNU General Public License for more details. | |
26ac0430 | 27 | * |
30a4f2a8 | 28 | * You should have received a copy of the GNU General Public License |
29 | * along with this program; if not, write to the Free Software | |
cbdec147 | 30 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 31 | * |
983061ed | 32 | */ |
983061ed | 33 | |
582c2af2 | 34 | #include "squid.h" |
a011edee | 35 | #include "acl/FilledChecklist.h" |
08f774de | 36 | #include "base/Vector.h" |
a011edee | 37 | #include "CachePeer.h" |
a011edee | 38 | #include "client_side.h" |
602d9612 | 39 | #include "client_side_request.h" |
9f518b4a | 40 | #include "comm.h" |
cfd66529 | 41 | #include "comm/Connection.h" |
aed188fd | 42 | #include "comm/ConnOpener.h" |
ec41b64c | 43 | #include "comm/Write.h" |
a011edee FC |
44 | #include "errorpage.h" |
45 | #include "fde.h" | |
e5ee81f0 | 46 | #include "http.h" |
a011edee | 47 | #include "HttpRequest.h" |
46f4b111 | 48 | #include "HttpStateFlags.h" |
6dc6127b | 49 | #include "ip/QosConfig.h" |
a011edee | 50 | #include "MemBuf.h" |
cfd66529 | 51 | #include "PeerSelectState.h" |
4d5904f7 | 52 | #include "SquidConfig.h" |
e4f1fdae | 53 | #include "StatCounters.h" |
4e540555 | 54 | #include "tools.h" |
582c2af2 FC |
55 | #if USE_DELAY_POOLS |
56 | #include "DelayId.h" | |
57 | #endif | |
58 | ||
59 | #if HAVE_LIMITS_H | |
60 | #include <limits.h> | |
61 | #endif | |
21d845b1 FC |
62 | #if HAVE_ERRNO_H |
63 | #include <errno.h> | |
64 | #endif | |
582c2af2 | 65 | |
8ca1d33d AJ |
66 | /** |
67 | * TunnelStateData is the state engine performing the tasks for | |
68 | * setup of a TCP tunnel from an existing open client FD to a server | |
69 | * then shuffling binary data between the resulting FD pair. | |
70 | */ | |
71 | /* | |
72 | * TODO 1: implement a read/write API on ConnStateData to send/receive blocks | |
73 | * of pre-formatted data. Then we can use that as the client side of the tunnel | |
74 | * instead of re-implementing it here and occasionally getting the ConnStateData | |
75 | * read/write state wrong. | |
76 | * | |
77 | * TODO 2: then convert this into a AsyncJob, possibly a child of 'Server' | |
78 | */ | |
fa34dd97 | 79 | class TunnelStateData |
62e76326 | 80 | { |
a46d2c0e | 81 | |
82 | public: | |
8ca1d33d AJ |
83 | TunnelStateData(); |
84 | ~TunnelStateData(); | |
85 | TunnelStateData(const TunnelStateData &); // do not implement | |
86 | TunnelStateData &operator =(const TunnelStateData &); // do not implement | |
a46d2c0e | 87 | |
88 | class Connection; | |
e0d28505 AJ |
89 | static void ReadClient(const Comm::ConnectionPointer &, char *buf, size_t len, comm_err_t errcode, int xerrno, void *data); |
90 | static void ReadServer(const Comm::ConnectionPointer &, char *buf, size_t len, comm_err_t errcode, int xerrno, void *data); | |
91 | static void WriteClientDone(const Comm::ConnectionPointer &, char *buf, size_t len, comm_err_t flag, int xerrno, void *data); | |
92 | static void WriteServerDone(const Comm::ConnectionPointer &, char *buf, size_t len, comm_err_t flag, int xerrno, void *data); | |
a46d2c0e | 93 | |
3ed5793b AR |
94 | /// Starts reading peer response to our CONNECT request. |
95 | void readConnectResponse(); | |
96 | ||
97 | /// Called when we may be done handling a CONNECT exchange with the peer. | |
98 | void connectExchangeCheckpoint(); | |
99 | ||
a46d2c0e | 100 | bool noConnections() const; |
983061ed | 101 | char *url; |
8a70cdbb | 102 | HttpRequest::Pointer request; |
06521a10 | 103 | AccessLogEntryPointer al; |
00ae51e4 | 104 | Comm::ConnectionList serverDestinations; |
62e76326 | 105 | |
fb046c1b AJ |
106 | const char * getHost() const { |
107 | return (server.conn != NULL && server.conn->getPeer() ? server.conn->getPeer()->host : request->GetHost()); | |
108 | }; | |
109 | ||
3ed5793b AR |
110 | /// Whether we are writing a CONNECT request to a peer. |
111 | bool waitingForConnectRequest() const { return connectReqWriting; } | |
112 | /// Whether we are reading a CONNECT response from a peer. | |
113 | bool waitingForConnectResponse() const { return connectRespBuf; } | |
114 | /// Whether we are waiting for the CONNECT request/response exchange with the peer. | |
115 | bool waitingForConnectExchange() const { return waitingForConnectRequest() || waitingForConnectResponse(); } | |
116 | ||
117 | /// Whether the client sent a CONNECT request to us. | |
1c8fc082 A |
118 | bool clientExpectsConnectResponse() const { |
119 | return !(request != NULL && | |
120 | (request->flags.interceptTproxy || request->flags.intercepted)); | |
121 | } | |
3ed5793b | 122 | |
a46d2c0e | 123 | class Connection |
62e76326 | 124 | { |
a46d2c0e | 125 | |
126 | public: | |
8a467c4b | 127 | Connection() : len (0), buf ((char *)xmalloc(SQUID_TCP_SO_RCVBUF)), size_ptr(NULL) {} |
a46d2c0e | 128 | |
8a467c4b AJ |
129 | ~Connection(); |
130 | ||
131 | int bytesWanted(int lower=0, int upper = INT_MAX) const; | |
a46d2c0e | 132 | void bytesIn(int const &); |
9a0a18de | 133 | #if USE_DELAY_POOLS |
a46d2c0e | 134 | |
135 | void setDelayId(DelayId const &); | |
136 | #endif | |
137 | ||
138 | void error(int const xerrno); | |
5c926411 | 139 | int debugLevelForError(int const xerrno) const; |
3ed5793b AR |
140 | /// handles a non-I/O error associated with this Connection |
141 | void logicError(const char *errMsg); | |
a46d2c0e | 142 | void closeIfOpen(); |
8a467c4b | 143 | void dataSent (size_t amount); |
62e76326 | 144 | int len; |
8a467c4b | 145 | char *buf; |
47f6e231 | 146 | int64_t *size_ptr; /* pointer to size in an ConnStateData for logging */ |
62e76326 | 147 | |
fb046c1b | 148 | Comm::ConnectionPointer conn; ///< The currently connected connection. |
fb046c1b | 149 | |
a46d2c0e | 150 | private: |
9a0a18de | 151 | #if USE_DELAY_POOLS |
62e76326 | 152 | |
a46d2c0e | 153 | DelayId delayId; |
59715b38 | 154 | #endif |
62e76326 | 155 | |
a46d2c0e | 156 | }; |
157 | ||
158 | Connection client, server; | |
159 | int *status_ptr; /* pointer to status for logging */ | |
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 | |
162 | ||
a46d2c0e | 163 | void copyRead(Connection &from, IOCB *completion); |
164 | ||
165 | private: | |
83564ee7 | 166 | CBDATA_CLASS2(TunnelStateData); |
3ed5793b AR |
167 | bool keepGoingAfterRead(size_t len, comm_err_t errcode, int xerrno, Connection &from, Connection &to); |
168 | void copy(size_t len, Connection &from, Connection &to, IOCB *); | |
169 | void handleConnectResponse(const size_t chunkSize); | |
a46d2c0e | 170 | void readServer(char *buf, size_t len, comm_err_t errcode, int xerrno); |
171 | void readClient(char *buf, size_t len, comm_err_t errcode, int xerrno); | |
172 | void writeClientDone(char *buf, size_t len, comm_err_t flag, int xerrno); | |
173 | void writeServerDone(char *buf, size_t len, comm_err_t flag, int xerrno); | |
3ed5793b AR |
174 | |
175 | static void ReadConnectResponseDone(const Comm::ConnectionPointer &, char *buf, size_t len, comm_err_t errcode, int xerrno, void *data); | |
176 | void readConnectResponseDone(char *buf, size_t len, comm_err_t errcode, int xerrno); | |
a46d2c0e | 177 | }; |
983061ed | 178 | |
3c4fcf0f | 179 | static const char *const conn_established = "HTTP/1.1 200 Connection established\r\n\r\n"; |
983061ed | 180 | |
11007d4b | 181 | static CNCB tunnelConnectDone; |
182 | static ERCB tunnelErrorComplete; | |
575d05c4 AJ |
183 | static CLCB tunnelServerClosed; |
184 | static CLCB tunnelClientClosed; | |
8d77a37c | 185 | static CTCB tunnelTimeout; |
11007d4b | 186 | static PSC tunnelPeerSelectComplete; |
f01d4b80 AJ |
187 | static void tunnelConnected(const Comm::ConnectionPointer &server, void *); |
188 | static void tunnelRelayConnectRequest(const Comm::ConnectionPointer &server, void *); | |
30a4f2a8 | 189 | |
b8d8561b | 190 | static void |
575d05c4 | 191 | tunnelServerClosed(const CommCloseCbParams ¶ms) |
30a4f2a8 | 192 | { |
575d05c4 AJ |
193 | TunnelStateData *tunnelState = (TunnelStateData *)params.data; |
194 | debugs(26, 3, HERE << tunnelState->server.conn); | |
fb046c1b | 195 | tunnelState->server.conn = NULL; |
62e76326 | 196 | |
71a2ced6 | 197 | if (tunnelState->noConnections()) { |
8ca1d33d | 198 | delete tunnelState; |
26ac0430 | 199 | return; |
71a2ced6 | 200 | } |
26ac0430 | 201 | |
71a2ced6 | 202 | if (!tunnelState->server.len) { |
fb046c1b | 203 | tunnelState->client.conn->close(); |
26ac0430 | 204 | return; |
71a2ced6 | 205 | } |
30a4f2a8 | 206 | } |
207 | ||
b177367b | 208 | static void |
575d05c4 | 209 | tunnelClientClosed(const CommCloseCbParams ¶ms) |
30a4f2a8 | 210 | { |
575d05c4 AJ |
211 | TunnelStateData *tunnelState = (TunnelStateData *)params.data; |
212 | debugs(26, 3, HERE << tunnelState->client.conn); | |
fb046c1b | 213 | tunnelState->client.conn = NULL; |
62e76326 | 214 | |
71a2ced6 | 215 | if (tunnelState->noConnections()) { |
8ca1d33d | 216 | delete tunnelState; |
26ac0430 | 217 | return; |
71a2ced6 | 218 | } |
26ac0430 | 219 | |
71a2ced6 | 220 | if (!tunnelState->client.len) { |
fb046c1b | 221 | tunnelState->server.conn->close(); |
26ac0430 | 222 | return; |
71a2ced6 | 223 | } |
30a4f2a8 | 224 | } |
983061ed | 225 | |
8ca1d33d AJ |
226 | TunnelStateData::TunnelStateData() : |
227 | url(NULL), | |
228 | request(NULL), | |
3ed5793b AR |
229 | status_ptr(NULL), |
230 | connectRespBuf(NULL), | |
231 | connectReqWriting(false) | |
8ca1d33d AJ |
232 | { |
233 | debugs(26, 3, "TunnelStateData constructed this=" << this); | |
234 | } | |
235 | ||
236 | TunnelStateData::~TunnelStateData() | |
983061ed | 237 | { |
8ca1d33d AJ |
238 | debugs(26, 3, "TunnelStateData destructed this=" << this); |
239 | assert(noConnections()); | |
240 | xfree(url); | |
241 | serverDestinations.clean(); | |
3ed5793b | 242 | delete connectRespBuf; |
983061ed | 243 | } |
244 | ||
8a467c4b AJ |
245 | TunnelStateData::Connection::~Connection() |
246 | { | |
247 | safe_free(buf); | |
248 | } | |
249 | ||
a46d2c0e | 250 | int |
8a467c4b | 251 | TunnelStateData::Connection::bytesWanted(int lowerbound, int upperbound) const |
447e176b | 252 | { |
9a0a18de | 253 | #if USE_DELAY_POOLS |
8a467c4b | 254 | return delayId.bytesWanted(lowerbound, upperbound); |
a46d2c0e | 255 | #else |
8a467c4b AJ |
256 | |
257 | return upperbound; | |
a46d2c0e | 258 | #endif |
259 | } | |
62e76326 | 260 | |
a46d2c0e | 261 | void |
fa34dd97 | 262 | TunnelStateData::Connection::bytesIn(int const &count) |
a46d2c0e | 263 | { |
fd54d9b2 | 264 | debugs(26, 3, HERE << "len=" << len << " + count=" << count); |
9a0a18de | 265 | #if USE_DELAY_POOLS |
a46d2c0e | 266 | delayId.bytesIn(count); |
267 | #endif | |
62e76326 | 268 | |
a46d2c0e | 269 | len += count; |
447e176b | 270 | } |
62e76326 | 271 | |
a46d2c0e | 272 | int |
fa34dd97 | 273 | TunnelStateData::Connection::debugLevelForError(int const xerrno) const |
a46d2c0e | 274 | { |
275 | #ifdef ECONNRESET | |
276 | ||
277 | if (xerrno == ECONNRESET) | |
278 | return 2; | |
279 | ||
447e176b | 280 | #endif |
281 | ||
a46d2c0e | 282 | if (ignoreErrno(xerrno)) |
283 | return 3; | |
284 | ||
285 | return 1; | |
286 | } | |
adb78bd4 | 287 | |
983061ed | 288 | /* Read from server side and queue it for writing to the client */ |
a46d2c0e | 289 | void |
fd54d9b2 | 290 | TunnelStateData::ReadServer(const Comm::ConnectionPointer &c, char *buf, size_t len, comm_err_t errcode, int xerrno, void *data) |
983061ed | 291 | { |
fa34dd97 | 292 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
e0d28505 | 293 | assert(cbdataReferenceValid(tunnelState)); |
fd54d9b2 | 294 | debugs(26, 3, HERE << c); |
c4b7a5a9 | 295 | |
11007d4b | 296 | tunnelState->readServer(buf, len, errcode, xerrno); |
a46d2c0e | 297 | } |
62e76326 | 298 | |
a46d2c0e | 299 | void |
fa34dd97 | 300 | TunnelStateData::readServer(char *buf, size_t len, comm_err_t errcode, int xerrno) |
a46d2c0e | 301 | { |
fd54d9b2 | 302 | debugs(26, 3, HERE << server.conn << ", read " << len << " bytes, err=" << errcode); |
d01053a2 | 303 | |
a46d2c0e | 304 | /* |
305 | * Bail out early on COMM_ERR_CLOSING | |
26ac0430 | 306 | * - close handlers will tidy up for us |
a46d2c0e | 307 | */ |
a55f4cea | 308 | |
a46d2c0e | 309 | if (errcode == COMM_ERR_CLOSING) |
310 | return; | |
62e76326 | 311 | |
ee1679df | 312 | if (len > 0) { |
a46d2c0e | 313 | server.bytesIn(len); |
e4f1fdae FC |
314 | kb_incr(&(statCounter.server.all.kbytes_in), len); |
315 | kb_incr(&(statCounter.server.other.kbytes_in), len); | |
ee1679df | 316 | } |
62e76326 | 317 | |
3ed5793b AR |
318 | if (keepGoingAfterRead(len, errcode, xerrno, server, client)) |
319 | copy(len, server, client, WriteClientDone); | |
320 | } | |
321 | ||
322 | /// Called when we read [a part of] CONNECT response from the peer | |
323 | void | |
324 | TunnelStateData::readConnectResponseDone(char *buf, size_t len, comm_err_t errcode, int xerrno) | |
325 | { | |
326 | debugs(26, 3, server.conn << ", read " << len << " bytes, err=" << errcode); | |
327 | assert(waitingForConnectResponse()); | |
328 | ||
329 | if (errcode == COMM_ERR_CLOSING) | |
330 | return; | |
331 | ||
332 | if (len > 0) { | |
333 | connectRespBuf->appended(len); | |
334 | server.bytesIn(len); | |
335 | kb_incr(&(statCounter.server.all.kbytes_in), len); | |
336 | kb_incr(&(statCounter.server.other.kbytes_in), len); | |
337 | } | |
338 | ||
339 | if (keepGoingAfterRead(len, errcode, xerrno, server, client)) | |
340 | handleConnectResponse(len); | |
341 | } | |
342 | ||
343 | /* Read from client side and queue it for writing to the server */ | |
344 | void | |
345 | TunnelStateData::ReadConnectResponseDone(const Comm::ConnectionPointer &, char *buf, size_t len, comm_err_t errcode, int xerrno, void *data) | |
346 | { | |
347 | TunnelStateData *tunnelState = (TunnelStateData *)data; | |
348 | assert (cbdataReferenceValid (tunnelState)); | |
349 | ||
350 | tunnelState->readConnectResponseDone(buf, len, errcode, xerrno); | |
351 | } | |
352 | ||
353 | /// Parses [possibly incomplete] CONNECT response and reacts to it. | |
354 | /// If the tunnel is being closed or more response data is needed, returns false. | |
355 | /// Otherwise, the caller should handle the remaining read data, if any. | |
356 | void | |
357 | TunnelStateData::handleConnectResponse(const size_t chunkSize) | |
358 | { | |
359 | assert(waitingForConnectResponse()); | |
360 | ||
361 | // Ideally, client and server should use MemBuf or better, but current code | |
362 | // never accumulates more than one read when shoveling data (XXX) so it does | |
1c8fc082 | 363 | // not need to deal with MemBuf complexity. To keep it simple, we use a |
3ed5793b AR |
364 | // dedicated MemBuf for accumulating CONNECT responses. TODO: When shoveling |
365 | // is optimized, reuse server.buf for CONNEC response accumulation instead. | |
366 | ||
367 | /* mimic the basic parts of HttpStateData::processReplyHeader() */ | |
368 | HttpReply rep; | |
369 | Http::StatusCode parseErr = Http::scNone; | |
370 | const bool eof = !chunkSize; | |
371 | const bool parsed = rep.parse(connectRespBuf, eof, &parseErr); | |
372 | if (!parsed) { | |
373 | if (parseErr > 0) { // unrecoverable parsing error | |
374 | server.logicError("malformed CONNECT response from peer"); | |
375 | return; | |
376 | } | |
377 | ||
378 | // need more data | |
379 | assert(!eof); | |
380 | assert(!parseErr); | |
381 | ||
382 | if (!connectRespBuf->hasSpace()) { | |
383 | server.logicError("huge CONNECT response from peer"); | |
384 | return; | |
385 | } | |
386 | ||
387 | // keep reading | |
388 | readConnectResponse(); | |
389 | return; | |
390 | } | |
391 | ||
392 | // CONNECT response was successfully parsed | |
393 | *status_ptr = rep.sline.status(); | |
394 | ||
395 | // bail if we did not get an HTTP 200 (Connection Established) response | |
396 | if (rep.sline.status() != Http::scOkay) { | |
397 | server.logicError("unsupported CONNECT response status code"); | |
398 | return; | |
399 | } | |
400 | ||
401 | if (rep.hdr_sz < connectRespBuf->contentSize()) { | |
402 | // preserve bytes that the server already sent after the CONNECT response | |
403 | server.len = connectRespBuf->contentSize() - rep.hdr_sz; | |
404 | memcpy(server.buf, connectRespBuf->content()+rep.hdr_sz, server.len); | |
405 | } else { | |
406 | // reset; delay pools were using this field to throttle CONNECT response | |
407 | server.len = 0; | |
408 | } | |
409 | ||
410 | delete connectRespBuf; | |
411 | connectRespBuf = NULL; | |
412 | connectExchangeCheckpoint(); | |
413 | } | |
414 | ||
415 | void | |
416 | TunnelStateData::Connection::logicError(const char *errMsg) | |
417 | { | |
418 | debugs(50, 3, conn << " closing on error: " << errMsg); | |
419 | conn->close(); | |
a46d2c0e | 420 | } |
421 | ||
422 | void | |
fa34dd97 | 423 | TunnelStateData::Connection::error(int const xerrno) |
a46d2c0e | 424 | { |
425 | /* XXX fixme xstrerror and xerrno... */ | |
426 | errno = xerrno; | |
427 | ||
e0d28505 | 428 | debugs(50, debugLevelForError(xerrno), HERE << conn << ": read/write failure: " << xstrerror()); |
62e76326 | 429 | |
a46d2c0e | 430 | if (!ignoreErrno(xerrno)) |
fb046c1b | 431 | conn->close(); |
983061ed | 432 | } |
433 | ||
434 | /* Read from client side and queue it for writing to the server */ | |
a46d2c0e | 435 | void |
e0d28505 | 436 | TunnelStateData::ReadClient(const Comm::ConnectionPointer &, char *buf, size_t len, comm_err_t errcode, int xerrno, void *data) |
983061ed | 437 | { |
fa34dd97 | 438 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 439 | assert (cbdataReferenceValid (tunnelState)); |
62e76326 | 440 | |
11007d4b | 441 | tunnelState->readClient(buf, len, errcode, xerrno); |
a46d2c0e | 442 | } |
62e76326 | 443 | |
a46d2c0e | 444 | void |
fa34dd97 | 445 | TunnelStateData::readClient(char *buf, size_t len, comm_err_t errcode, int xerrno) |
a46d2c0e | 446 | { |
fd54d9b2 | 447 | debugs(26, 3, HERE << client.conn << ", read " << len << " bytes, err=" << errcode); |
d01053a2 | 448 | |
a46d2c0e | 449 | /* |
450 | * Bail out early on COMM_ERR_CLOSING | |
26ac0430 | 451 | * - close handlers will tidy up for us |
a46d2c0e | 452 | */ |
a55f4cea | 453 | |
a46d2c0e | 454 | if (errcode == COMM_ERR_CLOSING) |
455 | return; | |
a55f4cea | 456 | |
a46d2c0e | 457 | if (len > 0) { |
458 | client.bytesIn(len); | |
e4f1fdae | 459 | kb_incr(&(statCounter.client_http.kbytes_in), len); |
a46d2c0e | 460 | } |
62e76326 | 461 | |
3ed5793b AR |
462 | if (keepGoingAfterRead(len, errcode, xerrno, client, server)) |
463 | copy(len, client, server, WriteServerDone); | |
a46d2c0e | 464 | } |
62e76326 | 465 | |
3ed5793b AR |
466 | /// Updates state after reading from client or server. |
467 | /// Returns whether the caller should use the data just read. | |
468 | bool | |
469 | TunnelStateData::keepGoingAfterRead(size_t len, comm_err_t errcode, int xerrno, Connection &from, Connection &to) | |
a46d2c0e | 470 | { |
fd54d9b2 AJ |
471 | debugs(26, 3, HERE << "from={" << from.conn << "}, to={" << to.conn << "}"); |
472 | ||
a46d2c0e | 473 | /* I think this is to prevent free-while-in-a-callback behaviour |
26ac0430 | 474 | * - RBC 20030229 |
fb046c1b | 475 | * from.conn->close() / to.conn->close() done here trigger close callbacks which may free TunnelStateData |
a46d2c0e | 476 | */ |
83564ee7 | 477 | const CbcPointer<TunnelStateData> safetyLock(this); |
62e76326 | 478 | |
60dafd5e | 479 | /* Bump the source connection read timeout on any activity */ |
8d77a37c AJ |
480 | if (Comm::IsConnOpen(from.conn)) { |
481 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", | |
dc49061a | 482 | CommTimeoutCbPtrFun(tunnelTimeout, this)); |
8d77a37c AJ |
483 | commSetConnTimeout(from.conn, Config.Timeout.read, timeoutCall); |
484 | } | |
99c02c10 | 485 | |
2677c3b1 JPM |
486 | /* Bump the dest connection read timeout on any activity */ |
487 | /* see Bug 3659: tunnels can be weird, with very long one-way transfers */ | |
488 | if (Comm::IsConnOpen(to.conn)) { | |
489 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", | |
490 | CommTimeoutCbPtrFun(tunnelTimeout, this)); | |
491 | commSetConnTimeout(to.conn, Config.Timeout.read, timeoutCall); | |
492 | } | |
493 | ||
58c0b17d | 494 | if (errcode) |
a46d2c0e | 495 | from.error (xerrno); |
60dafd5e | 496 | else if (len == 0 || !Comm::IsConnOpen(to.conn)) { |
fd54d9b2 | 497 | debugs(26, 3, HERE << "Nothing to write or client gone. Terminate the tunnel."); |
fb046c1b | 498 | from.conn->close(); |
62e76326 | 499 | |
60dafd5e | 500 | /* Only close the remote end if we've finished queueing data to it */ |
97c81191 | 501 | if (from.len == 0 && Comm::IsConnOpen(to.conn) ) { |
fb046c1b | 502 | to.conn->close(); |
c4b7a5a9 | 503 | } |
ec41b64c | 504 | } else if (cbdataReferenceValid(this)) { |
3ed5793b AR |
505 | return true; |
506 | } | |
507 | ||
508 | return false; | |
509 | } | |
510 | ||
511 | void | |
512 | TunnelStateData::copy(size_t len, Connection &from, Connection &to, IOCB *completion) | |
513 | { | |
1c8fc082 A |
514 | debugs(26, 3, HERE << "Schedule Write"); |
515 | AsyncCall::Pointer call = commCbCall(5,5, "TunnelBlindCopyWriteHandler", | |
516 | CommIoCbPtrFun(completion, this)); | |
517 | Comm::Write(to.conn, from.buf, len, call, NULL); | |
983061ed | 518 | } |
519 | ||
520 | /* Writes data from the client buffer to the server side */ | |
a46d2c0e | 521 | void |
e0d28505 | 522 | TunnelStateData::WriteServerDone(const Comm::ConnectionPointer &, char *buf, size_t len, comm_err_t flag, int xerrno, void *data) |
983061ed | 523 | { |
fa34dd97 | 524 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 525 | assert (cbdataReferenceValid (tunnelState)); |
a46d2c0e | 526 | |
11007d4b | 527 | tunnelState->writeServerDone(buf, len, flag, xerrno); |
a46d2c0e | 528 | } |
a55f4cea | 529 | |
a46d2c0e | 530 | void |
fa34dd97 | 531 | TunnelStateData::writeServerDone(char *buf, size_t len, comm_err_t flag, int xerrno) |
a46d2c0e | 532 | { |
fd54d9b2 | 533 | debugs(26, 3, HERE << server.conn << ", " << len << " bytes written, flag=" << flag); |
62e76326 | 534 | |
5dacdf3f | 535 | /* Error? */ |
58c0b17d | 536 | if (flag != COMM_OK) { |
fd54d9b2 AJ |
537 | if (flag != COMM_ERR_CLOSING) { |
538 | debugs(26, 4, HERE << "calling TunnelStateData::server.error(" << xerrno <<")"); | |
58c0b17d | 539 | server.error(xerrno); // may call comm_close |
fd54d9b2 | 540 | } |
5dacdf3f | 541 | return; |
c4b7a5a9 | 542 | } |
62e76326 | 543 | |
5dacdf3f | 544 | /* EOF? */ |
a46d2c0e | 545 | if (len == 0) { |
fd54d9b2 | 546 | debugs(26, 4, HERE << "No read input. Closing server connection."); |
fb046c1b | 547 | server.conn->close(); |
a46d2c0e | 548 | return; |
549 | } | |
62e76326 | 550 | |
5dacdf3f | 551 | /* Valid data */ |
e4f1fdae FC |
552 | kb_incr(&(statCounter.server.all.kbytes_out), len); |
553 | kb_incr(&(statCounter.server.other.kbytes_out), len); | |
5dacdf3f | 554 | client.dataSent(len); |
555 | ||
a46d2c0e | 556 | /* If the other end has closed, so should we */ |
97c81191 | 557 | if (!Comm::IsConnOpen(client.conn)) { |
fd54d9b2 | 558 | debugs(26, 4, HERE << "Client gone away. Shutting down server connection."); |
fb046c1b | 559 | server.conn->close(); |
a55f4cea | 560 | return; |
c4b7a5a9 | 561 | } |
62e76326 | 562 | |
83564ee7 | 563 | const CbcPointer<TunnelStateData> safetyLock(this); /* ??? should be locked by the caller... */ |
a46d2c0e | 564 | |
5dacdf3f | 565 | if (cbdataReferenceValid(this)) |
a46d2c0e | 566 | copyRead(client, ReadClient); |
983061ed | 567 | } |
568 | ||
569 | /* Writes data from the server buffer to the client side */ | |
a46d2c0e | 570 | void |
e0d28505 | 571 | TunnelStateData::WriteClientDone(const Comm::ConnectionPointer &, char *buf, size_t len, comm_err_t flag, int xerrno, void *data) |
983061ed | 572 | { |
fa34dd97 | 573 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 574 | assert (cbdataReferenceValid (tunnelState)); |
a46d2c0e | 575 | |
11007d4b | 576 | tunnelState->writeClientDone(buf, len, flag, xerrno); |
a46d2c0e | 577 | } |
578 | ||
579 | void | |
e0d28505 | 580 | TunnelStateData::Connection::dataSent(size_t amount) |
a46d2c0e | 581 | { |
fd54d9b2 | 582 | debugs(26, 3, HERE << "len=" << len << " - amount=" << amount); |
a46d2c0e | 583 | assert(amount == (size_t)len); |
584 | len =0; | |
585 | /* increment total object size */ | |
586 | ||
587 | if (size_ptr) | |
588 | *size_ptr += amount; | |
589 | } | |
590 | ||
591 | void | |
fa34dd97 | 592 | TunnelStateData::writeClientDone(char *buf, size_t len, comm_err_t flag, int xerrno) |
a46d2c0e | 593 | { |
fd54d9b2 | 594 | debugs(26, 3, HERE << client.conn << ", " << len << " bytes written, flag=" << flag); |
62e76326 | 595 | |
5dacdf3f | 596 | /* Error? */ |
58c0b17d | 597 | if (flag != COMM_OK) { |
fd54d9b2 AJ |
598 | if (flag != COMM_ERR_CLOSING) { |
599 | debugs(26, 4, HERE << "Closing client connection due to comm flags."); | |
58c0b17d | 600 | client.error(xerrno); // may call comm_close |
fd54d9b2 | 601 | } |
5dacdf3f | 602 | return; |
c4b7a5a9 | 603 | } |
62e76326 | 604 | |
5dacdf3f | 605 | /* EOF? */ |
a46d2c0e | 606 | if (len == 0) { |
fd54d9b2 | 607 | debugs(26, 4, HERE << "Closing client connection due to 0 byte read."); |
fb046c1b | 608 | client.conn->close(); |
62e76326 | 609 | return; |
983061ed | 610 | } |
62e76326 | 611 | |
5dacdf3f | 612 | /* Valid data */ |
e4f1fdae | 613 | kb_incr(&(statCounter.client_http.kbytes_out), len); |
5dacdf3f | 614 | server.dataSent(len); |
615 | ||
a46d2c0e | 616 | /* If the other end has closed, so should we */ |
97c81191 | 617 | if (!Comm::IsConnOpen(server.conn)) { |
fd54d9b2 | 618 | debugs(26, 4, HERE << "Server has gone away. Terminating client connection."); |
fb046c1b | 619 | client.conn->close(); |
a55f4cea | 620 | return; |
983061ed | 621 | } |
62e76326 | 622 | |
83564ee7 | 623 | CbcPointer<TunnelStateData> safetyLock(this); /* ??? should be locked by the caller... */ |
a55f4cea | 624 | |
5dacdf3f | 625 | if (cbdataReferenceValid(this)) |
a46d2c0e | 626 | copyRead(server, ReadServer); |
983061ed | 627 | } |
628 | ||
b8d8561b | 629 | static void |
8d77a37c | 630 | tunnelTimeout(const CommTimeoutCbParams &io) |
983061ed | 631 | { |
8d77a37c AJ |
632 | TunnelStateData *tunnelState = static_cast<TunnelStateData *>(io.data); |
633 | debugs(26, 3, HERE << io.conn); | |
11007d4b | 634 | /* Temporary lock to protect our own feets (comm_close -> tunnelClientClosed -> Free) */ |
83564ee7 | 635 | CbcPointer<TunnelStateData> safetyLock(tunnelState); |
a55f4cea | 636 | |
11007d4b | 637 | tunnelState->client.closeIfOpen(); |
638 | tunnelState->server.closeIfOpen(); | |
a46d2c0e | 639 | } |
62e76326 | 640 | |
a46d2c0e | 641 | void |
fa34dd97 | 642 | TunnelStateData::Connection::closeIfOpen() |
a46d2c0e | 643 | { |
97c81191 | 644 | if (Comm::IsConnOpen(conn)) |
fb046c1b | 645 | conn->close(); |
a46d2c0e | 646 | } |
647 | ||
648 | void | |
fa34dd97 | 649 | TunnelStateData::copyRead(Connection &from, IOCB *completion) |
a46d2c0e | 650 | { |
651 | assert(from.len == 0); | |
fd54d9b2 | 652 | AsyncCall::Pointer call = commCbCall(5,4, "TunnelBlindCopyReadHandler", |
abd8f140 | 653 | CommIoCbPtrFun(completion, this)); |
8a467c4b | 654 | comm_read(from.conn, from.buf, from.bytesWanted(1, SQUID_TCP_SO_RCVBUF), call); |
983061ed | 655 | } |
656 | ||
3ed5793b AR |
657 | void |
658 | TunnelStateData::readConnectResponse() | |
659 | { | |
660 | assert(waitingForConnectResponse()); | |
661 | ||
662 | AsyncCall::Pointer call = commCbCall(5,4, "readConnectResponseDone", | |
663 | CommIoCbPtrFun(ReadConnectResponseDone, this)); | |
664 | comm_read(server.conn, connectRespBuf->space(), | |
665 | server.bytesWanted(1, connectRespBuf->spaceSize()), call); | |
666 | } | |
667 | ||
379e8c1c | 668 | /** |
87f237a9 | 669 | * Set the HTTP status for this request and sets the read handlers for client |
379e8c1c AR |
670 | * and server side connections. |
671 | */ | |
672 | static void | |
673 | tunnelStartShoveling(TunnelStateData *tunnelState) | |
674 | { | |
3ed5793b | 675 | assert(!tunnelState->waitingForConnectExchange()); |
955394ce | 676 | *tunnelState->status_ptr = Http::scOkay; |
379e8c1c | 677 | if (cbdataReferenceValid(tunnelState)) { |
3ed5793b | 678 | if (!tunnelState->server.len) |
1c8fc082 | 679 | tunnelState->copyRead(tunnelState->server, TunnelStateData::ReadServer); |
3ed5793b AR |
680 | else |
681 | tunnelState->copy(tunnelState->server.len, tunnelState->server, tunnelState->client, TunnelStateData::WriteClientDone); | |
379e8c1c AR |
682 | tunnelState->copyRead(tunnelState->client, TunnelStateData::ReadClient); |
683 | } | |
684 | } | |
685 | ||
b0388924 AJ |
686 | /** |
687 | * All the pieces we need to write to client and/or server connection | |
1b76e6c1 | 688 | * have been written. |
379e8c1c | 689 | * Call the tunnelStartShoveling to start the blind pump. |
b0388924 | 690 | */ |
c4b7a5a9 | 691 | static void |
e0d28505 | 692 | tunnelConnectedWriteDone(const Comm::ConnectionPointer &conn, char *buf, size_t size, comm_err_t flag, int xerrno, void *data) |
c4b7a5a9 | 693 | { |
fa34dd97 | 694 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
fd54d9b2 | 695 | debugs(26, 3, HERE << conn << ", flag=" << flag); |
62e76326 | 696 | |
697 | if (flag != COMM_OK) { | |
955394ce | 698 | *tunnelState->status_ptr = Http::scInternalServerError; |
e0d28505 | 699 | tunnelErrorComplete(conn->fd, data, 0); |
62e76326 | 700 | return; |
701 | } | |
702 | ||
379e8c1c | 703 | tunnelStartShoveling(tunnelState); |
c4b7a5a9 | 704 | } |
705 | ||
3ed5793b AR |
706 | /// Called when we are done writing CONNECT request to a peer. |
707 | static void | |
708 | tunnelConnectReqWriteDone(const Comm::ConnectionPointer &conn, char *buf, size_t size, comm_err_t flag, int xerrno, void *data) | |
709 | { | |
710 | TunnelStateData *tunnelState = (TunnelStateData *)data; | |
711 | debugs(26, 3, conn << ", flag=" << flag); | |
712 | assert(tunnelState->waitingForConnectRequest()); | |
713 | ||
714 | if (flag != COMM_OK) { | |
715 | *tunnelState->status_ptr = Http::scInternalServerError; | |
716 | tunnelErrorComplete(conn->fd, data, 0); | |
717 | return; | |
718 | } | |
719 | ||
720 | tunnelState->connectReqWriting = false; | |
721 | tunnelState->connectExchangeCheckpoint(); | |
722 | } | |
723 | ||
724 | void | |
725 | TunnelStateData::connectExchangeCheckpoint() | |
726 | { | |
727 | if (waitingForConnectResponse()) { | |
728 | debugs(26, 5, "still reading CONNECT response on " << server.conn); | |
729 | } else if (waitingForConnectRequest()) { | |
730 | debugs(26, 5, "still writing CONNECT request on " << server.conn); | |
731 | } else { | |
732 | assert(!waitingForConnectExchange()); | |
733 | debugs(26, 3, "done with CONNECT exchange on " << server.conn); | |
734 | tunnelConnected(server.conn, this); | |
735 | } | |
736 | } | |
737 | ||
c4b7a5a9 | 738 | /* |
1b76e6c1 | 739 | * handle the write completion from a proxy request to an upstream origin |
c4b7a5a9 | 740 | */ |
b8d8561b | 741 | static void |
f01d4b80 | 742 | tunnelConnected(const Comm::ConnectionPointer &server, void *data) |
983061ed | 743 | { |
fa34dd97 | 744 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
f01d4b80 | 745 | debugs(26, 3, HERE << server << ", tunnelState=" << tunnelState); |
379e8c1c | 746 | |
3ed5793b | 747 | if (!tunnelState->clientExpectsConnectResponse()) |
379e8c1c AR |
748 | tunnelStartShoveling(tunnelState); // ssl-bumped connection, be quiet |
749 | else { | |
750 | AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone", | |
751 | CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState)); | |
752 | Comm::Write(tunnelState->client.conn, conn_established, strlen(conn_established), call, NULL); | |
753 | } | |
983061ed | 754 | } |
755 | ||
b8d8561b | 756 | static void |
fd54d9b2 | 757 | tunnelErrorComplete(int fd/*const Comm::ConnectionPointer &*/, void *data, size_t) |
30a4f2a8 | 758 | { |
fa34dd97 | 759 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
fd54d9b2 | 760 | debugs(26, 3, HERE << "FD " << fd); |
11007d4b | 761 | assert(tunnelState != NULL); |
762 | /* temporary lock to save our own feets (comm_close -> tunnelClientClosed -> Free) */ | |
83564ee7 | 763 | CbcPointer<TunnelStateData> safetyLock(tunnelState); |
62e76326 | 764 | |
97c81191 | 765 | if (Comm::IsConnOpen(tunnelState->client.conn)) |
fb046c1b | 766 | tunnelState->client.conn->close(); |
62e76326 | 767 | |
97c81191 | 768 | if (Comm::IsConnOpen(tunnelState->server.conn)) |
fb046c1b | 769 | tunnelState->server.conn->close(); |
30a4f2a8 | 770 | } |
771 | ||
b8d8561b | 772 | static void |
f01d4b80 | 773 | tunnelConnectDone(const Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data) |
983061ed | 774 | { |
fa34dd97 | 775 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
cfd66529 AJ |
776 | |
777 | if (status != COMM_OK) { | |
fd54d9b2 | 778 | debugs(26, 4, HERE << conn << ", comm failure recovery."); |
aed188fd AJ |
779 | /* At this point only the TCP handshake has failed. no data has been passed. |
780 | * we are allowed to re-try the TCP-level connection to alternate IPs for CONNECT. | |
781 | */ | |
00ae51e4 AJ |
782 | tunnelState->serverDestinations.shift(); |
783 | if (status != COMM_TIMEOUT && tunnelState->serverDestinations.size() > 0) { | |
aed188fd | 784 | /* Try another IP of this destination host */ |
6dc6127b SW |
785 | |
786 | if (Ip::Qos::TheConfig.isAclTosActive()) { | |
8a70cdbb | 787 | tunnelState->serverDestinations[0]->tos = GetTosToServer(tunnelState->request.getRaw()); |
6dc6127b SW |
788 | } |
789 | ||
790 | #if SO_MARK && USE_LIBCAP | |
8a70cdbb | 791 | tunnelState->serverDestinations[0]->nfmark = GetNfmarkToServer(tunnelState->request.getRaw()); |
6dc6127b SW |
792 | #endif |
793 | ||
fd54d9b2 | 794 | debugs(26, 4, HERE << "retry with : " << tunnelState->serverDestinations[0]); |
aed188fd | 795 | AsyncCall::Pointer call = commCbCall(26,3, "tunnelConnectDone", CommConnectCbPtrFun(tunnelConnectDone, tunnelState)); |
00ae51e4 | 796 | Comm::ConnOpener *cs = new Comm::ConnOpener(tunnelState->serverDestinations[0], call, Config.Timeout.connect); |
aed188fd | 797 | cs->setHost(tunnelState->url); |
855150a4 | 798 | AsyncJob::Start(cs); |
aed188fd | 799 | } else { |
fd54d9b2 | 800 | debugs(26, 4, HERE << "terminate with error."); |
8a70cdbb | 801 | ErrorState *err = new ErrorState(ERR_CONNECT_FAIL, Http::scServiceUnavailable, tunnelState->request.getRaw()); |
955394ce | 802 | *tunnelState->status_ptr = Http::scServiceUnavailable; |
aed188fd AJ |
803 | err->xerrno = xerrno; |
804 | // on timeout is this still: err->xerrno = ETIMEDOUT; | |
4dd643d5 | 805 | err->port = conn->remote.port(); |
aed188fd AJ |
806 | err->callback = tunnelErrorComplete; |
807 | err->callback_data = tunnelState; | |
e0d28505 | 808 | errorSend(tunnelState->client.conn, err); |
aed188fd | 809 | } |
cfd66529 AJ |
810 | return; |
811 | } | |
812 | ||
4beb4bab AJ |
813 | #if USE_DELAY_POOLS |
814 | /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */ | |
815 | if (conn->getPeer() && conn->getPeer()->options.no_delay) | |
816 | tunnelState->server.setDelayId(DelayId()); | |
817 | #endif | |
818 | ||
a14f38d0 | 819 | tunnelState->request->hier.note(conn, tunnelState->getHost()); |
4beb4bab | 820 | |
fb046c1b | 821 | tunnelState->server.conn = conn; |
4beb4bab | 822 | tunnelState->request->peer_host = conn->getPeer() ? conn->getPeer()->host : NULL; |
fb046c1b | 823 | comm_add_close_handler(conn->fd, tunnelServerClosed, tunnelState); |
cfd66529 | 824 | |
fd54d9b2 | 825 | debugs(26, 4, HERE << "determine post-connect handling pathway."); |
739b352a AJ |
826 | if (conn->getPeer()) { |
827 | tunnelState->request->peer_login = conn->getPeer()->login; | |
e857372a | 828 | tunnelState->request->flags.proxying = !(conn->getPeer()->options.originserver); |
fe40a877 | 829 | } else { |
cfd66529 | 830 | tunnelState->request->peer_login = NULL; |
e857372a | 831 | tunnelState->request->flags.proxying = false; |
cfd66529 | 832 | } |
62e76326 | 833 | |
45e5102d | 834 | if (tunnelState->request->flags.proxying) |
fb046c1b | 835 | tunnelRelayConnectRequest(conn, tunnelState); |
cfd66529 | 836 | else { |
fb046c1b | 837 | tunnelConnected(conn, tunnelState); |
983061ed | 838 | } |
cfd66529 | 839 | |
8d77a37c | 840 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", |
dc49061a | 841 | CommTimeoutCbPtrFun(tunnelTimeout, tunnelState)); |
8d77a37c | 842 | commSetConnTimeout(conn, Config.Timeout.read, timeoutCall); |
983061ed | 843 | } |
30a4f2a8 | 844 | |
82afb125 FC |
845 | tos_t GetTosToServer(HttpRequest * request); |
846 | nfmark_t GetNfmarkToServer(HttpRequest * request); | |
425de4c8 | 847 | |
770f051d | 848 | void |
06521a10 | 849 | tunnelStart(ClientHttpRequest * http, int64_t * size_ptr, int *status_ptr, const AccessLogEntryPointer &al) |
30a4f2a8 | 850 | { |
fd54d9b2 | 851 | debugs(26, 3, HERE); |
30a4f2a8 | 852 | /* Create state structure. */ |
fa34dd97 | 853 | TunnelStateData *tunnelState = NULL; |
9b312a19 | 854 | ErrorState *err = NULL; |
190154cf | 855 | HttpRequest *request = http->request; |
d5964d58 | 856 | char *url = http->uri; |
fb046c1b | 857 | |
f1003989 | 858 | /* |
4dd643d5 | 859 | * client_addr.isNoAddr() indicates this is an "internal" request |
a4b8110e | 860 | * from peer_digest.c, asn.c, netdb.c, etc and should always |
861 | * be allowed. yuck, I know. | |
862 | */ | |
62e76326 | 863 | |
4dd643d5 | 864 | if (Config.accessList.miss && !request->client_addr.isNoAddr()) { |
62e76326 | 865 | /* |
866 | * Check if this host is allowed to fetch MISSES from us (miss_access) | |
b50e327b | 867 | * default is to allow. |
62e76326 | 868 | */ |
c0941a6a | 869 | ACLFilledChecklist ch(Config.accessList.miss, request, NULL); |
62e76326 | 870 | ch.src_addr = request->client_addr; |
871 | ch.my_addr = request->my_addr; | |
2efeb0b7 | 872 | if (ch.fastCheck() == ACCESS_DENIED) { |
fd54d9b2 | 873 | debugs(26, 4, HERE << "MISS access forbidden."); |
955394ce AJ |
874 | err = new ErrorState(ERR_FORWARDING_DENIED, Http::scForbidden, request); |
875 | *status_ptr = Http::scForbidden; | |
73c36fd9 | 876 | errorSend(http->getConn()->clientConnection, err); |
62e76326 | 877 | return; |
878 | } | |
f1003989 | 879 | } |
62e76326 | 880 | |
c9fd01b4 | 881 | debugs(26, 3, HERE << "'" << RequestMethodStr(request->method) << " " << url << " " << request->http_ver << "'"); |
5db6bf73 FC |
882 | ++statCounter.server.all.requests; |
883 | ++statCounter.server.other.requests; | |
62e76326 | 884 | |
fa34dd97 | 885 | tunnelState = new TunnelStateData; |
9a0a18de | 886 | #if USE_DELAY_POOLS |
11007d4b | 887 | tunnelState->server.setDelayId(DelayId::DelayClient(http)); |
59715b38 | 888 | #endif |
11007d4b | 889 | tunnelState->url = xstrdup(url); |
b248c2a3 | 890 | tunnelState->request = request; |
11007d4b | 891 | tunnelState->server.size_ptr = size_ptr; |
892 | tunnelState->status_ptr = status_ptr; | |
73c36fd9 | 893 | tunnelState->client.conn = http->getConn()->clientConnection; |
06521a10 | 894 | tunnelState->al = al; |
fb046c1b AJ |
895 | |
896 | comm_add_close_handler(tunnelState->client.conn->fd, | |
11007d4b | 897 | tunnelClientClosed, |
898 | tunnelState); | |
8d77a37c AJ |
899 | |
900 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", | |
dc49061a | 901 | CommTimeoutCbPtrFun(tunnelTimeout, tunnelState)); |
8d77a37c | 902 | commSetConnTimeout(tunnelState->client.conn, Config.Timeout.lifetime, timeoutCall); |
cfd66529 | 903 | |
00ae51e4 | 904 | peerSelect(&(tunnelState->serverDestinations), request, |
62e76326 | 905 | NULL, |
11007d4b | 906 | tunnelPeerSelectComplete, |
907 | tunnelState); | |
30a4f2a8 | 908 | } |
98ffb7e4 | 909 | |
b8d8561b | 910 | static void |
b0388924 | 911 | tunnelRelayConnectRequest(const Comm::ConnectionPointer &srv, void *data) |
98ffb7e4 | 912 | { |
fa34dd97 | 913 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
3ed5793b | 914 | assert(!tunnelState->waitingForConnectExchange()); |
75faaa7a | 915 | HttpHeader hdr_out(hoRequest); |
e1e72f06 | 916 | Packer p; |
46f4b111 | 917 | HttpStateFlags flags; |
b0388924 | 918 | debugs(26, 3, HERE << srv << ", tunnelState=" << tunnelState); |
b4b5fd95 | 919 | memset(&flags, '\0', sizeof(flags)); |
45e5102d | 920 | flags.proxying = tunnelState->request->flags.proxying; |
032785bf | 921 | MemBuf mb; |
2fe7eff9 | 922 | mb.init(); |
3872be7c | 923 | mb.Printf("CONNECT %s HTTP/1.1\r\n", tunnelState->url); |
8a70cdbb | 924 | HttpStateData::httpBuildRequestHeader(tunnelState->request.getRaw(), |
e5ee81f0 | 925 | NULL, /* StoreEntry */ |
06521a10 | 926 | tunnelState->al, /* AccessLogEntry */ |
e5ee81f0 | 927 | &hdr_out, |
928 | flags); /* flags */ | |
e1e72f06 | 929 | packerToMemInit(&p, &mb); |
a9925b40 | 930 | hdr_out.packInto(&p); |
519e0948 | 931 | hdr_out.clean(); |
e1e72f06 | 932 | packerClean(&p); |
2fe7eff9 | 933 | mb.append("\r\n", 2); |
c4b7a5a9 | 934 | |
3ed5793b | 935 | if (tunnelState->clientExpectsConnectResponse()) { |
1c8fc082 A |
936 | // hack: blindly tunnel peer response (to our CONNECT request) to the client as ours. |
937 | AsyncCall::Pointer writeCall = commCbCall(5,5, "tunnelConnectedWriteDone", | |
938 | CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState)); | |
939 | Comm::Write(srv, &mb, writeCall); | |
3ed5793b AR |
940 | } else { |
941 | // we have to eat the connect response from the peer (so that the client | |
942 | // does not see it) and only then start shoveling data to the client | |
943 | AsyncCall::Pointer writeCall = commCbCall(5,5, "tunnelConnectReqWriteDone", | |
944 | CommIoCbPtrFun(tunnelConnectReqWriteDone, | |
1c8fc082 | 945 | tunnelState)); |
3ed5793b AR |
946 | Comm::Write(srv, &mb, writeCall); |
947 | tunnelState->connectReqWriting = true; | |
948 | ||
949 | tunnelState->connectRespBuf = new MemBuf; | |
950 | // SQUID_TCP_SO_RCVBUF: we should not accumulate more than regular I/O buffer | |
951 | // can hold since any CONNECT response leftovers have to fit into server.buf. | |
952 | // 2*SQUID_TCP_SO_RCVBUF: HttpMsg::parse() zero-terminates, which uses space. | |
953 | tunnelState->connectRespBuf->init(SQUID_TCP_SO_RCVBUF, 2*SQUID_TCP_SO_RCVBUF); | |
954 | tunnelState->readConnectResponse(); | |
955 | ||
956 | assert(tunnelState->waitingForConnectExchange()); | |
957 | } | |
8d77a37c AJ |
958 | |
959 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", | |
dc49061a | 960 | CommTimeoutCbPtrFun(tunnelTimeout, tunnelState)); |
8d77a37c | 961 | commSetConnTimeout(srv, Config.Timeout.read, timeoutCall); |
98ffb7e4 | 962 | } |
33ea9fff | 963 | |
33ea9fff | 964 | static void |
a37fdd8a | 965 | tunnelPeerSelectComplete(Comm::ConnectionList *peer_paths, ErrorState *err, void *data) |
33ea9fff | 966 | { |
fa34dd97 | 967 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
62e76326 | 968 | |
cfd66529 | 969 | if (peer_paths == NULL || peer_paths->size() < 1) { |
fd54d9b2 | 970 | debugs(26, 3, HERE << "No paths found. Aborting CONNECT"); |
a37fdd8a | 971 | if (!err) { |
8a70cdbb | 972 | err = new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, tunnelState->request.getRaw()); |
a37fdd8a AJ |
973 | } |
974 | *tunnelState->status_ptr = err->httpStatus; | |
11007d4b | 975 | err->callback = tunnelErrorComplete; |
976 | err->callback_data = tunnelState; | |
e0d28505 | 977 | errorSend(tunnelState->client.conn, err); |
62e76326 | 978 | return; |
db1cd23c | 979 | } |
a37fdd8a AJ |
980 | delete err; |
981 | ||
6dc6127b | 982 | if (Ip::Qos::TheConfig.isAclTosActive()) { |
8a70cdbb | 983 | tunnelState->serverDestinations[0]->tos = GetTosToServer(tunnelState->request.getRaw()); |
6dc6127b SW |
984 | } |
985 | ||
986 | #if SO_MARK && USE_LIBCAP | |
8a70cdbb | 987 | tunnelState->serverDestinations[0]->nfmark = GetNfmarkToServer(tunnelState->request.getRaw()); |
6dc6127b SW |
988 | #endif |
989 | ||
fd54d9b2 AJ |
990 | debugs(26, 3, HERE << "paths=" << peer_paths->size() << ", p[0]={" << (*peer_paths)[0] << "}, serverDest[0]={" << |
991 | tunnelState->serverDestinations[0] << "}"); | |
62e76326 | 992 | |
cfd66529 | 993 | AsyncCall::Pointer call = commCbCall(26,3, "tunnelConnectDone", CommConnectCbPtrFun(tunnelConnectDone, tunnelState)); |
00ae51e4 | 994 | Comm::ConnOpener *cs = new Comm::ConnOpener(tunnelState->serverDestinations[0], call, Config.Timeout.connect); |
aed188fd | 995 | cs->setHost(tunnelState->url); |
855150a4 | 996 | AsyncJob::Start(cs); |
33ea9fff | 997 | } |
a46d2c0e | 998 | |
fa34dd97 | 999 | CBDATA_CLASS_INIT(TunnelStateData); |
a46d2c0e | 1000 | |
a46d2c0e | 1001 | bool |
fa34dd97 | 1002 | TunnelStateData::noConnections() const |
a46d2c0e | 1003 | { |
97c81191 | 1004 | return !Comm::IsConnOpen(server.conn) && !Comm::IsConnOpen(client.conn); |
a46d2c0e | 1005 | } |
1006 | ||
9a0a18de | 1007 | #if USE_DELAY_POOLS |
a46d2c0e | 1008 | void |
fa34dd97 | 1009 | TunnelStateData::Connection::setDelayId(DelayId const &newDelay) |
a46d2c0e | 1010 | { |
1011 | delayId = newDelay; | |
1012 | } | |
1013 | ||
1014 | #endif |