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