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