]>
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" |
cfd66529 | 36 | #include "Array.h" |
a011edee FC |
37 | #include "CachePeer.h" |
38 | #include "client_side_request.h" | |
39 | #include "client_side.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 FC |
47 | #include "HttpRequest.h" |
48 | #include "MemBuf.h" | |
cfd66529 | 49 | #include "PeerSelectState.h" |
4d5904f7 | 50 | #include "SquidConfig.h" |
e4f1fdae | 51 | #include "StatCounters.h" |
4e540555 | 52 | #include "tools.h" |
582c2af2 FC |
53 | #if USE_DELAY_POOLS |
54 | #include "DelayId.h" | |
55 | #endif | |
56 | ||
57 | #if HAVE_LIMITS_H | |
58 | #include <limits.h> | |
59 | #endif | |
21d845b1 FC |
60 | #if HAVE_ERRNO_H |
61 | #include <errno.h> | |
62 | #endif | |
582c2af2 | 63 | |
fa34dd97 | 64 | class TunnelStateData |
62e76326 | 65 | { |
a46d2c0e | 66 | |
67 | public: | |
68 | ||
69 | class Connection; | |
70 | void *operator new(size_t); | |
71 | void operator delete (void *); | |
e0d28505 AJ |
72 | static void ReadClient(const Comm::ConnectionPointer &, char *buf, size_t len, comm_err_t errcode, int xerrno, void *data); |
73 | static void ReadServer(const Comm::ConnectionPointer &, char *buf, size_t len, comm_err_t errcode, int xerrno, void *data); | |
74 | static void WriteClientDone(const Comm::ConnectionPointer &, char *buf, size_t len, comm_err_t flag, int xerrno, void *data); | |
75 | static void WriteServerDone(const Comm::ConnectionPointer &, char *buf, size_t len, comm_err_t flag, int xerrno, void *data); | |
a46d2c0e | 76 | |
77 | bool noConnections() const; | |
983061ed | 78 | char *url; |
190154cf | 79 | HttpRequest *request; |
00ae51e4 | 80 | Comm::ConnectionList serverDestinations; |
62e76326 | 81 | |
fb046c1b AJ |
82 | const char * getHost() const { |
83 | return (server.conn != NULL && server.conn->getPeer() ? server.conn->getPeer()->host : request->GetHost()); | |
84 | }; | |
85 | ||
a46d2c0e | 86 | class Connection |
62e76326 | 87 | { |
a46d2c0e | 88 | |
89 | public: | |
8a467c4b | 90 | Connection() : len (0), buf ((char *)xmalloc(SQUID_TCP_SO_RCVBUF)), size_ptr(NULL) {} |
a46d2c0e | 91 | |
8a467c4b AJ |
92 | ~Connection(); |
93 | ||
94 | int bytesWanted(int lower=0, int upper = INT_MAX) const; | |
a46d2c0e | 95 | void bytesIn(int const &); |
9a0a18de | 96 | #if USE_DELAY_POOLS |
a46d2c0e | 97 | |
98 | void setDelayId(DelayId const &); | |
99 | #endif | |
100 | ||
101 | void error(int const xerrno); | |
5c926411 | 102 | int debugLevelForError(int const xerrno) const; |
a46d2c0e | 103 | void closeIfOpen(); |
8a467c4b | 104 | void dataSent (size_t amount); |
62e76326 | 105 | int len; |
8a467c4b | 106 | char *buf; |
47f6e231 | 107 | int64_t *size_ptr; /* pointer to size in an ConnStateData for logging */ |
62e76326 | 108 | |
fb046c1b | 109 | Comm::ConnectionPointer conn; ///< The currently connected connection. |
fb046c1b | 110 | |
a46d2c0e | 111 | private: |
9a0a18de | 112 | #if USE_DELAY_POOLS |
62e76326 | 113 | |
a46d2c0e | 114 | DelayId delayId; |
59715b38 | 115 | #endif |
62e76326 | 116 | |
a46d2c0e | 117 | }; |
118 | ||
119 | Connection client, server; | |
120 | int *status_ptr; /* pointer to status for logging */ | |
121 | void copyRead(Connection &from, IOCB *completion); | |
122 | ||
123 | private: | |
fa34dd97 | 124 | CBDATA_CLASS(TunnelStateData); |
8a467c4b | 125 | void copy (size_t len, comm_err_t errcode, int xerrno, Connection &from, Connection &to, IOCB *); |
a46d2c0e | 126 | void readServer(char *buf, size_t len, comm_err_t errcode, int xerrno); |
127 | void readClient(char *buf, size_t len, comm_err_t errcode, int xerrno); | |
128 | void writeClientDone(char *buf, size_t len, comm_err_t flag, int xerrno); | |
129 | void writeServerDone(char *buf, size_t len, comm_err_t flag, int xerrno); | |
130 | }; | |
983061ed | 131 | |
3c4fcf0f | 132 | static const char *const conn_established = "HTTP/1.1 200 Connection established\r\n\r\n"; |
983061ed | 133 | |
11007d4b | 134 | static CNCB tunnelConnectDone; |
135 | static ERCB tunnelErrorComplete; | |
575d05c4 AJ |
136 | static CLCB tunnelServerClosed; |
137 | static CLCB tunnelClientClosed; | |
8d77a37c | 138 | static CTCB tunnelTimeout; |
11007d4b | 139 | static PSC tunnelPeerSelectComplete; |
fa34dd97 | 140 | static void tunnelStateFree(TunnelStateData * tunnelState); |
f01d4b80 AJ |
141 | static void tunnelConnected(const Comm::ConnectionPointer &server, void *); |
142 | static void tunnelRelayConnectRequest(const Comm::ConnectionPointer &server, void *); | |
30a4f2a8 | 143 | |
b8d8561b | 144 | static void |
575d05c4 | 145 | tunnelServerClosed(const CommCloseCbParams ¶ms) |
30a4f2a8 | 146 | { |
575d05c4 AJ |
147 | TunnelStateData *tunnelState = (TunnelStateData *)params.data; |
148 | debugs(26, 3, HERE << tunnelState->server.conn); | |
fb046c1b | 149 | tunnelState->server.conn = NULL; |
62e76326 | 150 | |
71a2ced6 | 151 | if (tunnelState->noConnections()) { |
11007d4b | 152 | tunnelStateFree(tunnelState); |
26ac0430 | 153 | return; |
71a2ced6 | 154 | } |
26ac0430 | 155 | |
71a2ced6 | 156 | if (!tunnelState->server.len) { |
fb046c1b | 157 | tunnelState->client.conn->close(); |
26ac0430 | 158 | return; |
71a2ced6 | 159 | } |
30a4f2a8 | 160 | } |
161 | ||
b177367b | 162 | static void |
575d05c4 | 163 | tunnelClientClosed(const CommCloseCbParams ¶ms) |
30a4f2a8 | 164 | { |
575d05c4 AJ |
165 | TunnelStateData *tunnelState = (TunnelStateData *)params.data; |
166 | debugs(26, 3, HERE << tunnelState->client.conn); | |
fb046c1b | 167 | tunnelState->client.conn = NULL; |
62e76326 | 168 | |
71a2ced6 | 169 | if (tunnelState->noConnections()) { |
11007d4b | 170 | tunnelStateFree(tunnelState); |
26ac0430 | 171 | return; |
71a2ced6 | 172 | } |
26ac0430 | 173 | |
71a2ced6 | 174 | if (!tunnelState->client.len) { |
fb046c1b | 175 | tunnelState->server.conn->close(); |
26ac0430 | 176 | return; |
71a2ced6 | 177 | } |
30a4f2a8 | 178 | } |
983061ed | 179 | |
b177367b | 180 | static void |
fa34dd97 | 181 | tunnelStateFree(TunnelStateData * tunnelState) |
983061ed | 182 | { |
fd54d9b2 | 183 | debugs(26, 3, HERE << "tunnelState=" << tunnelState); |
11007d4b | 184 | assert(tunnelState != NULL); |
185 | assert(tunnelState->noConnections()); | |
186 | safe_free(tunnelState->url); | |
00ae51e4 | 187 | tunnelState->serverDestinations.clean(); |
11007d4b | 188 | HTTPMSGUNLOCK(tunnelState->request); |
189 | delete tunnelState; | |
983061ed | 190 | } |
191 | ||
8a467c4b AJ |
192 | TunnelStateData::Connection::~Connection() |
193 | { | |
194 | safe_free(buf); | |
195 | } | |
196 | ||
a46d2c0e | 197 | int |
8a467c4b | 198 | TunnelStateData::Connection::bytesWanted(int lowerbound, int upperbound) const |
447e176b | 199 | { |
9a0a18de | 200 | #if USE_DELAY_POOLS |
8a467c4b | 201 | return delayId.bytesWanted(lowerbound, upperbound); |
a46d2c0e | 202 | #else |
8a467c4b AJ |
203 | |
204 | return upperbound; | |
a46d2c0e | 205 | #endif |
206 | } | |
62e76326 | 207 | |
a46d2c0e | 208 | void |
fa34dd97 | 209 | TunnelStateData::Connection::bytesIn(int const &count) |
a46d2c0e | 210 | { |
fd54d9b2 | 211 | debugs(26, 3, HERE << "len=" << len << " + count=" << count); |
9a0a18de | 212 | #if USE_DELAY_POOLS |
a46d2c0e | 213 | delayId.bytesIn(count); |
214 | #endif | |
62e76326 | 215 | |
a46d2c0e | 216 | len += count; |
447e176b | 217 | } |
62e76326 | 218 | |
a46d2c0e | 219 | int |
fa34dd97 | 220 | TunnelStateData::Connection::debugLevelForError(int const xerrno) const |
a46d2c0e | 221 | { |
222 | #ifdef ECONNRESET | |
223 | ||
224 | if (xerrno == ECONNRESET) | |
225 | return 2; | |
226 | ||
447e176b | 227 | #endif |
228 | ||
a46d2c0e | 229 | if (ignoreErrno(xerrno)) |
230 | return 3; | |
231 | ||
232 | return 1; | |
233 | } | |
adb78bd4 | 234 | |
983061ed | 235 | /* Read from server side and queue it for writing to the client */ |
a46d2c0e | 236 | void |
fd54d9b2 | 237 | TunnelStateData::ReadServer(const Comm::ConnectionPointer &c, char *buf, size_t len, comm_err_t errcode, int xerrno, void *data) |
983061ed | 238 | { |
fa34dd97 | 239 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
e0d28505 | 240 | assert(cbdataReferenceValid(tunnelState)); |
fd54d9b2 | 241 | debugs(26, 3, HERE << c); |
c4b7a5a9 | 242 | |
11007d4b | 243 | tunnelState->readServer(buf, len, errcode, xerrno); |
a46d2c0e | 244 | } |
62e76326 | 245 | |
a46d2c0e | 246 | void |
fa34dd97 | 247 | TunnelStateData::readServer(char *buf, size_t len, comm_err_t errcode, int xerrno) |
a46d2c0e | 248 | { |
fd54d9b2 | 249 | debugs(26, 3, HERE << server.conn << ", read " << len << " bytes, err=" << errcode); |
d01053a2 | 250 | |
a46d2c0e | 251 | /* |
252 | * Bail out early on COMM_ERR_CLOSING | |
26ac0430 | 253 | * - close handlers will tidy up for us |
a46d2c0e | 254 | */ |
a55f4cea | 255 | |
a46d2c0e | 256 | if (errcode == COMM_ERR_CLOSING) |
257 | return; | |
62e76326 | 258 | |
ee1679df | 259 | if (len > 0) { |
a46d2c0e | 260 | server.bytesIn(len); |
e4f1fdae FC |
261 | kb_incr(&(statCounter.server.all.kbytes_in), len); |
262 | kb_incr(&(statCounter.server.other.kbytes_in), len); | |
ee1679df | 263 | } |
62e76326 | 264 | |
8a467c4b | 265 | copy (len, errcode, xerrno, server, client, WriteClientDone); |
a46d2c0e | 266 | } |
267 | ||
268 | void | |
fa34dd97 | 269 | TunnelStateData::Connection::error(int const xerrno) |
a46d2c0e | 270 | { |
271 | /* XXX fixme xstrerror and xerrno... */ | |
272 | errno = xerrno; | |
273 | ||
e0d28505 | 274 | debugs(50, debugLevelForError(xerrno), HERE << conn << ": read/write failure: " << xstrerror()); |
62e76326 | 275 | |
a46d2c0e | 276 | if (!ignoreErrno(xerrno)) |
fb046c1b | 277 | conn->close(); |
983061ed | 278 | } |
279 | ||
280 | /* Read from client side and queue it for writing to the server */ | |
a46d2c0e | 281 | void |
e0d28505 | 282 | TunnelStateData::ReadClient(const Comm::ConnectionPointer &, char *buf, size_t len, comm_err_t errcode, int xerrno, void *data) |
983061ed | 283 | { |
fa34dd97 | 284 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 285 | assert (cbdataReferenceValid (tunnelState)); |
62e76326 | 286 | |
11007d4b | 287 | tunnelState->readClient(buf, len, errcode, xerrno); |
a46d2c0e | 288 | } |
62e76326 | 289 | |
a46d2c0e | 290 | void |
fa34dd97 | 291 | TunnelStateData::readClient(char *buf, size_t len, comm_err_t errcode, int xerrno) |
a46d2c0e | 292 | { |
fd54d9b2 | 293 | debugs(26, 3, HERE << client.conn << ", read " << len << " bytes, err=" << errcode); |
d01053a2 | 294 | |
a46d2c0e | 295 | /* |
296 | * Bail out early on COMM_ERR_CLOSING | |
26ac0430 | 297 | * - close handlers will tidy up for us |
a46d2c0e | 298 | */ |
a55f4cea | 299 | |
a46d2c0e | 300 | if (errcode == COMM_ERR_CLOSING) |
301 | return; | |
a55f4cea | 302 | |
a46d2c0e | 303 | if (len > 0) { |
304 | client.bytesIn(len); | |
e4f1fdae | 305 | kb_incr(&(statCounter.client_http.kbytes_in), len); |
a46d2c0e | 306 | } |
62e76326 | 307 | |
8a467c4b | 308 | copy (len, errcode, xerrno, client, server, WriteServerDone); |
a46d2c0e | 309 | } |
62e76326 | 310 | |
a46d2c0e | 311 | void |
8a467c4b | 312 | TunnelStateData::copy (size_t len, comm_err_t errcode, int xerrno, Connection &from, Connection &to, IOCB *completion) |
a46d2c0e | 313 | { |
fd54d9b2 AJ |
314 | debugs(26, 3, HERE << "from={" << from.conn << "}, to={" << to.conn << "}"); |
315 | ||
a46d2c0e | 316 | /* I think this is to prevent free-while-in-a-callback behaviour |
26ac0430 | 317 | * - RBC 20030229 |
fb046c1b | 318 | * from.conn->close() / to.conn->close() done here trigger close callbacks which may free TunnelStateData |
a46d2c0e | 319 | */ |
320 | cbdataInternalLock(this); /* ??? should be locked by the caller... */ | |
62e76326 | 321 | |
60dafd5e | 322 | /* Bump the source connection read timeout on any activity */ |
8d77a37c AJ |
323 | if (Comm::IsConnOpen(from.conn)) { |
324 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", | |
dc49061a | 325 | CommTimeoutCbPtrFun(tunnelTimeout, this)); |
8d77a37c AJ |
326 | commSetConnTimeout(from.conn, Config.Timeout.read, timeoutCall); |
327 | } | |
99c02c10 | 328 | |
58c0b17d | 329 | if (errcode) |
a46d2c0e | 330 | from.error (xerrno); |
60dafd5e | 331 | else if (len == 0 || !Comm::IsConnOpen(to.conn)) { |
fd54d9b2 | 332 | debugs(26, 3, HERE << "Nothing to write or client gone. Terminate the tunnel."); |
fb046c1b | 333 | from.conn->close(); |
62e76326 | 334 | |
60dafd5e | 335 | /* Only close the remote end if we've finished queueing data to it */ |
97c81191 | 336 | if (from.len == 0 && Comm::IsConnOpen(to.conn) ) { |
fb046c1b | 337 | to.conn->close(); |
c4b7a5a9 | 338 | } |
ec41b64c | 339 | } else if (cbdataReferenceValid(this)) { |
fd54d9b2 | 340 | debugs(26, 3, HERE << "Schedule Write"); |
8a467c4b AJ |
341 | AsyncCall::Pointer call = commCbCall(5,5, "TunnelBlindCopyWriteHandler", |
342 | CommIoCbPtrFun(completion, this)); | |
343 | Comm::Write(to.conn, from.buf, len, call, NULL); | |
ec41b64c | 344 | } |
a55f4cea | 345 | |
a46d2c0e | 346 | cbdataInternalUnlock(this); /* ??? */ |
983061ed | 347 | } |
348 | ||
349 | /* Writes data from the client buffer to the server side */ | |
a46d2c0e | 350 | void |
e0d28505 | 351 | TunnelStateData::WriteServerDone(const Comm::ConnectionPointer &, char *buf, size_t len, comm_err_t flag, int xerrno, void *data) |
983061ed | 352 | { |
fa34dd97 | 353 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 354 | assert (cbdataReferenceValid (tunnelState)); |
a46d2c0e | 355 | |
11007d4b | 356 | tunnelState->writeServerDone(buf, len, flag, xerrno); |
a46d2c0e | 357 | } |
a55f4cea | 358 | |
a46d2c0e | 359 | void |
fa34dd97 | 360 | TunnelStateData::writeServerDone(char *buf, size_t len, comm_err_t flag, int xerrno) |
a46d2c0e | 361 | { |
fd54d9b2 | 362 | debugs(26, 3, HERE << server.conn << ", " << len << " bytes written, flag=" << flag); |
62e76326 | 363 | |
5dacdf3f | 364 | /* Error? */ |
58c0b17d | 365 | if (flag != COMM_OK) { |
fd54d9b2 AJ |
366 | if (flag != COMM_ERR_CLOSING) { |
367 | debugs(26, 4, HERE << "calling TunnelStateData::server.error(" << xerrno <<")"); | |
58c0b17d | 368 | server.error(xerrno); // may call comm_close |
fd54d9b2 | 369 | } |
5dacdf3f | 370 | return; |
c4b7a5a9 | 371 | } |
62e76326 | 372 | |
5dacdf3f | 373 | /* EOF? */ |
a46d2c0e | 374 | if (len == 0) { |
fd54d9b2 | 375 | debugs(26, 4, HERE << "No read input. Closing server connection."); |
fb046c1b | 376 | server.conn->close(); |
a46d2c0e | 377 | return; |
378 | } | |
62e76326 | 379 | |
5dacdf3f | 380 | /* Valid data */ |
e4f1fdae FC |
381 | kb_incr(&(statCounter.server.all.kbytes_out), len); |
382 | kb_incr(&(statCounter.server.other.kbytes_out), len); | |
5dacdf3f | 383 | client.dataSent(len); |
384 | ||
a46d2c0e | 385 | /* If the other end has closed, so should we */ |
97c81191 | 386 | if (!Comm::IsConnOpen(client.conn)) { |
fd54d9b2 | 387 | debugs(26, 4, HERE << "Client gone away. Shutting down server connection."); |
fb046c1b | 388 | server.conn->close(); |
a55f4cea | 389 | return; |
c4b7a5a9 | 390 | } |
62e76326 | 391 | |
a46d2c0e | 392 | cbdataInternalLock(this); /* ??? should be locked by the caller... */ |
a46d2c0e | 393 | |
5dacdf3f | 394 | if (cbdataReferenceValid(this)) |
a46d2c0e | 395 | copyRead(client, ReadClient); |
396 | ||
397 | cbdataInternalUnlock(this); /* ??? */ | |
983061ed | 398 | } |
399 | ||
400 | /* Writes data from the server buffer to the client side */ | |
a46d2c0e | 401 | void |
e0d28505 | 402 | TunnelStateData::WriteClientDone(const Comm::ConnectionPointer &, char *buf, size_t len, comm_err_t flag, int xerrno, void *data) |
983061ed | 403 | { |
fa34dd97 | 404 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 405 | assert (cbdataReferenceValid (tunnelState)); |
a46d2c0e | 406 | |
11007d4b | 407 | tunnelState->writeClientDone(buf, len, flag, xerrno); |
a46d2c0e | 408 | } |
409 | ||
410 | void | |
e0d28505 | 411 | TunnelStateData::Connection::dataSent(size_t amount) |
a46d2c0e | 412 | { |
fd54d9b2 | 413 | debugs(26, 3, HERE << "len=" << len << " - amount=" << amount); |
a46d2c0e | 414 | assert(amount == (size_t)len); |
415 | len =0; | |
416 | /* increment total object size */ | |
417 | ||
418 | if (size_ptr) | |
419 | *size_ptr += amount; | |
420 | } | |
421 | ||
422 | void | |
fa34dd97 | 423 | TunnelStateData::writeClientDone(char *buf, size_t len, comm_err_t flag, int xerrno) |
a46d2c0e | 424 | { |
fd54d9b2 | 425 | debugs(26, 3, HERE << client.conn << ", " << len << " bytes written, flag=" << flag); |
62e76326 | 426 | |
5dacdf3f | 427 | /* Error? */ |
58c0b17d | 428 | if (flag != COMM_OK) { |
fd54d9b2 AJ |
429 | if (flag != COMM_ERR_CLOSING) { |
430 | debugs(26, 4, HERE << "Closing client connection due to comm flags."); | |
58c0b17d | 431 | client.error(xerrno); // may call comm_close |
fd54d9b2 | 432 | } |
5dacdf3f | 433 | return; |
c4b7a5a9 | 434 | } |
62e76326 | 435 | |
5dacdf3f | 436 | /* EOF? */ |
a46d2c0e | 437 | if (len == 0) { |
fd54d9b2 | 438 | debugs(26, 4, HERE << "Closing client connection due to 0 byte read."); |
fb046c1b | 439 | client.conn->close(); |
62e76326 | 440 | return; |
983061ed | 441 | } |
62e76326 | 442 | |
5dacdf3f | 443 | /* Valid data */ |
e4f1fdae | 444 | kb_incr(&(statCounter.client_http.kbytes_out), len); |
5dacdf3f | 445 | server.dataSent(len); |
446 | ||
a46d2c0e | 447 | /* If the other end has closed, so should we */ |
97c81191 | 448 | if (!Comm::IsConnOpen(server.conn)) { |
fd54d9b2 | 449 | debugs(26, 4, HERE << "Server has gone away. Terminating client connection."); |
fb046c1b | 450 | client.conn->close(); |
a55f4cea | 451 | return; |
983061ed | 452 | } |
62e76326 | 453 | |
a46d2c0e | 454 | cbdataInternalLock(this); /* ??? should be locked by the caller... */ |
a55f4cea | 455 | |
5dacdf3f | 456 | if (cbdataReferenceValid(this)) |
a46d2c0e | 457 | copyRead(server, ReadServer); |
2c202d66 | 458 | |
a46d2c0e | 459 | cbdataInternalUnlock(this); /* ??? */ |
983061ed | 460 | } |
461 | ||
b8d8561b | 462 | static void |
8d77a37c | 463 | tunnelTimeout(const CommTimeoutCbParams &io) |
983061ed | 464 | { |
8d77a37c AJ |
465 | TunnelStateData *tunnelState = static_cast<TunnelStateData *>(io.data); |
466 | debugs(26, 3, HERE << io.conn); | |
11007d4b | 467 | /* Temporary lock to protect our own feets (comm_close -> tunnelClientClosed -> Free) */ |
468 | cbdataInternalLock(tunnelState); | |
a55f4cea | 469 | |
11007d4b | 470 | tunnelState->client.closeIfOpen(); |
471 | tunnelState->server.closeIfOpen(); | |
472 | cbdataInternalUnlock(tunnelState); | |
a46d2c0e | 473 | } |
62e76326 | 474 | |
a46d2c0e | 475 | void |
fa34dd97 | 476 | TunnelStateData::Connection::closeIfOpen() |
a46d2c0e | 477 | { |
97c81191 | 478 | if (Comm::IsConnOpen(conn)) |
fb046c1b | 479 | conn->close(); |
a46d2c0e | 480 | } |
481 | ||
482 | void | |
fa34dd97 | 483 | TunnelStateData::copyRead(Connection &from, IOCB *completion) |
a46d2c0e | 484 | { |
485 | assert(from.len == 0); | |
fd54d9b2 | 486 | AsyncCall::Pointer call = commCbCall(5,4, "TunnelBlindCopyReadHandler", |
abd8f140 | 487 | CommIoCbPtrFun(completion, this)); |
8a467c4b | 488 | comm_read(from.conn, from.buf, from.bytesWanted(1, SQUID_TCP_SO_RCVBUF), call); |
983061ed | 489 | } |
490 | ||
379e8c1c | 491 | /** |
87f237a9 | 492 | * Set the HTTP status for this request and sets the read handlers for client |
379e8c1c AR |
493 | * and server side connections. |
494 | */ | |
495 | static void | |
496 | tunnelStartShoveling(TunnelStateData *tunnelState) | |
497 | { | |
498 | *tunnelState->status_ptr = HTTP_OK; | |
499 | if (cbdataReferenceValid(tunnelState)) { | |
500 | tunnelState->copyRead(tunnelState->server, TunnelStateData::ReadServer); | |
501 | tunnelState->copyRead(tunnelState->client, TunnelStateData::ReadClient); | |
502 | } | |
503 | } | |
504 | ||
b0388924 AJ |
505 | /** |
506 | * All the pieces we need to write to client and/or server connection | |
1b76e6c1 | 507 | * have been written. |
379e8c1c | 508 | * Call the tunnelStartShoveling to start the blind pump. |
b0388924 | 509 | */ |
c4b7a5a9 | 510 | static void |
e0d28505 | 511 | tunnelConnectedWriteDone(const Comm::ConnectionPointer &conn, char *buf, size_t size, comm_err_t flag, int xerrno, void *data) |
c4b7a5a9 | 512 | { |
fa34dd97 | 513 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
fd54d9b2 | 514 | debugs(26, 3, HERE << conn << ", flag=" << flag); |
62e76326 | 515 | |
516 | if (flag != COMM_OK) { | |
1b76e6c1 | 517 | *tunnelState->status_ptr = HTTP_INTERNAL_SERVER_ERROR; |
e0d28505 | 518 | tunnelErrorComplete(conn->fd, data, 0); |
62e76326 | 519 | return; |
520 | } | |
521 | ||
379e8c1c | 522 | tunnelStartShoveling(tunnelState); |
c4b7a5a9 | 523 | } |
524 | ||
c4b7a5a9 | 525 | /* |
1b76e6c1 | 526 | * handle the write completion from a proxy request to an upstream origin |
c4b7a5a9 | 527 | */ |
b8d8561b | 528 | static void |
f01d4b80 | 529 | tunnelConnected(const Comm::ConnectionPointer &server, void *data) |
983061ed | 530 | { |
fa34dd97 | 531 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
f01d4b80 | 532 | debugs(26, 3, HERE << server << ", tunnelState=" << tunnelState); |
379e8c1c | 533 | |
b58b5f77 | 534 | if (tunnelState->request && (tunnelState->request->flags.spoofClientIp() || tunnelState->request->flags.intercepted())) |
379e8c1c AR |
535 | tunnelStartShoveling(tunnelState); // ssl-bumped connection, be quiet |
536 | else { | |
537 | AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone", | |
538 | CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState)); | |
539 | Comm::Write(tunnelState->client.conn, conn_established, strlen(conn_established), call, NULL); | |
540 | } | |
983061ed | 541 | } |
542 | ||
b8d8561b | 543 | static void |
fd54d9b2 | 544 | tunnelErrorComplete(int fd/*const Comm::ConnectionPointer &*/, void *data, size_t) |
30a4f2a8 | 545 | { |
fa34dd97 | 546 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
fd54d9b2 | 547 | debugs(26, 3, HERE << "FD " << fd); |
11007d4b | 548 | assert(tunnelState != NULL); |
549 | /* temporary lock to save our own feets (comm_close -> tunnelClientClosed -> Free) */ | |
550 | cbdataInternalLock(tunnelState); | |
62e76326 | 551 | |
97c81191 | 552 | if (Comm::IsConnOpen(tunnelState->client.conn)) |
fb046c1b | 553 | tunnelState->client.conn->close(); |
62e76326 | 554 | |
97c81191 | 555 | if (Comm::IsConnOpen(tunnelState->server.conn)) |
fb046c1b | 556 | tunnelState->server.conn->close(); |
380963c2 | 557 | |
11007d4b | 558 | cbdataInternalUnlock(tunnelState); |
30a4f2a8 | 559 | } |
560 | ||
b8d8561b | 561 | static void |
f01d4b80 | 562 | tunnelConnectDone(const Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data) |
983061ed | 563 | { |
fa34dd97 | 564 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
cfd66529 AJ |
565 | |
566 | if (status != COMM_OK) { | |
fd54d9b2 | 567 | debugs(26, 4, HERE << conn << ", comm failure recovery."); |
aed188fd AJ |
568 | /* At this point only the TCP handshake has failed. no data has been passed. |
569 | * we are allowed to re-try the TCP-level connection to alternate IPs for CONNECT. | |
570 | */ | |
00ae51e4 AJ |
571 | tunnelState->serverDestinations.shift(); |
572 | if (status != COMM_TIMEOUT && tunnelState->serverDestinations.size() > 0) { | |
aed188fd | 573 | /* Try another IP of this destination host */ |
fd54d9b2 | 574 | debugs(26, 4, HERE << "retry with : " << tunnelState->serverDestinations[0]); |
aed188fd | 575 | AsyncCall::Pointer call = commCbCall(26,3, "tunnelConnectDone", CommConnectCbPtrFun(tunnelConnectDone, tunnelState)); |
00ae51e4 | 576 | Comm::ConnOpener *cs = new Comm::ConnOpener(tunnelState->serverDestinations[0], call, Config.Timeout.connect); |
aed188fd | 577 | cs->setHost(tunnelState->url); |
855150a4 | 578 | AsyncJob::Start(cs); |
aed188fd | 579 | } else { |
fd54d9b2 | 580 | debugs(26, 4, HERE << "terminate with error."); |
913524f0 | 581 | ErrorState *err = new ErrorState(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, tunnelState->request); |
aed188fd AJ |
582 | *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE; |
583 | err->xerrno = xerrno; | |
584 | // on timeout is this still: err->xerrno = ETIMEDOUT; | |
585 | err->port = conn->remote.GetPort(); | |
586 | err->callback = tunnelErrorComplete; | |
587 | err->callback_data = tunnelState; | |
e0d28505 | 588 | errorSend(tunnelState->client.conn, err); |
aed188fd | 589 | } |
cfd66529 AJ |
590 | return; |
591 | } | |
592 | ||
4beb4bab AJ |
593 | #if USE_DELAY_POOLS |
594 | /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */ | |
595 | if (conn->getPeer() && conn->getPeer()->options.no_delay) | |
596 | tunnelState->server.setDelayId(DelayId()); | |
597 | #endif | |
598 | ||
a14f38d0 | 599 | tunnelState->request->hier.note(conn, tunnelState->getHost()); |
4beb4bab | 600 | |
fb046c1b | 601 | tunnelState->server.conn = conn; |
4beb4bab | 602 | tunnelState->request->peer_host = conn->getPeer() ? conn->getPeer()->host : NULL; |
fb046c1b | 603 | comm_add_close_handler(conn->fd, tunnelServerClosed, tunnelState); |
cfd66529 | 604 | |
fd54d9b2 | 605 | debugs(26, 4, HERE << "determine post-connect handling pathway."); |
739b352a AJ |
606 | if (conn->getPeer()) { |
607 | tunnelState->request->peer_login = conn->getPeer()->login; | |
d60a166f FC |
608 | if (conn->getPeer()->options.originserver) |
609 | tunnelState->request->flags.setProxying(); | |
610 | else | |
611 | tunnelState->request->flags.clearProxying(); | |
fe40a877 | 612 | } else { |
cfd66529 | 613 | tunnelState->request->peer_login = NULL; |
d60a166f | 614 | tunnelState->request->flags.clearProxying(); |
cfd66529 | 615 | } |
62e76326 | 616 | |
d60a166f | 617 | if (tunnelState->request->flags.proxying()) |
fb046c1b | 618 | tunnelRelayConnectRequest(conn, tunnelState); |
cfd66529 | 619 | else { |
fb046c1b | 620 | tunnelConnected(conn, tunnelState); |
983061ed | 621 | } |
cfd66529 | 622 | |
8d77a37c | 623 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", |
dc49061a | 624 | CommTimeoutCbPtrFun(tunnelTimeout, tunnelState)); |
8d77a37c | 625 | commSetConnTimeout(conn, Config.Timeout.read, timeoutCall); |
983061ed | 626 | } |
30a4f2a8 | 627 | |
425de4c8 AJ |
628 | extern tos_t GetTosToServer(HttpRequest * request); |
629 | extern nfmark_t GetNfmarkToServer(HttpRequest * request); | |
630 | ||
770f051d | 631 | void |
47f6e231 | 632 | tunnelStart(ClientHttpRequest * http, int64_t * size_ptr, int *status_ptr) |
30a4f2a8 | 633 | { |
fd54d9b2 | 634 | debugs(26, 3, HERE); |
30a4f2a8 | 635 | /* Create state structure. */ |
fa34dd97 | 636 | TunnelStateData *tunnelState = NULL; |
9b312a19 | 637 | ErrorState *err = NULL; |
190154cf | 638 | HttpRequest *request = http->request; |
d5964d58 | 639 | char *url = http->uri; |
fb046c1b | 640 | |
f1003989 | 641 | /* |
cc192b50 | 642 | * client_addr.IsNoAddr() indicates this is an "internal" request |
a4b8110e | 643 | * from peer_digest.c, asn.c, netdb.c, etc and should always |
644 | * be allowed. yuck, I know. | |
645 | */ | |
62e76326 | 646 | |
b8a25eaa | 647 | if (Config.accessList.miss && !request->client_addr.IsNoAddr()) { |
62e76326 | 648 | /* |
649 | * Check if this host is allowed to fetch MISSES from us (miss_access) | |
b50e327b | 650 | * default is to allow. |
62e76326 | 651 | */ |
c0941a6a | 652 | ACLFilledChecklist ch(Config.accessList.miss, request, NULL); |
62e76326 | 653 | ch.src_addr = request->client_addr; |
654 | ch.my_addr = request->my_addr; | |
2efeb0b7 | 655 | if (ch.fastCheck() == ACCESS_DENIED) { |
fd54d9b2 | 656 | debugs(26, 4, HERE << "MISS access forbidden."); |
913524f0 | 657 | err = new ErrorState(ERR_FORWARDING_DENIED, HTTP_FORBIDDEN, request); |
62e76326 | 658 | *status_ptr = HTTP_FORBIDDEN; |
73c36fd9 | 659 | errorSend(http->getConn()->clientConnection, err); |
62e76326 | 660 | return; |
661 | } | |
f1003989 | 662 | } |
62e76326 | 663 | |
c9fd01b4 | 664 | debugs(26, 3, HERE << "'" << RequestMethodStr(request->method) << " " << url << " " << request->http_ver << "'"); |
5db6bf73 FC |
665 | ++statCounter.server.all.requests; |
666 | ++statCounter.server.other.requests; | |
62e76326 | 667 | |
fa34dd97 | 668 | tunnelState = new TunnelStateData; |
9a0a18de | 669 | #if USE_DELAY_POOLS |
11007d4b | 670 | tunnelState->server.setDelayId(DelayId::DelayClient(http)); |
59715b38 | 671 | #endif |
11007d4b | 672 | tunnelState->url = xstrdup(url); |
673 | tunnelState->request = HTTPMSGLOCK(request); | |
674 | tunnelState->server.size_ptr = size_ptr; | |
675 | tunnelState->status_ptr = status_ptr; | |
73c36fd9 | 676 | tunnelState->client.conn = http->getConn()->clientConnection; |
fb046c1b AJ |
677 | |
678 | comm_add_close_handler(tunnelState->client.conn->fd, | |
11007d4b | 679 | tunnelClientClosed, |
680 | tunnelState); | |
8d77a37c AJ |
681 | |
682 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", | |
dc49061a | 683 | CommTimeoutCbPtrFun(tunnelTimeout, tunnelState)); |
8d77a37c | 684 | commSetConnTimeout(tunnelState->client.conn, Config.Timeout.lifetime, timeoutCall); |
cfd66529 | 685 | |
00ae51e4 | 686 | peerSelect(&(tunnelState->serverDestinations), request, |
62e76326 | 687 | NULL, |
11007d4b | 688 | tunnelPeerSelectComplete, |
689 | tunnelState); | |
30a4f2a8 | 690 | } |
98ffb7e4 | 691 | |
b8d8561b | 692 | static void |
b0388924 | 693 | tunnelRelayConnectRequest(const Comm::ConnectionPointer &srv, void *data) |
98ffb7e4 | 694 | { |
fa34dd97 | 695 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
75faaa7a | 696 | HttpHeader hdr_out(hoRequest); |
e1e72f06 | 697 | Packer p; |
b4b5fd95 | 698 | http_state_flags flags; |
b0388924 | 699 | debugs(26, 3, HERE << srv << ", tunnelState=" << tunnelState); |
b4b5fd95 | 700 | memset(&flags, '\0', sizeof(flags)); |
d60a166f | 701 | flags.proxying = tunnelState->request->flags.proxying(); |
032785bf | 702 | MemBuf mb; |
2fe7eff9 | 703 | mb.init(); |
3872be7c | 704 | mb.Printf("CONNECT %s HTTP/1.1\r\n", tunnelState->url); |
11007d4b | 705 | HttpStateData::httpBuildRequestHeader(tunnelState->request, |
e5ee81f0 | 706 | NULL, /* StoreEntry */ |
4bf68cfa | 707 | NULL, /* AccessLogEntry */ |
e5ee81f0 | 708 | &hdr_out, |
709 | flags); /* flags */ | |
e1e72f06 | 710 | packerToMemInit(&p, &mb); |
a9925b40 | 711 | hdr_out.packInto(&p); |
519e0948 | 712 | hdr_out.clean(); |
e1e72f06 | 713 | packerClean(&p); |
2fe7eff9 | 714 | mb.append("\r\n", 2); |
c4b7a5a9 | 715 | |
8d77a37c | 716 | AsyncCall::Pointer writeCall = commCbCall(5,5, "tunnelConnectedWriteDone", |
dc49061a | 717 | CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState)); |
8d77a37c AJ |
718 | Comm::Write(srv, &mb, writeCall); |
719 | ||
720 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", | |
dc49061a | 721 | CommTimeoutCbPtrFun(tunnelTimeout, tunnelState)); |
8d77a37c | 722 | commSetConnTimeout(srv, Config.Timeout.read, timeoutCall); |
98ffb7e4 | 723 | } |
33ea9fff | 724 | |
33ea9fff | 725 | static void |
a37fdd8a | 726 | tunnelPeerSelectComplete(Comm::ConnectionList *peer_paths, ErrorState *err, void *data) |
33ea9fff | 727 | { |
fa34dd97 | 728 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
62e76326 | 729 | |
cfd66529 | 730 | if (peer_paths == NULL || peer_paths->size() < 1) { |
fd54d9b2 | 731 | debugs(26, 3, HERE << "No paths found. Aborting CONNECT"); |
a37fdd8a AJ |
732 | if (!err) { |
733 | err = new ErrorState(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE, tunnelState->request); | |
734 | } | |
735 | *tunnelState->status_ptr = err->httpStatus; | |
11007d4b | 736 | err->callback = tunnelErrorComplete; |
737 | err->callback_data = tunnelState; | |
e0d28505 | 738 | errorSend(tunnelState->client.conn, err); |
62e76326 | 739 | return; |
db1cd23c | 740 | } |
a37fdd8a AJ |
741 | delete err; |
742 | ||
fd54d9b2 AJ |
743 | debugs(26, 3, HERE << "paths=" << peer_paths->size() << ", p[0]={" << (*peer_paths)[0] << "}, serverDest[0]={" << |
744 | tunnelState->serverDestinations[0] << "}"); | |
62e76326 | 745 | |
cfd66529 | 746 | AsyncCall::Pointer call = commCbCall(26,3, "tunnelConnectDone", CommConnectCbPtrFun(tunnelConnectDone, tunnelState)); |
00ae51e4 | 747 | Comm::ConnOpener *cs = new Comm::ConnOpener(tunnelState->serverDestinations[0], call, Config.Timeout.connect); |
aed188fd | 748 | cs->setHost(tunnelState->url); |
855150a4 | 749 | AsyncJob::Start(cs); |
33ea9fff | 750 | } |
a46d2c0e | 751 | |
fa34dd97 | 752 | CBDATA_CLASS_INIT(TunnelStateData); |
a46d2c0e | 753 | |
754 | void * | |
fa34dd97 | 755 | TunnelStateData::operator new (size_t) |
a46d2c0e | 756 | { |
fa34dd97 | 757 | CBDATA_INIT_TYPE(TunnelStateData); |
758 | TunnelStateData *result = cbdataAlloc(TunnelStateData); | |
a46d2c0e | 759 | return result; |
760 | } | |
761 | ||
762 | void | |
fa34dd97 | 763 | TunnelStateData::operator delete (void *address) |
a46d2c0e | 764 | { |
fa34dd97 | 765 | TunnelStateData *t = static_cast<TunnelStateData *>(address); |
a46d2c0e | 766 | cbdataFree(t); |
767 | } | |
768 | ||
a46d2c0e | 769 | bool |
fa34dd97 | 770 | TunnelStateData::noConnections() const |
a46d2c0e | 771 | { |
97c81191 | 772 | return !Comm::IsConnOpen(server.conn) && !Comm::IsConnOpen(client.conn); |
a46d2c0e | 773 | } |
774 | ||
9a0a18de | 775 | #if USE_DELAY_POOLS |
a46d2c0e | 776 | void |
fa34dd97 | 777 | TunnelStateData::Connection::setDelayId(DelayId const &newDelay) |
a46d2c0e | 778 | { |
779 | delayId = newDelay; | |
780 | } | |
781 | ||
782 | #endif |