]>
Commit | Line | Data |
---|---|---|
95d659f0 | 1 | |
983061ed | 2 | /* |
47f6e231 | 3 | * $Id: tunnel.cc,v 1.173 2007/08/13 17:20:51 hno Exp $ |
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. | |
24 | * | |
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. | |
29 | * | |
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" | |
9f518b4a | 40 | #include "comm.h" |
528b2c61 | 41 | #include "client_side_request.h" |
4fb35c3c | 42 | #include "ACLChecklist.h" |
b67e2c8c | 43 | #if DELAY_POOLS |
44 | #include "DelayId.h" | |
45 | #endif | |
a46d2c0e | 46 | #include "client_side.h" |
0eb49b6d | 47 | #include "MemBuf.h" |
e5ee81f0 | 48 | #include "http.h" |
983061ed | 49 | |
fa34dd97 | 50 | class TunnelStateData |
62e76326 | 51 | { |
a46d2c0e | 52 | |
53 | public: | |
54 | ||
55 | class Connection; | |
56 | void *operator new(size_t); | |
57 | void operator delete (void *); | |
a46d2c0e | 58 | static void ReadClient(int fd, char *buf, size_t len, comm_err_t errcode, int xerrno, void *data); |
59 | static void ReadServer(int fd, char *buf, size_t len, comm_err_t errcode, int xerrno, void *data); | |
60 | static void WriteClientDone(int fd, char *buf, size_t len, comm_err_t flag, int xerrno, void *data); | |
61 | static void WriteServerDone(int fd, char *buf, size_t len, comm_err_t flag, int xerrno, void *data); | |
62 | ||
63 | bool noConnections() const; | |
983061ed | 64 | char *url; |
98ffb7e4 | 65 | char *host; /* either request->host or proxy host */ |
66 | u_short port; | |
190154cf | 67 | HttpRequest *request; |
64d8034e | 68 | FwdServer *servers; |
62e76326 | 69 | |
a46d2c0e | 70 | class Connection |
62e76326 | 71 | { |
a46d2c0e | 72 | |
73 | public: | |
74 | Connection() : len (0),buf ((char *)xmalloc(SQUID_TCP_SO_RCVBUF)), size_ptr(NULL), fd_(-1){} | |
75 | ||
76 | ~Connection(); | |
77 | int const & fd() const { return fd_;} | |
78 | ||
79 | void fd(int const newFD); | |
80 | int bytesWanted(int lower=0, int upper = INT_MAX) const; | |
81 | void bytesIn(int const &); | |
82 | #if DELAY_POOLS | |
83 | ||
84 | void setDelayId(DelayId const &); | |
85 | #endif | |
86 | ||
87 | void error(int const xerrno); | |
5c926411 | 88 | int debugLevelForError(int const xerrno) const; |
a46d2c0e | 89 | void closeIfOpen(); |
90 | void dataSent (size_t amount); | |
62e76326 | 91 | int len; |
92 | char *buf; | |
47f6e231 | 93 | int64_t *size_ptr; /* pointer to size in an ConnStateData for logging */ |
62e76326 | 94 | |
a46d2c0e | 95 | private: |
96 | int fd_; | |
59715b38 | 97 | #if DELAY_POOLS |
62e76326 | 98 | |
a46d2c0e | 99 | DelayId delayId; |
59715b38 | 100 | #endif |
62e76326 | 101 | |
a46d2c0e | 102 | }; |
103 | ||
104 | Connection client, server; | |
105 | int *status_ptr; /* pointer to status for logging */ | |
106 | void copyRead(Connection &from, IOCB *completion); | |
107 | ||
108 | private: | |
fa34dd97 | 109 | CBDATA_CLASS(TunnelStateData); |
2b663917 | 110 | void copy (size_t len, comm_err_t errcode, int xerrno, Connection &from, Connection &to, IOCB *); |
a46d2c0e | 111 | void readServer(char *buf, size_t len, comm_err_t errcode, int xerrno); |
112 | void readClient(char *buf, size_t len, comm_err_t errcode, int xerrno); | |
113 | void writeClientDone(char *buf, size_t len, comm_err_t flag, int xerrno); | |
114 | void writeServerDone(char *buf, size_t len, comm_err_t flag, int xerrno); | |
115 | }; | |
983061ed | 116 | |
0ee4272b | 117 | static const char *const conn_established = "HTTP/1.0 200 Connection established\r\n\r\n"; |
983061ed | 118 | |
11007d4b | 119 | static CNCB tunnelConnectDone; |
120 | static ERCB tunnelErrorComplete; | |
121 | static PF tunnelServerClosed; | |
122 | static PF tunnelClientClosed; | |
123 | static PF tunnelTimeout; | |
124 | static PSC tunnelPeerSelectComplete; | |
fa34dd97 | 125 | static void tunnelStateFree(TunnelStateData * tunnelState); |
11007d4b | 126 | static void tunnelConnected(int fd, void *); |
127 | static void tunnelProxyConnected(int fd, void *); | |
30a4f2a8 | 128 | |
b8d8561b | 129 | static void |
11007d4b | 130 | tunnelServerClosed(int fd, void *data) |
30a4f2a8 | 131 | { |
fa34dd97 | 132 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 133 | debugs(26, 3, "tunnelServerClosed: FD " << fd); |
134 | assert(fd == tunnelState->server.fd()); | |
135 | tunnelState->server.fd(-1); | |
62e76326 | 136 | |
11007d4b | 137 | if (tunnelState->noConnections()) |
138 | tunnelStateFree(tunnelState); | |
30a4f2a8 | 139 | } |
140 | ||
b177367b | 141 | static void |
11007d4b | 142 | tunnelClientClosed(int fd, void *data) |
30a4f2a8 | 143 | { |
fa34dd97 | 144 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 145 | debugs(26, 3, "tunnelClientClosed: FD " << fd); |
146 | assert(fd == tunnelState->client.fd()); | |
147 | tunnelState->client.fd(-1); | |
62e76326 | 148 | |
11007d4b | 149 | if (tunnelState->noConnections()) |
150 | tunnelStateFree(tunnelState); | |
30a4f2a8 | 151 | } |
983061ed | 152 | |
b177367b | 153 | static void |
fa34dd97 | 154 | tunnelStateFree(TunnelStateData * tunnelState) |
983061ed | 155 | { |
11007d4b | 156 | debugs(26, 3, "tunnelStateFree: tunnelState=" << tunnelState); |
157 | assert(tunnelState != NULL); | |
158 | assert(tunnelState->noConnections()); | |
159 | safe_free(tunnelState->url); | |
160 | FwdState::serversFree(&tunnelState->servers); | |
161 | tunnelState->host = NULL; | |
162 | HTTPMSGUNLOCK(tunnelState->request); | |
163 | delete tunnelState; | |
983061ed | 164 | } |
165 | ||
fa34dd97 | 166 | TunnelStateData::Connection::~Connection() |
a46d2c0e | 167 | { |
168 | safe_free (buf); | |
169 | } | |
170 | ||
171 | int | |
fa34dd97 | 172 | TunnelStateData::Connection::bytesWanted(int lowerbound, int upperbound) const |
447e176b | 173 | { |
a46d2c0e | 174 | #if DELAY_POOLS |
175 | return delayId.bytesWanted(lowerbound, upperbound); | |
176 | #else | |
62e76326 | 177 | |
6b13136a | 178 | return upperbound; |
a46d2c0e | 179 | #endif |
180 | } | |
62e76326 | 181 | |
a46d2c0e | 182 | void |
fa34dd97 | 183 | TunnelStateData::Connection::bytesIn(int const &count) |
a46d2c0e | 184 | { |
185 | #if DELAY_POOLS | |
186 | delayId.bytesIn(count); | |
187 | #endif | |
62e76326 | 188 | |
a46d2c0e | 189 | len += count; |
447e176b | 190 | } |
62e76326 | 191 | |
a46d2c0e | 192 | int |
fa34dd97 | 193 | TunnelStateData::Connection::debugLevelForError(int const xerrno) const |
a46d2c0e | 194 | { |
195 | #ifdef ECONNRESET | |
196 | ||
197 | if (xerrno == ECONNRESET) | |
198 | return 2; | |
199 | ||
447e176b | 200 | #endif |
201 | ||
a46d2c0e | 202 | if (ignoreErrno(xerrno)) |
203 | return 3; | |
204 | ||
205 | return 1; | |
206 | } | |
adb78bd4 | 207 | |
983061ed | 208 | /* Read from server side and queue it for writing to the client */ |
a46d2c0e | 209 | void |
fa34dd97 | 210 | TunnelStateData::ReadServer(int fd, char *buf, size_t len, comm_err_t errcode, int xerrno, void *data) |
983061ed | 211 | { |
fa34dd97 | 212 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 213 | assert (cbdataReferenceValid (tunnelState)); |
c4b7a5a9 | 214 | |
11007d4b | 215 | assert(fd == tunnelState->server.fd()); |
216 | tunnelState->readServer(buf, len, errcode, xerrno); | |
a46d2c0e | 217 | } |
62e76326 | 218 | |
a46d2c0e | 219 | void |
fa34dd97 | 220 | TunnelStateData::readServer(char *buf, size_t len, comm_err_t errcode, int xerrno) |
a46d2c0e | 221 | { |
222 | /* | |
223 | * Bail out early on COMM_ERR_CLOSING | |
224 | * - close handlers will tidy up for us | |
225 | */ | |
a55f4cea | 226 | |
a46d2c0e | 227 | if (errcode == COMM_ERR_CLOSING) |
228 | return; | |
62e76326 | 229 | |
11007d4b | 230 | debugs(26, 3, "tunnelReadServer: FD " << server.fd() << ", read " << len << " bytes"); |
62e76326 | 231 | |
ee1679df | 232 | if (len > 0) { |
a46d2c0e | 233 | server.bytesIn(len); |
62e76326 | 234 | kb_incr(&statCounter.server.all.kbytes_in, len); |
235 | kb_incr(&statCounter.server.other.kbytes_in, len); | |
ee1679df | 236 | } |
62e76326 | 237 | |
a46d2c0e | 238 | copy (len, errcode, xerrno, server, client, WriteClientDone); |
239 | } | |
240 | ||
241 | void | |
fa34dd97 | 242 | TunnelStateData::Connection::error(int const xerrno) |
a46d2c0e | 243 | { |
244 | /* XXX fixme xstrerror and xerrno... */ | |
245 | errno = xerrno; | |
246 | ||
247 | if (xerrno == COMM_ERR_CLOSING) | |
a55f4cea | 248 | return; |
62e76326 | 249 | |
11007d4b | 250 | debugs(50, debugLevelForError(xerrno), "tunnelReadServer: FD " << fd() << |
bf8fe701 | 251 | ": read failure: " << xstrerror()); |
62e76326 | 252 | |
a46d2c0e | 253 | if (!ignoreErrno(xerrno)) |
254 | comm_close(fd()); | |
983061ed | 255 | } |
256 | ||
257 | /* Read from client side and queue it for writing to the server */ | |
a46d2c0e | 258 | void |
fa34dd97 | 259 | TunnelStateData::ReadClient(int fd, char *buf, size_t len, comm_err_t errcode, int xerrno, void *data) |
983061ed | 260 | { |
fa34dd97 | 261 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 262 | assert (cbdataReferenceValid (tunnelState)); |
62e76326 | 263 | |
11007d4b | 264 | assert(fd == tunnelState->client.fd()); |
265 | tunnelState->readClient(buf, len, errcode, xerrno); | |
a46d2c0e | 266 | } |
62e76326 | 267 | |
a46d2c0e | 268 | void |
fa34dd97 | 269 | TunnelStateData::readClient(char *buf, size_t len, comm_err_t errcode, int xerrno) |
a46d2c0e | 270 | { |
271 | /* | |
272 | * Bail out early on COMM_ERR_CLOSING | |
273 | * - close handlers will tidy up for us | |
274 | */ | |
a55f4cea | 275 | |
a46d2c0e | 276 | if (errcode == COMM_ERR_CLOSING) |
277 | return; | |
a55f4cea | 278 | |
11007d4b | 279 | debugs(26, 3, "tunnelReadClient: FD " << client.fd() << ", read " << len << " bytes"); |
62e76326 | 280 | |
a46d2c0e | 281 | if (len > 0) { |
282 | client.bytesIn(len); | |
283 | kb_incr(&statCounter.client_http.kbytes_in, len); | |
284 | } | |
62e76326 | 285 | |
a46d2c0e | 286 | copy (len, errcode, xerrno, client, server, WriteServerDone); |
287 | } | |
62e76326 | 288 | |
a46d2c0e | 289 | void |
fa34dd97 | 290 | TunnelStateData::copy (size_t len, comm_err_t errcode, int xerrno, Connection &from, Connection &to, IOCB *completion) |
a46d2c0e | 291 | { |
292 | /* I think this is to prevent free-while-in-a-callback behaviour | |
293 | * - RBC 20030229 | |
294 | */ | |
295 | cbdataInternalLock(this); /* ??? should be locked by the caller... */ | |
62e76326 | 296 | |
99c02c10 | 297 | /* Bump the server connection timeout on any activity */ |
298 | if (server.fd() != -1) | |
299 | commSetTimeout(server.fd(), Config.Timeout.read, tunnelTimeout, this); | |
300 | ||
a46d2c0e | 301 | if (len < 0 || errcode) |
302 | from.error (xerrno); | |
303 | else if (len == 0 || to.fd() == -1) { | |
304 | comm_close(from.fd()); | |
62e76326 | 305 | /* Only close the remote end if we've finished queueing data to it */ |
306 | ||
a46d2c0e | 307 | if (from.len == 0 && to.fd() != -1) { |
308 | comm_close(to.fd()); | |
c4b7a5a9 | 309 | } |
a46d2c0e | 310 | } else if (cbdataReferenceValid(this)) |
2b663917 | 311 | comm_write(to.fd(), from.buf, len, completion, this, NULL); |
a55f4cea | 312 | |
a46d2c0e | 313 | cbdataInternalUnlock(this); /* ??? */ |
983061ed | 314 | } |
315 | ||
316 | /* Writes data from the client buffer to the server side */ | |
a46d2c0e | 317 | void |
fa34dd97 | 318 | TunnelStateData::WriteServerDone(int fd, char *buf, size_t len, comm_err_t flag, int xerrno, void *data) |
983061ed | 319 | { |
fa34dd97 | 320 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 321 | assert (cbdataReferenceValid (tunnelState)); |
a46d2c0e | 322 | |
11007d4b | 323 | assert(fd == tunnelState->server.fd()); |
324 | tunnelState->writeServerDone(buf, len, flag, xerrno); | |
a46d2c0e | 325 | } |
a55f4cea | 326 | |
a46d2c0e | 327 | void |
fa34dd97 | 328 | TunnelStateData::writeServerDone(char *buf, size_t len, comm_err_t flag, int xerrno) |
a46d2c0e | 329 | { |
11007d4b | 330 | debugs(26, 3, "tunnelWriteServer: FD " << server.fd() << ", " << len << " bytes written"); |
62e76326 | 331 | |
5dacdf3f | 332 | /* Error? */ |
333 | if (len < 0 || flag != COMM_OK) { | |
334 | server.error(xerrno); // may call comm_close | |
335 | return; | |
c4b7a5a9 | 336 | } |
62e76326 | 337 | |
5dacdf3f | 338 | /* EOF? */ |
a46d2c0e | 339 | if (len == 0) { |
340 | comm_close(server.fd()); | |
341 | return; | |
342 | } | |
62e76326 | 343 | |
5dacdf3f | 344 | /* Valid data */ |
345 | kb_incr(&statCounter.server.all.kbytes_out, len); | |
346 | kb_incr(&statCounter.server.other.kbytes_out, len); | |
347 | client.dataSent(len); | |
348 | ||
a46d2c0e | 349 | /* If the other end has closed, so should we */ |
350 | if (client.fd() == -1) { | |
351 | comm_close(server.fd()); | |
a55f4cea | 352 | return; |
c4b7a5a9 | 353 | } |
62e76326 | 354 | |
a46d2c0e | 355 | cbdataInternalLock(this); /* ??? should be locked by the caller... */ |
a46d2c0e | 356 | |
5dacdf3f | 357 | if (cbdataReferenceValid(this)) |
a46d2c0e | 358 | copyRead(client, ReadClient); |
359 | ||
360 | cbdataInternalUnlock(this); /* ??? */ | |
983061ed | 361 | } |
362 | ||
363 | /* Writes data from the server buffer to the client side */ | |
a46d2c0e | 364 | void |
fa34dd97 | 365 | TunnelStateData::WriteClientDone(int fd, char *buf, size_t len, comm_err_t flag, int xerrno, void *data) |
983061ed | 366 | { |
fa34dd97 | 367 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 368 | assert (cbdataReferenceValid (tunnelState)); |
a46d2c0e | 369 | |
11007d4b | 370 | assert(fd == tunnelState->client.fd()); |
371 | tunnelState->writeClientDone(buf, len, flag, xerrno); | |
a46d2c0e | 372 | } |
373 | ||
374 | void | |
fa34dd97 | 375 | TunnelStateData::Connection::dataSent (size_t amount) |
a46d2c0e | 376 | { |
377 | assert(amount == (size_t)len); | |
378 | len =0; | |
379 | /* increment total object size */ | |
380 | ||
381 | if (size_ptr) | |
382 | *size_ptr += amount; | |
383 | } | |
384 | ||
385 | void | |
fa34dd97 | 386 | TunnelStateData::writeClientDone(char *buf, size_t len, comm_err_t flag, int xerrno) |
a46d2c0e | 387 | { |
11007d4b | 388 | debugs(26, 3, "tunnelWriteClient: FD " << client.fd() << ", " << len << " bytes written"); |
62e76326 | 389 | |
5dacdf3f | 390 | /* Error? */ |
391 | if (len < 0 || flag != COMM_OK) { | |
392 | client.error(xerrno); // may call comm_close | |
393 | return; | |
c4b7a5a9 | 394 | } |
62e76326 | 395 | |
5dacdf3f | 396 | /* EOF? */ |
a46d2c0e | 397 | if (len == 0) { |
398 | comm_close(client.fd()); | |
62e76326 | 399 | return; |
983061ed | 400 | } |
62e76326 | 401 | |
5dacdf3f | 402 | /* Valid data */ |
403 | kb_incr(&statCounter.client_http.kbytes_out, len); | |
404 | server.dataSent(len); | |
405 | ||
a46d2c0e | 406 | /* If the other end has closed, so should we */ |
407 | if (server.fd() == -1) { | |
408 | comm_close(client.fd()); | |
a55f4cea | 409 | return; |
983061ed | 410 | } |
62e76326 | 411 | |
a46d2c0e | 412 | cbdataInternalLock(this); /* ??? should be locked by the caller... */ |
a55f4cea | 413 | |
5dacdf3f | 414 | if (cbdataReferenceValid(this)) |
a46d2c0e | 415 | copyRead(server, ReadServer); |
2c202d66 | 416 | |
a46d2c0e | 417 | cbdataInternalUnlock(this); /* ??? */ |
983061ed | 418 | } |
419 | ||
b8d8561b | 420 | static void |
11007d4b | 421 | tunnelTimeout(int fd, void *data) |
983061ed | 422 | { |
fa34dd97 | 423 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 424 | debugs(26, 3, "tunnelTimeout: FD " << fd); |
425 | /* Temporary lock to protect our own feets (comm_close -> tunnelClientClosed -> Free) */ | |
426 | cbdataInternalLock(tunnelState); | |
a55f4cea | 427 | |
11007d4b | 428 | tunnelState->client.closeIfOpen(); |
429 | tunnelState->server.closeIfOpen(); | |
430 | cbdataInternalUnlock(tunnelState); | |
a46d2c0e | 431 | } |
62e76326 | 432 | |
a46d2c0e | 433 | void |
fa34dd97 | 434 | TunnelStateData::Connection::closeIfOpen() |
a46d2c0e | 435 | { |
436 | if (fd() != -1) | |
437 | comm_close(fd()); | |
438 | } | |
439 | ||
440 | void | |
fa34dd97 | 441 | TunnelStateData::copyRead(Connection &from, IOCB *completion) |
a46d2c0e | 442 | { |
443 | assert(from.len == 0); | |
444 | comm_read(from.fd(), from.buf, from.bytesWanted(1, SQUID_TCP_SO_RCVBUF), completion, this); | |
983061ed | 445 | } |
446 | ||
380963c2 | 447 | static void |
11007d4b | 448 | tunnelConnectTimeout(int fd, void *data) |
380963c2 | 449 | { |
fa34dd97 | 450 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 451 | HttpRequest *request = tunnelState->request; |
380963c2 | 452 | ErrorState *err = NULL; |
453 | ||
11007d4b | 454 | if (tunnelState->servers->_peer) |
455 | hierarchyNote(&tunnelState->request->hier, tunnelState->servers->code, | |
456 | tunnelState->servers->_peer->host); | |
380963c2 | 457 | else if (Config.onoff.log_ip_on_direct) |
11007d4b | 458 | hierarchyNote(&tunnelState->request->hier, tunnelState->servers->code, |
459 | fd_table[tunnelState->server.fd()].ipaddr); | |
380963c2 | 460 | else |
11007d4b | 461 | hierarchyNote(&tunnelState->request->hier, tunnelState->servers->code, |
462 | tunnelState->host); | |
380963c2 | 463 | |
464 | comm_close(fd); | |
465 | ||
2cc81f1f | 466 | err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request); |
380963c2 | 467 | |
11007d4b | 468 | *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE; |
380963c2 | 469 | |
470 | err->xerrno = ETIMEDOUT; | |
471 | ||
11007d4b | 472 | err->port = tunnelState->port; |
380963c2 | 473 | |
11007d4b | 474 | err->callback = tunnelErrorComplete; |
380963c2 | 475 | |
11007d4b | 476 | err->callback_data = tunnelState; |
380963c2 | 477 | |
11007d4b | 478 | errorSend(tunnelState->client.fd(), err); |
380963c2 | 479 | } |
a46d2c0e | 480 | |
c4b7a5a9 | 481 | static void |
11007d4b | 482 | tunnelConnectedWriteDone(int fd, char *buf, size_t size, comm_err_t flag, int xerrno, void *data) |
c4b7a5a9 | 483 | { |
fa34dd97 | 484 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
62e76326 | 485 | |
486 | if (flag != COMM_OK) { | |
11007d4b | 487 | tunnelErrorComplete(fd, data, 0); |
62e76326 | 488 | return; |
489 | } | |
490 | ||
11007d4b | 491 | if (cbdataReferenceValid(tunnelState)) { |
fa34dd97 | 492 | tunnelState->copyRead(tunnelState->server, TunnelStateData::ReadServer); |
493 | tunnelState->copyRead(tunnelState->client, TunnelStateData::ReadClient); | |
62e76326 | 494 | } |
c4b7a5a9 | 495 | } |
496 | ||
c4b7a5a9 | 497 | /* |
498 | * handle the write completion from a proxy request to an upstream proxy | |
499 | */ | |
500 | static void | |
11007d4b | 501 | tunnelProxyConnectedWriteDone(int fd, char *buf, size_t size, comm_err_t flag, int xerrno, void *data) |
c4b7a5a9 | 502 | { |
11007d4b | 503 | tunnelConnectedWriteDone(fd, buf, size, flag, xerrno, data); |
c4b7a5a9 | 504 | } |
505 | ||
b8d8561b | 506 | static void |
11007d4b | 507 | tunnelConnected(int fd, void *data) |
983061ed | 508 | { |
fa34dd97 | 509 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 510 | debugs(26, 3, "tunnelConnected: FD " << fd << " tunnelState=" << tunnelState); |
511 | *tunnelState->status_ptr = HTTP_OK; | |
512 | comm_write(tunnelState->client.fd(), conn_established, strlen(conn_established), | |
513 | tunnelConnectedWriteDone, tunnelState, NULL); | |
983061ed | 514 | } |
515 | ||
b8d8561b | 516 | static void |
11007d4b | 517 | tunnelErrorComplete(int fdnotused, void *data, size_t sizenotused) |
30a4f2a8 | 518 | { |
fa34dd97 | 519 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 520 | assert(tunnelState != NULL); |
521 | /* temporary lock to save our own feets (comm_close -> tunnelClientClosed -> Free) */ | |
522 | cbdataInternalLock(tunnelState); | |
62e76326 | 523 | |
11007d4b | 524 | if (tunnelState->client.fd() > -1) |
525 | comm_close(tunnelState->client.fd()); | |
62e76326 | 526 | |
11007d4b | 527 | if (tunnelState->server.fd() > -1) |
528 | comm_close(tunnelState->server.fd()); | |
380963c2 | 529 | |
11007d4b | 530 | cbdataInternalUnlock(tunnelState); |
30a4f2a8 | 531 | } |
532 | ||
983061ed | 533 | |
b8d8561b | 534 | static void |
11007d4b | 535 | tunnelConnectDone(int fdnotused, comm_err_t status, int xerrno, void *data) |
983061ed | 536 | { |
fa34dd97 | 537 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 538 | HttpRequest *request = tunnelState->request; |
9b312a19 | 539 | ErrorState *err = NULL; |
62e76326 | 540 | |
11007d4b | 541 | if (tunnelState->servers->_peer) |
542 | hierarchyNote(&tunnelState->request->hier, tunnelState->servers->code, | |
543 | tunnelState->servers->_peer->host); | |
890b0fa8 | 544 | else if (Config.onoff.log_ip_on_direct) |
11007d4b | 545 | hierarchyNote(&tunnelState->request->hier, tunnelState->servers->code, |
546 | fd_table[tunnelState->server.fd()].ipaddr); | |
890b0fa8 | 547 | else |
11007d4b | 548 | hierarchyNote(&tunnelState->request->hier, tunnelState->servers->code, |
549 | tunnelState->host); | |
62e76326 | 550 | |
edeb28fd | 551 | if (status == COMM_ERR_DNS) { |
11007d4b | 552 | debugs(26, 4, "tunnelConnect: Unknown host: " << tunnelState->host); |
2cc81f1f | 553 | err = errorCon(ERR_DNS_FAIL, HTTP_NOT_FOUND, request); |
11007d4b | 554 | *tunnelState->status_ptr = HTTP_NOT_FOUND; |
62e76326 | 555 | err->dnsserver_msg = xstrdup(dns_error_message); |
11007d4b | 556 | err->callback = tunnelErrorComplete; |
557 | err->callback_data = tunnelState; | |
558 | errorSend(tunnelState->client.fd(), err); | |
edeb28fd | 559 | } else if (status != COMM_OK) { |
2cc81f1f | 560 | err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request); |
11007d4b | 561 | *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE; |
f3400a93 | 562 | err->xerrno = xerrno; |
11007d4b | 563 | err->port = tunnelState->port; |
564 | err->callback = tunnelErrorComplete; | |
565 | err->callback_data = tunnelState; | |
566 | errorSend(tunnelState->client.fd(), err); | |
fe40a877 | 567 | } else { |
11007d4b | 568 | if (tunnelState->servers->_peer) |
569 | tunnelProxyConnected(tunnelState->server.fd(), tunnelState); | |
62e76326 | 570 | else { |
11007d4b | 571 | tunnelConnected(tunnelState->server.fd(), tunnelState); |
62e76326 | 572 | } |
573 | ||
11007d4b | 574 | commSetTimeout(tunnelState->server.fd(), |
62e76326 | 575 | Config.Timeout.read, |
11007d4b | 576 | tunnelTimeout, |
577 | tunnelState); | |
983061ed | 578 | } |
983061ed | 579 | } |
30a4f2a8 | 580 | |
770f051d | 581 | void |
47f6e231 | 582 | tunnelStart(ClientHttpRequest * http, int64_t * size_ptr, int *status_ptr) |
30a4f2a8 | 583 | { |
584 | /* Create state structure. */ | |
fa34dd97 | 585 | TunnelStateData *tunnelState = NULL; |
30a4f2a8 | 586 | int sock; |
9b312a19 | 587 | ErrorState *err = NULL; |
f1003989 | 588 | int answer; |
98242069 | 589 | int fd = http->getConn()->fd; |
190154cf | 590 | HttpRequest *request = http->request; |
d5964d58 | 591 | char *url = http->uri; |
f1003989 | 592 | /* |
a4b8110e | 593 | * client_addr == no_addr indicates this is an "internal" request |
594 | * from peer_digest.c, asn.c, netdb.c, etc and should always | |
595 | * be allowed. yuck, I know. | |
596 | */ | |
62e76326 | 597 | |
f1003989 | 598 | if (request->client_addr.s_addr != no_addr.s_addr) { |
62e76326 | 599 | /* |
600 | * Check if this host is allowed to fetch MISSES from us (miss_access) | |
601 | */ | |
602 | ACLChecklist ch; | |
603 | ch.src_addr = request->client_addr; | |
604 | ch.my_addr = request->my_addr; | |
605 | ch.my_port = request->my_port; | |
6dd9f4bd | 606 | ch.request = HTTPMSGLOCK(request); |
506768d9 | 607 | ch.accessList = cbdataReference(Config.accessList.miss); |
108d65b2 | 608 | /* cbdataReferenceDone() happens in either fastCheck() or ~ACLCheckList */ |
b448c119 | 609 | answer = ch.fastCheck(); |
62e76326 | 610 | |
611 | if (answer == 0) { | |
2cc81f1f | 612 | err = errorCon(ERR_FORWARDING_DENIED, HTTP_FORBIDDEN, request); |
62e76326 | 613 | *status_ptr = HTTP_FORBIDDEN; |
62e76326 | 614 | errorSend(fd, err); |
615 | return; | |
616 | } | |
f1003989 | 617 | } |
62e76326 | 618 | |
11007d4b | 619 | debugs(26, 3, "tunnelStart: '" << RequestMethodStr[request->method] << " " << url << "'"); |
83704487 | 620 | statCounter.server.all.requests++; |
621 | statCounter.server.other.requests++; | |
30a4f2a8 | 622 | /* Create socket. */ |
d6827718 | 623 | sock = comm_openex(SOCK_STREAM, |
bdb741f4 | 624 | IPPROTO_TCP, |
62e76326 | 625 | getOutgoingAddr(request), |
626 | 0, | |
627 | COMM_NONBLOCKING, | |
628 | getOutgoingTOS(request), | |
629 | url); | |
630 | ||
30a4f2a8 | 631 | if (sock == COMM_ERROR) { |
11007d4b | 632 | debugs(26, 4, "tunnelStart: Failed because we're out of sockets."); |
2cc81f1f | 633 | err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request); |
62e76326 | 634 | *status_ptr = HTTP_INTERNAL_SERVER_ERROR; |
635 | err->xerrno = errno; | |
62e76326 | 636 | errorSend(fd, err); |
637 | return; | |
30a4f2a8 | 638 | } |
62e76326 | 639 | |
fa34dd97 | 640 | tunnelState = new TunnelStateData; |
59715b38 | 641 | #if DELAY_POOLS |
62e76326 | 642 | |
11007d4b | 643 | tunnelState->server.setDelayId(DelayId::DelayClient(http)); |
59715b38 | 644 | #endif |
62e76326 | 645 | |
11007d4b | 646 | tunnelState->url = xstrdup(url); |
647 | tunnelState->request = HTTPMSGLOCK(request); | |
648 | tunnelState->server.size_ptr = size_ptr; | |
649 | tunnelState->status_ptr = status_ptr; | |
650 | tunnelState->client.fd(fd); | |
651 | tunnelState->server.fd(sock); | |
652 | comm_add_close_handler(tunnelState->server.fd(), | |
653 | tunnelServerClosed, | |
654 | tunnelState); | |
655 | comm_add_close_handler(tunnelState->client.fd(), | |
656 | tunnelClientClosed, | |
657 | tunnelState); | |
658 | commSetTimeout(tunnelState->client.fd(), | |
62e76326 | 659 | Config.Timeout.lifetime, |
11007d4b | 660 | tunnelTimeout, |
661 | tunnelState); | |
662 | commSetTimeout(tunnelState->server.fd(), | |
62e76326 | 663 | Config.Timeout.connect, |
11007d4b | 664 | tunnelConnectTimeout, |
665 | tunnelState); | |
b6c0e933 | 666 | peerSelect(request, |
62e76326 | 667 | NULL, |
11007d4b | 668 | tunnelPeerSelectComplete, |
669 | tunnelState); | |
adb78bd4 | 670 | /* |
671 | * Disable the client read handler until peer selection is complete | |
672 | * Take control away from client_side.c. | |
673 | */ | |
11007d4b | 674 | commSetSelect(tunnelState->client.fd(), COMM_SELECT_READ, NULL, NULL, 0); |
30a4f2a8 | 675 | } |
98ffb7e4 | 676 | |
b8d8561b | 677 | static void |
11007d4b | 678 | tunnelProxyConnected(int fd, void *data) |
98ffb7e4 | 679 | { |
fa34dd97 | 680 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
75faaa7a | 681 | HttpHeader hdr_out(hoRequest); |
e1e72f06 | 682 | Packer p; |
b4b5fd95 | 683 | http_state_flags flags; |
11007d4b | 684 | debugs(26, 3, "tunnelProxyConnected: FD " << fd << " tunnelState=" << tunnelState); |
b4b5fd95 | 685 | memset(&flags, '\0', sizeof(flags)); |
11007d4b | 686 | flags.proxying = tunnelState->request->flags.proxying; |
032785bf | 687 | MemBuf mb; |
2fe7eff9 | 688 | mb.init(); |
11007d4b | 689 | mb.Printf("CONNECT %s HTTP/1.0\r\n", tunnelState->url); |
690 | HttpStateData::httpBuildRequestHeader(tunnelState->request, | |
691 | tunnelState->request, | |
e5ee81f0 | 692 | NULL, /* StoreEntry */ |
693 | &hdr_out, | |
694 | flags); /* flags */ | |
e1e72f06 | 695 | packerToMemInit(&p, &mb); |
a9925b40 | 696 | hdr_out.packInto(&p); |
519e0948 | 697 | hdr_out.clean(); |
e1e72f06 | 698 | packerClean(&p); |
2fe7eff9 | 699 | mb.append("\r\n", 2); |
c4b7a5a9 | 700 | |
11007d4b | 701 | comm_write_mbuf(tunnelState->server.fd(), &mb, tunnelProxyConnectedWriteDone, tunnelState); |
702 | commSetTimeout(tunnelState->server.fd(), Config.Timeout.read, tunnelTimeout, tunnelState); | |
98ffb7e4 | 703 | } |
33ea9fff | 704 | |
33ea9fff | 705 | static void |
11007d4b | 706 | tunnelPeerSelectComplete(FwdServer * fs, void *data) |
33ea9fff | 707 | { |
fa34dd97 | 708 | TunnelStateData *tunnelState = (TunnelStateData *)data; |
11007d4b | 709 | HttpRequest *request = tunnelState->request; |
deb79f06 | 710 | peer *g = NULL; |
62e76326 | 711 | |
db1cd23c | 712 | if (fs == NULL) { |
62e76326 | 713 | ErrorState *err; |
2cc81f1f | 714 | err = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE, request); |
11007d4b | 715 | *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE; |
716 | err->callback = tunnelErrorComplete; | |
717 | err->callback_data = tunnelState; | |
718 | errorSend(tunnelState->client.fd(), err); | |
62e76326 | 719 | return; |
db1cd23c | 720 | } |
62e76326 | 721 | |
11007d4b | 722 | tunnelState->servers = fs; |
723 | tunnelState->host = fs->_peer ? fs->_peer->host : request->host; | |
62e76326 | 724 | |
29b8d8d6 | 725 | if (fs->_peer == NULL) { |
11007d4b | 726 | tunnelState->port = request->port; |
29b8d8d6 | 727 | } else if (fs->_peer->http_port != 0) { |
11007d4b | 728 | tunnelState->port = fs->_peer->http_port; |
29b8d8d6 | 729 | } else if ((g = peerFindByName(fs->_peer->host))) { |
11007d4b | 730 | tunnelState->port = g->http_port; |
33ea9fff | 731 | } else { |
11007d4b | 732 | tunnelState->port = CACHE_HTTP_PORT; |
33ea9fff | 733 | } |
62e76326 | 734 | |
29b8d8d6 | 735 | if (fs->_peer) { |
11007d4b | 736 | tunnelState->request->peer_login = fs->_peer->login; |
737 | tunnelState->request->flags.proxying = 1; | |
1f38f50a | 738 | } else { |
11007d4b | 739 | tunnelState->request->peer_login = NULL; |
740 | tunnelState->request->flags.proxying = 0; | |
1f38f50a | 741 | } |
62e76326 | 742 | |
59715b38 | 743 | #if DELAY_POOLS |
11007d4b | 744 | /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */ |
a46d2c0e | 745 | if (g && g->options.no_delay) |
11007d4b | 746 | tunnelState->server.setDelayId(DelayId()); |
62e76326 | 747 | |
59715b38 | 748 | #endif |
a46d2c0e | 749 | |
11007d4b | 750 | commConnectStart(tunnelState->server.fd(), |
751 | tunnelState->host, | |
752 | tunnelState->port, | |
753 | tunnelConnectDone, | |
754 | tunnelState); | |
33ea9fff | 755 | } |
a46d2c0e | 756 | |
fa34dd97 | 757 | CBDATA_CLASS_INIT(TunnelStateData); |
a46d2c0e | 758 | |
759 | void * | |
fa34dd97 | 760 | TunnelStateData::operator new (size_t) |
a46d2c0e | 761 | { |
fa34dd97 | 762 | CBDATA_INIT_TYPE(TunnelStateData); |
763 | TunnelStateData *result = cbdataAlloc(TunnelStateData); | |
a46d2c0e | 764 | return result; |
765 | } | |
766 | ||
767 | void | |
fa34dd97 | 768 | TunnelStateData::operator delete (void *address) |
a46d2c0e | 769 | { |
fa34dd97 | 770 | TunnelStateData *t = static_cast<TunnelStateData *>(address); |
a46d2c0e | 771 | cbdataFree(t); |
772 | } | |
773 | ||
a46d2c0e | 774 | void |
fa34dd97 | 775 | TunnelStateData::Connection::fd(int const newFD) |
a46d2c0e | 776 | { |
777 | fd_ = newFD; | |
778 | } | |
779 | ||
780 | bool | |
fa34dd97 | 781 | TunnelStateData::noConnections() const |
a46d2c0e | 782 | { |
783 | return (server.fd() == -1) && (client.fd() == -1); | |
784 | } | |
785 | ||
786 | #if DELAY_POOLS | |
787 | void | |
fa34dd97 | 788 | TunnelStateData::Connection::setDelayId(DelayId const &newDelay) |
a46d2c0e | 789 | { |
790 | delayId = newDelay; | |
791 | } | |
792 | ||
793 | #endif |