]> git.ipfire.org Git - thirdparty/squid.git/blob - src/tunnel.cc
MemBuf implements Packable interface
[thirdparty/squid.git] / src / tunnel.cc
1 /*
2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /* DEBUG: section 26 Secure Sockets Layer Proxy */
10
11 #include "squid.h"
12 #include "acl/FilledChecklist.h"
13 #include "base/CbcPointer.h"
14 #include "CachePeer.h"
15 #include "cbdata.h"
16 #include "client_side.h"
17 #include "client_side_request.h"
18 #include "comm.h"
19 #include "comm/Connection.h"
20 #include "comm/ConnOpener.h"
21 #include "comm/Read.h"
22 #include "comm/Write.h"
23 #include "errorpage.h"
24 #include "fde.h"
25 #include "FwdState.h"
26 #include "globals.h"
27 #include "http.h"
28 #include "HttpRequest.h"
29 #include "HttpStateFlags.h"
30 #include "ip/QosConfig.h"
31 #include "LogTags.h"
32 #include "MemBuf.h"
33 #include "PeerSelectState.h"
34 #include "SBuf.h"
35 #include "SquidConfig.h"
36 #include "SquidTime.h"
37 #include "StatCounters.h"
38 #if USE_OPENSSL
39 #include "ssl/bio.h"
40 #include "ssl/PeerConnector.h"
41 #include "ssl/ServerBump.h"
42 #endif
43 #include "tools.h"
44 #if USE_DELAY_POOLS
45 #include "DelayId.h"
46 #endif
47
48 #include <climits>
49 #include <cerrno>
50
51 /**
52 * TunnelStateData is the state engine performing the tasks for
53 * setup of a TCP tunnel from an existing open client FD to a server
54 * then shuffling binary data between the resulting FD pair.
55 */
56 /*
57 * TODO 1: implement a read/write API on ConnStateData to send/receive blocks
58 * of pre-formatted data. Then we can use that as the client side of the tunnel
59 * instead of re-implementing it here and occasionally getting the ConnStateData
60 * read/write state wrong.
61 *
62 * TODO 2: then convert this into a AsyncJob, possibly a child of 'Server'
63 */
64 class TunnelStateData
65 {
66 CBDATA_CLASS(TunnelStateData);
67
68 public:
69 TunnelStateData();
70 ~TunnelStateData();
71 TunnelStateData(const TunnelStateData &); // do not implement
72 TunnelStateData &operator =(const TunnelStateData &); // do not implement
73
74 class Connection;
75 static void ReadClient(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data);
76 static void ReadServer(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data);
77 static void WriteClientDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data);
78 static void WriteServerDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data);
79
80 /// Starts reading peer response to our CONNECT request.
81 void readConnectResponse();
82
83 /// Called when we may be done handling a CONNECT exchange with the peer.
84 void connectExchangeCheckpoint();
85
86 bool noConnections() const;
87 char *url;
88 CbcPointer<ClientHttpRequest> http;
89 HttpRequest::Pointer request;
90 AccessLogEntryPointer al;
91 Comm::ConnectionList serverDestinations;
92
93 const char * getHost() const {
94 return (server.conn != NULL && server.conn->getPeer() ? server.conn->getPeer()->host : request->GetHost());
95 };
96
97 /// Whether we are writing a CONNECT request to a peer.
98 bool waitingForConnectRequest() const { return connectReqWriting; }
99 /// Whether we are reading a CONNECT response from a peer.
100 bool waitingForConnectResponse() const { return connectRespBuf; }
101 /// Whether we are waiting for the CONNECT request/response exchange with the peer.
102 bool waitingForConnectExchange() const { return waitingForConnectRequest() || waitingForConnectResponse(); }
103
104 /// Whether the client sent a CONNECT request to us.
105 bool clientExpectsConnectResponse() const {
106 #if USE_OPENSSL
107 // We are bumping and we had already send "OK CONNECTED"
108 if (http.valid() && http->getConn() && http->getConn()->serverBump() && http->getConn()->serverBump()->step > Ssl::bumpStep1)
109 return false;
110 #endif
111 return !(request != NULL &&
112 (request->flags.interceptTproxy || request->flags.intercepted));
113 }
114
115 class Connection
116 {
117
118 public:
119 Connection() : len (0), buf ((char *)xmalloc(SQUID_TCP_SO_RCVBUF)), size_ptr(NULL), delayedLoops(0),
120 readPending(NULL), readPendingFunc(NULL) {}
121
122 ~Connection();
123
124 int bytesWanted(int lower=0, int upper = INT_MAX) const;
125 void bytesIn(int const &);
126 #if USE_DELAY_POOLS
127
128 void setDelayId(DelayId const &);
129 #endif
130
131 void error(int const xerrno);
132 int debugLevelForError(int const xerrno) const;
133 /// handles a non-I/O error associated with this Connection
134 void logicError(const char *errMsg);
135 void closeIfOpen();
136 void dataSent (size_t amount);
137 int len;
138 char *buf;
139 int64_t *size_ptr; /* pointer to size in an ConnStateData for logging */
140
141 Comm::ConnectionPointer conn; ///< The currently connected connection.
142 uint8_t delayedLoops; ///< how many times a read on this connection has been postponed.
143
144 // XXX: make these an AsyncCall when event API can handle them
145 TunnelStateData *readPending;
146 EVH *readPendingFunc;
147 private:
148 #if USE_DELAY_POOLS
149
150 DelayId delayId;
151 #endif
152
153 };
154
155 Connection client, server;
156 int *status_ptr; ///< pointer for logging HTTP status
157 LogTags *logTag_ptr; ///< pointer for logging Squid processing code
158 MemBuf *connectRespBuf; ///< accumulates peer CONNECT response when we need it
159 bool connectReqWriting; ///< whether we are writing a CONNECT request to a peer
160 SBuf preReadClientData;
161
162 void copyRead(Connection &from, IOCB *completion);
163
164 /// continue to set up connection to a peer, going async for SSL peers
165 void connectToPeer();
166
167 private:
168 #if USE_OPENSSL
169 /// Gives PeerConnector access to Answer in the TunnelStateData callback dialer.
170 class MyAnswerDialer: public CallDialer, public Ssl::PeerConnector::CbDialer
171 {
172 public:
173 typedef void (TunnelStateData::*Method)(Ssl::PeerConnectorAnswer &);
174
175 MyAnswerDialer(Method method, TunnelStateData *tunnel):
176 method_(method), tunnel_(tunnel), answer_() {}
177
178 /* CallDialer API */
179 virtual bool canDial(AsyncCall &call) { return tunnel_.valid(); }
180 void dial(AsyncCall &call) { ((&(*tunnel_))->*method_)(answer_); }
181 virtual void print(std::ostream &os) const {
182 os << '(' << tunnel_.get() << ", " << answer_ << ')';
183 }
184
185 /* Ssl::PeerConnector::CbDialer API */
186 virtual Ssl::PeerConnectorAnswer &answer() { return answer_; }
187
188 private:
189 Method method_;
190 CbcPointer<TunnelStateData> tunnel_;
191 Ssl::PeerConnectorAnswer answer_;
192 };
193
194 void connectedToPeer(Ssl::PeerConnectorAnswer &answer);
195 #endif
196
197 public:
198 bool keepGoingAfterRead(size_t len, Comm::Flag errcode, int xerrno, Connection &from, Connection &to);
199 void copy(size_t len, Connection &from, Connection &to, IOCB *);
200 void handleConnectResponse(const size_t chunkSize);
201 void readServer(char *buf, size_t len, Comm::Flag errcode, int xerrno);
202 void readClient(char *buf, size_t len, Comm::Flag errcode, int xerrno);
203 void writeClientDone(char *buf, size_t len, Comm::Flag flag, int xerrno);
204 void writeServerDone(char *buf, size_t len, Comm::Flag flag, int xerrno);
205
206 static void ReadConnectResponseDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data);
207 void readConnectResponseDone(char *buf, size_t len, Comm::Flag errcode, int xerrno);
208 void copyClientBytes();
209 };
210
211 static const char *const conn_established = "HTTP/1.1 200 Connection established\r\n\r\n";
212
213 static CNCB tunnelConnectDone;
214 static ERCB tunnelErrorComplete;
215 static CLCB tunnelServerClosed;
216 static CLCB tunnelClientClosed;
217 static CTCB tunnelTimeout;
218 static PSC tunnelPeerSelectComplete;
219 static EVH tunnelDelayedClientRead;
220 static EVH tunnelDelayedServerRead;
221 static void tunnelConnected(const Comm::ConnectionPointer &server, void *);
222 static void tunnelRelayConnectRequest(const Comm::ConnectionPointer &server, void *);
223
224 static void
225 tunnelServerClosed(const CommCloseCbParams &params)
226 {
227 TunnelStateData *tunnelState = (TunnelStateData *)params.data;
228 debugs(26, 3, HERE << tunnelState->server.conn);
229 tunnelState->server.conn = NULL;
230
231 if (tunnelState->request != NULL)
232 tunnelState->request->hier.stopPeerClock(false);
233
234 if (tunnelState->noConnections()) {
235 delete tunnelState;
236 return;
237 }
238
239 if (!tunnelState->server.len) {
240 tunnelState->client.conn->close();
241 return;
242 }
243 }
244
245 static void
246 tunnelClientClosed(const CommCloseCbParams &params)
247 {
248 TunnelStateData *tunnelState = (TunnelStateData *)params.data;
249 debugs(26, 3, HERE << tunnelState->client.conn);
250 tunnelState->client.conn = NULL;
251
252 if (tunnelState->noConnections()) {
253 delete tunnelState;
254 return;
255 }
256
257 if (!tunnelState->client.len) {
258 tunnelState->server.conn->close();
259 return;
260 }
261 }
262
263 TunnelStateData::TunnelStateData() :
264 url(NULL),
265 http(),
266 request(NULL),
267 status_ptr(NULL),
268 logTag_ptr(NULL),
269 connectRespBuf(NULL),
270 connectReqWriting(false)
271 {
272 debugs(26, 3, "TunnelStateData constructed this=" << this);
273 client.readPendingFunc = &tunnelDelayedClientRead;
274 server.readPendingFunc = &tunnelDelayedServerRead;
275 }
276
277 TunnelStateData::~TunnelStateData()
278 {
279 debugs(26, 3, "TunnelStateData destructed this=" << this);
280 assert(noConnections());
281 xfree(url);
282 serverDestinations.clear();
283 delete connectRespBuf;
284 }
285
286 TunnelStateData::Connection::~Connection()
287 {
288 if (readPending)
289 eventDelete(readPendingFunc, readPending);
290
291 safe_free(buf);
292 }
293
294 int
295 TunnelStateData::Connection::bytesWanted(int lowerbound, int upperbound) const
296 {
297 #if USE_DELAY_POOLS
298 return delayId.bytesWanted(lowerbound, upperbound);
299 #else
300
301 return upperbound;
302 #endif
303 }
304
305 void
306 TunnelStateData::Connection::bytesIn(int const &count)
307 {
308 debugs(26, 3, HERE << "len=" << len << " + count=" << count);
309 #if USE_DELAY_POOLS
310 delayId.bytesIn(count);
311 #endif
312
313 len += count;
314 }
315
316 int
317 TunnelStateData::Connection::debugLevelForError(int const xerrno) const
318 {
319 #ifdef ECONNRESET
320
321 if (xerrno == ECONNRESET)
322 return 2;
323
324 #endif
325
326 if (ignoreErrno(xerrno))
327 return 3;
328
329 return 1;
330 }
331
332 /* Read from server side and queue it for writing to the client */
333 void
334 TunnelStateData::ReadServer(const Comm::ConnectionPointer &c, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data)
335 {
336 TunnelStateData *tunnelState = (TunnelStateData *)data;
337 assert(cbdataReferenceValid(tunnelState));
338 debugs(26, 3, HERE << c);
339
340 tunnelState->readServer(buf, len, errcode, xerrno);
341 }
342
343 void
344 TunnelStateData::readServer(char *, size_t len, Comm::Flag errcode, int xerrno)
345 {
346 debugs(26, 3, HERE << server.conn << ", read " << len << " bytes, err=" << errcode);
347 server.delayedLoops=0;
348
349 /*
350 * Bail out early on Comm::ERR_CLOSING
351 * - close handlers will tidy up for us
352 */
353
354 if (errcode == Comm::ERR_CLOSING)
355 return;
356
357 if (len > 0) {
358 server.bytesIn(len);
359 kb_incr(&(statCounter.server.all.kbytes_in), len);
360 kb_incr(&(statCounter.server.other.kbytes_in), len);
361 }
362
363 if (keepGoingAfterRead(len, errcode, xerrno, server, client))
364 copy(len, server, client, WriteClientDone);
365 }
366
367 /// Called when we read [a part of] CONNECT response from the peer
368 void
369 TunnelStateData::readConnectResponseDone(char *, size_t len, Comm::Flag errcode, int xerrno)
370 {
371 debugs(26, 3, server.conn << ", read " << len << " bytes, err=" << errcode);
372 assert(waitingForConnectResponse());
373
374 if (errcode == Comm::ERR_CLOSING)
375 return;
376
377 if (len > 0) {
378 connectRespBuf->appended(len);
379 server.bytesIn(len);
380 kb_incr(&(statCounter.server.all.kbytes_in), len);
381 kb_incr(&(statCounter.server.other.kbytes_in), len);
382 }
383
384 if (keepGoingAfterRead(len, errcode, xerrno, server, client))
385 handleConnectResponse(len);
386 }
387
388 /* Read from client side and queue it for writing to the server */
389 void
390 TunnelStateData::ReadConnectResponseDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data)
391 {
392 TunnelStateData *tunnelState = (TunnelStateData *)data;
393 assert (cbdataReferenceValid (tunnelState));
394
395 tunnelState->readConnectResponseDone(buf, len, errcode, xerrno);
396 }
397
398 /// Parses [possibly incomplete] CONNECT response and reacts to it.
399 /// If the tunnel is being closed or more response data is needed, returns false.
400 /// Otherwise, the caller should handle the remaining read data, if any.
401 void
402 TunnelStateData::handleConnectResponse(const size_t chunkSize)
403 {
404 assert(waitingForConnectResponse());
405
406 // Ideally, client and server should use MemBuf or better, but current code
407 // never accumulates more than one read when shoveling data (XXX) so it does
408 // not need to deal with MemBuf complexity. To keep it simple, we use a
409 // dedicated MemBuf for accumulating CONNECT responses. TODO: When shoveling
410 // is optimized, reuse server.buf for CONNEC response accumulation instead.
411
412 /* mimic the basic parts of HttpStateData::processReplyHeader() */
413 HttpReply rep;
414 Http::StatusCode parseErr = Http::scNone;
415 const bool eof = !chunkSize;
416 const bool parsed = rep.parse(connectRespBuf, eof, &parseErr);
417 if (!parsed) {
418 if (parseErr > 0) { // unrecoverable parsing error
419 server.logicError("malformed CONNECT response from peer");
420 return;
421 }
422
423 // need more data
424 assert(!eof);
425 assert(!parseErr);
426
427 if (!connectRespBuf->hasSpace()) {
428 server.logicError("huge CONNECT response from peer");
429 return;
430 }
431
432 // keep reading
433 readConnectResponse();
434 return;
435 }
436
437 // CONNECT response was successfully parsed
438 *status_ptr = rep.sline.status();
439
440 // bail if we did not get an HTTP 200 (Connection Established) response
441 if (rep.sline.status() != Http::scOkay) {
442 server.logicError("unsupported CONNECT response status code");
443 return;
444 }
445
446 if (rep.hdr_sz < connectRespBuf->contentSize()) {
447 // preserve bytes that the server already sent after the CONNECT response
448 server.len = connectRespBuf->contentSize() - rep.hdr_sz;
449 memcpy(server.buf, connectRespBuf->content()+rep.hdr_sz, server.len);
450 } else {
451 // reset; delay pools were using this field to throttle CONNECT response
452 server.len = 0;
453 }
454
455 delete connectRespBuf;
456 connectRespBuf = NULL;
457 connectExchangeCheckpoint();
458 }
459
460 void
461 TunnelStateData::Connection::logicError(const char *errMsg)
462 {
463 debugs(50, 3, conn << " closing on error: " << errMsg);
464 conn->close();
465 }
466
467 void
468 TunnelStateData::Connection::error(int const xerrno)
469 {
470 /* XXX fixme xstrerror and xerrno... */
471 errno = xerrno;
472
473 debugs(50, debugLevelForError(xerrno), HERE << conn << ": read/write failure: " << xstrerror());
474
475 if (!ignoreErrno(xerrno))
476 conn->close();
477 }
478
479 /* Read from client side and queue it for writing to the server */
480 void
481 TunnelStateData::ReadClient(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data)
482 {
483 TunnelStateData *tunnelState = (TunnelStateData *)data;
484 assert (cbdataReferenceValid (tunnelState));
485
486 tunnelState->readClient(buf, len, errcode, xerrno);
487 }
488
489 void
490 TunnelStateData::readClient(char *, size_t len, Comm::Flag errcode, int xerrno)
491 {
492 debugs(26, 3, HERE << client.conn << ", read " << len << " bytes, err=" << errcode);
493 client.delayedLoops=0;
494
495 /*
496 * Bail out early on Comm::ERR_CLOSING
497 * - close handlers will tidy up for us
498 */
499
500 if (errcode == Comm::ERR_CLOSING)
501 return;
502
503 if (len > 0) {
504 client.bytesIn(len);
505 kb_incr(&(statCounter.client_http.kbytes_in), len);
506 }
507
508 if (keepGoingAfterRead(len, errcode, xerrno, client, server))
509 copy(len, client, server, WriteServerDone);
510 }
511
512 /// Updates state after reading from client or server.
513 /// Returns whether the caller should use the data just read.
514 bool
515 TunnelStateData::keepGoingAfterRead(size_t len, Comm::Flag errcode, int xerrno, Connection &from, Connection &to)
516 {
517 debugs(26, 3, HERE << "from={" << from.conn << "}, to={" << to.conn << "}");
518
519 /* I think this is to prevent free-while-in-a-callback behaviour
520 * - RBC 20030229
521 * from.conn->close() / to.conn->close() done here trigger close callbacks which may free TunnelStateData
522 */
523 const CbcPointer<TunnelStateData> safetyLock(this);
524
525 /* Bump the source connection read timeout on any activity */
526 if (Comm::IsConnOpen(from.conn)) {
527 AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout",
528 CommTimeoutCbPtrFun(tunnelTimeout, this));
529 commSetConnTimeout(from.conn, Config.Timeout.read, timeoutCall);
530 }
531
532 /* Bump the dest connection read timeout on any activity */
533 /* see Bug 3659: tunnels can be weird, with very long one-way transfers */
534 if (Comm::IsConnOpen(to.conn)) {
535 AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout",
536 CommTimeoutCbPtrFun(tunnelTimeout, this));
537 commSetConnTimeout(to.conn, Config.Timeout.read, timeoutCall);
538 }
539
540 if (errcode)
541 from.error (xerrno);
542 else if (len == 0 || !Comm::IsConnOpen(to.conn)) {
543 debugs(26, 3, HERE << "Nothing to write or client gone. Terminate the tunnel.");
544 from.conn->close();
545
546 /* Only close the remote end if we've finished queueing data to it */
547 if (from.len == 0 && Comm::IsConnOpen(to.conn) ) {
548 to.conn->close();
549 }
550 } else if (cbdataReferenceValid(this)) {
551 return true;
552 }
553
554 return false;
555 }
556
557 void
558 TunnelStateData::copy(size_t len, Connection &from, Connection &to, IOCB *completion)
559 {
560 debugs(26, 3, HERE << "Schedule Write");
561 AsyncCall::Pointer call = commCbCall(5,5, "TunnelBlindCopyWriteHandler",
562 CommIoCbPtrFun(completion, this));
563 Comm::Write(to.conn, from.buf, len, call, NULL);
564 }
565
566 /* Writes data from the client buffer to the server side */
567 void
568 TunnelStateData::WriteServerDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data)
569 {
570 TunnelStateData *tunnelState = (TunnelStateData *)data;
571 assert (cbdataReferenceValid (tunnelState));
572
573 tunnelState->writeServerDone(buf, len, flag, xerrno);
574 }
575
576 void
577 TunnelStateData::writeServerDone(char *, size_t len, Comm::Flag flag, int xerrno)
578 {
579 debugs(26, 3, HERE << server.conn << ", " << len << " bytes written, flag=" << flag);
580
581 /* Error? */
582 if (flag != Comm::OK) {
583 if (flag != Comm::ERR_CLOSING) {
584 debugs(26, 4, HERE << "calling TunnelStateData::server.error(" << xerrno <<")");
585 server.error(xerrno); // may call comm_close
586 }
587 return;
588 }
589
590 /* EOF? */
591 if (len == 0) {
592 debugs(26, 4, HERE << "No read input. Closing server connection.");
593 server.conn->close();
594 return;
595 }
596
597 /* Valid data */
598 kb_incr(&(statCounter.server.all.kbytes_out), len);
599 kb_incr(&(statCounter.server.other.kbytes_out), len);
600 client.dataSent(len);
601
602 /* If the other end has closed, so should we */
603 if (!Comm::IsConnOpen(client.conn)) {
604 debugs(26, 4, HERE << "Client gone away. Shutting down server connection.");
605 server.conn->close();
606 return;
607 }
608
609 const CbcPointer<TunnelStateData> safetyLock(this); /* ??? should be locked by the caller... */
610
611 if (cbdataReferenceValid(this))
612 copyClientBytes();
613 }
614
615 /* Writes data from the server buffer to the client side */
616 void
617 TunnelStateData::WriteClientDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data)
618 {
619 TunnelStateData *tunnelState = (TunnelStateData *)data;
620 assert (cbdataReferenceValid (tunnelState));
621
622 tunnelState->writeClientDone(buf, len, flag, xerrno);
623 }
624
625 void
626 TunnelStateData::Connection::dataSent(size_t amount)
627 {
628 debugs(26, 3, HERE << "len=" << len << " - amount=" << amount);
629 assert(amount == (size_t)len);
630 len =0;
631 /* increment total object size */
632
633 if (size_ptr)
634 *size_ptr += amount;
635 }
636
637 void
638 TunnelStateData::writeClientDone(char *, size_t len, Comm::Flag flag, int xerrno)
639 {
640 debugs(26, 3, HERE << client.conn << ", " << len << " bytes written, flag=" << flag);
641
642 /* Error? */
643 if (flag != Comm::OK) {
644 if (flag != Comm::ERR_CLOSING) {
645 debugs(26, 4, HERE << "Closing client connection due to comm flags.");
646 client.error(xerrno); // may call comm_close
647 }
648 return;
649 }
650
651 /* EOF? */
652 if (len == 0) {
653 debugs(26, 4, HERE << "Closing client connection due to 0 byte read.");
654 client.conn->close();
655 return;
656 }
657
658 /* Valid data */
659 kb_incr(&(statCounter.client_http.kbytes_out), len);
660 server.dataSent(len);
661
662 /* If the other end has closed, so should we */
663 if (!Comm::IsConnOpen(server.conn)) {
664 debugs(26, 4, HERE << "Server has gone away. Terminating client connection.");
665 client.conn->close();
666 return;
667 }
668
669 CbcPointer<TunnelStateData> safetyLock(this); /* ??? should be locked by the caller... */
670
671 if (cbdataReferenceValid(this))
672 copyRead(server, ReadServer);
673 }
674
675 static void
676 tunnelTimeout(const CommTimeoutCbParams &io)
677 {
678 TunnelStateData *tunnelState = static_cast<TunnelStateData *>(io.data);
679 debugs(26, 3, HERE << io.conn);
680 /* Temporary lock to protect our own feets (comm_close -> tunnelClientClosed -> Free) */
681 CbcPointer<TunnelStateData> safetyLock(tunnelState);
682
683 tunnelState->client.closeIfOpen();
684 tunnelState->server.closeIfOpen();
685 }
686
687 void
688 TunnelStateData::Connection::closeIfOpen()
689 {
690 if (Comm::IsConnOpen(conn))
691 conn->close();
692 }
693
694 static void
695 tunnelDelayedClientRead(void *data)
696 {
697 if (!data)
698 return;
699 TunnelStateData *tunnel = static_cast<TunnelStateData*>(data);
700 if (!tunnel)
701 return;
702 tunnel->client.readPending = NULL;
703 static uint64_t counter=0;
704 debugs(26, 7, "Client read(2) delayed " << ++counter << " times");
705 tunnel->copyRead(tunnel->client, TunnelStateData::ReadClient);
706 }
707
708 static void
709 tunnelDelayedServerRead(void *data)
710 {
711 if (!data)
712 return;
713 TunnelStateData *tunnel = static_cast<TunnelStateData*>(data);
714 if (!tunnel)
715 return;
716 tunnel->server.readPending = NULL;
717 static uint64_t counter=0;
718 debugs(26, 7, "Server read(2) delayed " << ++counter << " times");
719 tunnel->copyRead(tunnel->server, TunnelStateData::ReadServer);
720 }
721
722 void
723 TunnelStateData::copyRead(Connection &from, IOCB *completion)
724 {
725 assert(from.len == 0);
726 // If only the minimum permitted read size is going to be attempted
727 // then we schedule an event to try again in a few I/O cycles.
728 // Allow at least 1 byte to be read every (0.3*10) seconds.
729 int bw = from.bytesWanted(1, SQUID_TCP_SO_RCVBUF);
730 if (bw == 1 && ++from.delayedLoops < 10) {
731 from.readPending = this;
732 eventAdd("tunnelDelayedServerRead", from.readPendingFunc, from.readPending, 0.3, true);
733 return;
734 }
735
736 AsyncCall::Pointer call = commCbCall(5,4, "TunnelBlindCopyReadHandler",
737 CommIoCbPtrFun(completion, this));
738 comm_read(from.conn, from.buf, bw, call);
739 }
740
741 void
742 TunnelStateData::readConnectResponse()
743 {
744 assert(waitingForConnectResponse());
745
746 AsyncCall::Pointer call = commCbCall(5,4, "readConnectResponseDone",
747 CommIoCbPtrFun(ReadConnectResponseDone, this));
748 comm_read(server.conn, connectRespBuf->space(),
749 server.bytesWanted(1, connectRespBuf->spaceSize()), call);
750 }
751
752 void
753 TunnelStateData::copyClientBytes()
754 {
755 if (preReadClientData.length()) {
756 size_t copyBytes = preReadClientData.length() > SQUID_TCP_SO_RCVBUF ? SQUID_TCP_SO_RCVBUF : preReadClientData.length();
757 memcpy(client.buf, preReadClientData.rawContent(), copyBytes);
758 preReadClientData.consume(copyBytes);
759 client.bytesIn(copyBytes);
760 if (keepGoingAfterRead(copyBytes, Comm::OK, 0, client, server))
761 copy(copyBytes, client, server, TunnelStateData::WriteServerDone);
762 } else
763 copyRead(client, ReadClient);
764 }
765
766 /**
767 * Set the HTTP status for this request and sets the read handlers for client
768 * and server side connections.
769 */
770 static void
771 tunnelStartShoveling(TunnelStateData *tunnelState)
772 {
773 assert(!tunnelState->waitingForConnectExchange());
774 *tunnelState->status_ptr = Http::scOkay;
775 if (tunnelState->logTag_ptr)
776 *tunnelState->logTag_ptr = LOG_TCP_TUNNEL;
777 if (cbdataReferenceValid(tunnelState)) {
778
779 // Shovel any payload already pushed into reply buffer by the server response
780 if (!tunnelState->server.len)
781 tunnelState->copyRead(tunnelState->server, TunnelStateData::ReadServer);
782 else {
783 debugs(26, DBG_DATA, "Tunnel server PUSH Payload: \n" << Raw("", tunnelState->server.buf, tunnelState->server.len) << "\n----------");
784 tunnelState->copy(tunnelState->server.len, tunnelState->server, tunnelState->client, TunnelStateData::WriteClientDone);
785 }
786
787 if (tunnelState->http.valid() && tunnelState->http->getConn() && !tunnelState->http->getConn()->in.buf.isEmpty()) {
788 struct ConnStateData::In *in = &tunnelState->http->getConn()->in;
789 debugs(26, DBG_DATA, "Tunnel client PUSH Payload: \n" << in->buf << "\n----------");
790 tunnelState->preReadClientData.append(in->buf);
791 in->buf.consume(); // ConnStateData buffer accounting after the shuffle.
792 }
793 tunnelState->copyClientBytes();
794 }
795 }
796
797 /**
798 * All the pieces we need to write to client and/or server connection
799 * have been written.
800 * Call the tunnelStartShoveling to start the blind pump.
801 */
802 static void
803 tunnelConnectedWriteDone(const Comm::ConnectionPointer &conn, char *, size_t, Comm::Flag flag, int, void *data)
804 {
805 TunnelStateData *tunnelState = (TunnelStateData *)data;
806 debugs(26, 3, HERE << conn << ", flag=" << flag);
807
808 if (flag != Comm::OK) {
809 *tunnelState->status_ptr = Http::scInternalServerError;
810 tunnelErrorComplete(conn->fd, data, 0);
811 return;
812 }
813
814 tunnelStartShoveling(tunnelState);
815 }
816
817 /// Called when we are done writing CONNECT request to a peer.
818 static void
819 tunnelConnectReqWriteDone(const Comm::ConnectionPointer &conn, char *, size_t, Comm::Flag flag, int, void *data)
820 {
821 TunnelStateData *tunnelState = (TunnelStateData *)data;
822 debugs(26, 3, conn << ", flag=" << flag);
823 assert(tunnelState->waitingForConnectRequest());
824
825 if (flag != Comm::OK) {
826 *tunnelState->status_ptr = Http::scInternalServerError;
827 tunnelErrorComplete(conn->fd, data, 0);
828 return;
829 }
830
831 tunnelState->connectReqWriting = false;
832 tunnelState->connectExchangeCheckpoint();
833 }
834
835 void
836 TunnelStateData::connectExchangeCheckpoint()
837 {
838 if (waitingForConnectResponse()) {
839 debugs(26, 5, "still reading CONNECT response on " << server.conn);
840 } else if (waitingForConnectRequest()) {
841 debugs(26, 5, "still writing CONNECT request on " << server.conn);
842 } else {
843 assert(!waitingForConnectExchange());
844 debugs(26, 3, "done with CONNECT exchange on " << server.conn);
845 tunnelConnected(server.conn, this);
846 }
847 }
848
849 /*
850 * handle the write completion from a proxy request to an upstream origin
851 */
852 static void
853 tunnelConnected(const Comm::ConnectionPointer &server, void *data)
854 {
855 TunnelStateData *tunnelState = (TunnelStateData *)data;
856 debugs(26, 3, HERE << server << ", tunnelState=" << tunnelState);
857
858 if (!tunnelState->clientExpectsConnectResponse())
859 tunnelStartShoveling(tunnelState); // ssl-bumped connection, be quiet
860 else {
861 AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone",
862 CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState));
863 Comm::Write(tunnelState->client.conn, conn_established, strlen(conn_established), call, NULL);
864 }
865 }
866
867 static void
868 tunnelErrorComplete(int fd/*const Comm::ConnectionPointer &*/, void *data, size_t)
869 {
870 TunnelStateData *tunnelState = (TunnelStateData *)data;
871 debugs(26, 3, HERE << "FD " << fd);
872 assert(tunnelState != NULL);
873 /* temporary lock to save our own feets (comm_close -> tunnelClientClosed -> Free) */
874 CbcPointer<TunnelStateData> safetyLock(tunnelState);
875
876 if (Comm::IsConnOpen(tunnelState->client.conn))
877 tunnelState->client.conn->close();
878
879 if (Comm::IsConnOpen(tunnelState->server.conn))
880 tunnelState->server.conn->close();
881 }
882
883 static void
884 tunnelConnectDone(const Comm::ConnectionPointer &conn, Comm::Flag status, int xerrno, void *data)
885 {
886 TunnelStateData *tunnelState = (TunnelStateData *)data;
887
888 if (status != Comm::OK) {
889 debugs(26, 4, HERE << conn << ", comm failure recovery.");
890 /* At this point only the TCP handshake has failed. no data has been passed.
891 * we are allowed to re-try the TCP-level connection to alternate IPs for CONNECT.
892 */
893 tunnelState->serverDestinations.erase(tunnelState->serverDestinations.begin());
894 if (status != Comm::TIMEOUT && tunnelState->serverDestinations.size() > 0) {
895 /* Try another IP of this destination host */
896 GetMarkingsToServer(tunnelState->request.getRaw(), *tunnelState->serverDestinations[0]);
897 debugs(26, 4, HERE << "retry with : " << tunnelState->serverDestinations[0]);
898 AsyncCall::Pointer call = commCbCall(26,3, "tunnelConnectDone", CommConnectCbPtrFun(tunnelConnectDone, tunnelState));
899 Comm::ConnOpener *cs = new Comm::ConnOpener(tunnelState->serverDestinations[0], call, Config.Timeout.connect);
900 cs->setHost(tunnelState->url);
901 AsyncJob::Start(cs);
902 } else {
903 debugs(26, 4, HERE << "terminate with error.");
904 ErrorState *err = new ErrorState(ERR_CONNECT_FAIL, Http::scServiceUnavailable, tunnelState->request.getRaw());
905 *tunnelState->status_ptr = Http::scServiceUnavailable;
906 err->xerrno = xerrno;
907 // on timeout is this still: err->xerrno = ETIMEDOUT;
908 err->port = conn->remote.port();
909 err->callback = tunnelErrorComplete;
910 err->callback_data = tunnelState;
911 errorSend(tunnelState->client.conn, err);
912 if (tunnelState->request != NULL)
913 tunnelState->request->hier.stopPeerClock(false);
914 }
915 return;
916 }
917
918 #if USE_DELAY_POOLS
919 /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */
920 if (conn->getPeer() && conn->getPeer()->options.no_delay)
921 tunnelState->server.setDelayId(DelayId());
922 #endif
923
924 tunnelState->request->hier.note(conn, tunnelState->getHost());
925
926 tunnelState->server.conn = conn;
927 tunnelState->request->peer_host = conn->getPeer() ? conn->getPeer()->host : NULL;
928 comm_add_close_handler(conn->fd, tunnelServerClosed, tunnelState);
929
930 debugs(26, 4, HERE << "determine post-connect handling pathway.");
931 if (conn->getPeer()) {
932 tunnelState->request->peer_login = conn->getPeer()->login;
933 tunnelState->request->flags.proxying = !(conn->getPeer()->options.originserver);
934 } else {
935 tunnelState->request->peer_login = NULL;
936 tunnelState->request->flags.proxying = false;
937 }
938
939 if (tunnelState->request->flags.proxying)
940 tunnelState->connectToPeer();
941 else {
942 tunnelConnected(conn, tunnelState);
943 }
944
945 AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout",
946 CommTimeoutCbPtrFun(tunnelTimeout, tunnelState));
947 commSetConnTimeout(conn, Config.Timeout.read, timeoutCall);
948 }
949
950 void
951 tunnelStart(ClientHttpRequest * http, int64_t * size_ptr, int *status_ptr, const AccessLogEntryPointer &al)
952 {
953 debugs(26, 3, HERE);
954 /* Create state structure. */
955 TunnelStateData *tunnelState = NULL;
956 ErrorState *err = NULL;
957 HttpRequest *request = http->request;
958 char *url = http->uri;
959
960 /*
961 * client_addr.isNoAddr() indicates this is an "internal" request
962 * from peer_digest.c, asn.c, netdb.c, etc and should always
963 * be allowed. yuck, I know.
964 */
965
966 if (Config.accessList.miss && !request->client_addr.isNoAddr()) {
967 /*
968 * Check if this host is allowed to fetch MISSES from us (miss_access)
969 * default is to allow.
970 */
971 ACLFilledChecklist ch(Config.accessList.miss, request, NULL);
972 ch.src_addr = request->client_addr;
973 ch.my_addr = request->my_addr;
974 if (ch.fastCheck() == ACCESS_DENIED) {
975 debugs(26, 4, HERE << "MISS access forbidden.");
976 err = new ErrorState(ERR_FORWARDING_DENIED, Http::scForbidden, request);
977 *status_ptr = Http::scForbidden;
978 errorSend(http->getConn()->clientConnection, err);
979 return;
980 }
981 }
982
983 debugs(26, 3, request->method << ' ' << url << ' ' << request->http_ver);
984 ++statCounter.server.all.requests;
985 ++statCounter.server.other.requests;
986
987 tunnelState = new TunnelStateData;
988 #if USE_DELAY_POOLS
989 tunnelState->server.setDelayId(DelayId::DelayClient(http));
990 #endif
991 tunnelState->url = xstrdup(url);
992 tunnelState->request = request;
993 tunnelState->server.size_ptr = size_ptr;
994 tunnelState->status_ptr = status_ptr;
995 tunnelState->logTag_ptr = &http->logType;
996 tunnelState->client.conn = http->getConn()->clientConnection;
997 tunnelState->http = http;
998 tunnelState->al = al;
999
1000 comm_add_close_handler(tunnelState->client.conn->fd,
1001 tunnelClientClosed,
1002 tunnelState);
1003
1004 AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout",
1005 CommTimeoutCbPtrFun(tunnelTimeout, tunnelState));
1006 commSetConnTimeout(tunnelState->client.conn, Config.Timeout.lifetime, timeoutCall);
1007
1008 peerSelect(&(tunnelState->serverDestinations), request, al,
1009 NULL,
1010 tunnelPeerSelectComplete,
1011 tunnelState);
1012 }
1013
1014 void
1015 TunnelStateData::connectToPeer()
1016 {
1017 const Comm::ConnectionPointer &srv = server.conn;
1018
1019 #if USE_OPENSSL
1020 if (CachePeer *p = srv->getPeer()) {
1021 if (p->secure.encryptTransport) {
1022 AsyncCall::Pointer callback = asyncCall(5,4,
1023 "TunnelStateData::ConnectedToPeer",
1024 MyAnswerDialer(&TunnelStateData::connectedToPeer, this));
1025 Ssl::PeerConnector *connector =
1026 new Ssl::PeerConnector(request, srv, client.conn, callback);
1027 AsyncJob::Start(connector); // will call our callback
1028 return;
1029 }
1030 }
1031 #endif
1032
1033 tunnelRelayConnectRequest(srv, this);
1034 }
1035
1036 #if USE_OPENSSL
1037 /// Ssl::PeerConnector callback
1038 void
1039 TunnelStateData::connectedToPeer(Ssl::PeerConnectorAnswer &answer)
1040 {
1041 if (ErrorState *error = answer.error.get()) {
1042 *status_ptr = error->httpStatus;
1043 error->callback = tunnelErrorComplete;
1044 error->callback_data = this;
1045 errorSend(client.conn, error);
1046 answer.error.clear(); // preserve error for errorSendComplete()
1047 return;
1048 }
1049
1050 tunnelRelayConnectRequest(server.conn, this);
1051 }
1052 #endif
1053
1054 static void
1055 tunnelRelayConnectRequest(const Comm::ConnectionPointer &srv, void *data)
1056 {
1057 TunnelStateData *tunnelState = (TunnelStateData *)data;
1058 assert(!tunnelState->waitingForConnectExchange());
1059 HttpHeader hdr_out(hoRequest);
1060 HttpStateFlags flags;
1061 debugs(26, 3, HERE << srv << ", tunnelState=" << tunnelState);
1062 memset(&flags, '\0', sizeof(flags));
1063 flags.proxying = tunnelState->request->flags.proxying;
1064 MemBuf mb;
1065 mb.init();
1066 mb.Printf("CONNECT %s HTTP/1.1\r\n", tunnelState->url);
1067 HttpStateData::httpBuildRequestHeader(tunnelState->request.getRaw(),
1068 NULL, /* StoreEntry */
1069 tunnelState->al, /* AccessLogEntry */
1070 &hdr_out,
1071 flags); /* flags */
1072 hdr_out.packInto(&mb);
1073 hdr_out.clean();
1074 mb.append("\r\n", 2);
1075
1076 debugs(11, 2, "Tunnel Server REQUEST: " << tunnelState->server.conn << ":\n----------\n" <<
1077 Raw("tunnelRelayConnectRequest", mb.content(), mb.contentSize()) << "\n----------");
1078
1079 if (tunnelState->clientExpectsConnectResponse()) {
1080 // hack: blindly tunnel peer response (to our CONNECT request) to the client as ours.
1081 AsyncCall::Pointer writeCall = commCbCall(5,5, "tunnelConnectedWriteDone",
1082 CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState));
1083 Comm::Write(srv, &mb, writeCall);
1084 } else {
1085 // we have to eat the connect response from the peer (so that the client
1086 // does not see it) and only then start shoveling data to the client
1087 AsyncCall::Pointer writeCall = commCbCall(5,5, "tunnelConnectReqWriteDone",
1088 CommIoCbPtrFun(tunnelConnectReqWriteDone,
1089 tunnelState));
1090 Comm::Write(srv, &mb, writeCall);
1091 tunnelState->connectReqWriting = true;
1092
1093 tunnelState->connectRespBuf = new MemBuf;
1094 // SQUID_TCP_SO_RCVBUF: we should not accumulate more than regular I/O buffer
1095 // can hold since any CONNECT response leftovers have to fit into server.buf.
1096 // 2*SQUID_TCP_SO_RCVBUF: HttpMsg::parse() zero-terminates, which uses space.
1097 tunnelState->connectRespBuf->init(SQUID_TCP_SO_RCVBUF, 2*SQUID_TCP_SO_RCVBUF);
1098 tunnelState->readConnectResponse();
1099
1100 assert(tunnelState->waitingForConnectExchange());
1101 }
1102
1103 AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout",
1104 CommTimeoutCbPtrFun(tunnelTimeout, tunnelState));
1105 commSetConnTimeout(srv, Config.Timeout.read, timeoutCall);
1106 }
1107
1108 static void
1109 tunnelPeerSelectComplete(Comm::ConnectionList *peer_paths, ErrorState *err, void *data)
1110 {
1111 TunnelStateData *tunnelState = (TunnelStateData *)data;
1112
1113 if (peer_paths == NULL || peer_paths->size() < 1) {
1114 debugs(26, 3, HERE << "No paths found. Aborting CONNECT");
1115 if (!err) {
1116 err = new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, tunnelState->request.getRaw());
1117 }
1118 *tunnelState->status_ptr = err->httpStatus;
1119 err->callback = tunnelErrorComplete;
1120 err->callback_data = tunnelState;
1121 errorSend(tunnelState->client.conn, err);
1122 return;
1123 }
1124 delete err;
1125
1126 GetMarkingsToServer(tunnelState->request.getRaw(), *tunnelState->serverDestinations[0]);
1127
1128 if (tunnelState->request != NULL)
1129 tunnelState->request->hier.startPeerClock();
1130
1131 debugs(26, 3, HERE << "paths=" << peer_paths->size() << ", p[0]={" << (*peer_paths)[0] << "}, serverDest[0]={" <<
1132 tunnelState->serverDestinations[0] << "}");
1133
1134 AsyncCall::Pointer call = commCbCall(26,3, "tunnelConnectDone", CommConnectCbPtrFun(tunnelConnectDone, tunnelState));
1135 Comm::ConnOpener *cs = new Comm::ConnOpener(tunnelState->serverDestinations[0], call, Config.Timeout.connect);
1136 cs->setHost(tunnelState->url);
1137 AsyncJob::Start(cs);
1138 }
1139
1140 CBDATA_CLASS_INIT(TunnelStateData);
1141
1142 bool
1143 TunnelStateData::noConnections() const
1144 {
1145 return !Comm::IsConnOpen(server.conn) && !Comm::IsConnOpen(client.conn);
1146 }
1147
1148 #if USE_DELAY_POOLS
1149 void
1150 TunnelStateData::Connection::setDelayId(DelayId const &newDelay)
1151 {
1152 delayId = newDelay;
1153 }
1154
1155 #endif
1156
1157 #if USE_OPENSSL
1158 void
1159 switchToTunnel(HttpRequest *request, Comm::ConnectionPointer &clientConn, Comm::ConnectionPointer &srvConn)
1160 {
1161 debugs(26,5, "Revert to tunnel FD " << clientConn->fd << " with FD " << srvConn->fd);
1162 /* Create state structure. */
1163 TunnelStateData *tunnelState = NULL;
1164 const char *url = urlCanonical(request);
1165
1166 debugs(26, 3, request->method << " " << url << " " << request->http_ver);
1167 ++statCounter.server.all.requests;
1168 ++statCounter.server.other.requests;
1169
1170 tunnelState = new TunnelStateData;
1171 tunnelState->url = xstrdup(url);
1172 tunnelState->request = request;
1173 tunnelState->server.size_ptr = NULL; //Set later if ClientSocketContext is available
1174
1175 // Temporary static variable to store the unneeded for our case status code
1176 static int status_code = 0;
1177 tunnelState->status_ptr = &status_code;
1178 tunnelState->client.conn = clientConn;
1179
1180 ConnStateData *conn;
1181 if ((conn = request->clientConnectionManager.get())) {
1182 ClientSocketContext::Pointer context = conn->getCurrentContext();
1183 if (context != NULL && context->http != NULL) {
1184 tunnelState->logTag_ptr = &context->http->logType;
1185 tunnelState->server.size_ptr = &context->http->out.size;
1186
1187 #if USE_DELAY_POOLS
1188 /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */
1189 if (srvConn->getPeer() && srvConn->getPeer()->options.no_delay)
1190 tunnelState->server.setDelayId(DelayId::DelayClient(context->http));
1191 #endif
1192 }
1193 }
1194
1195 comm_add_close_handler(tunnelState->client.conn->fd,
1196 tunnelClientClosed,
1197 tunnelState);
1198
1199 AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout",
1200 CommTimeoutCbPtrFun(tunnelTimeout, tunnelState));
1201 commSetConnTimeout(tunnelState->client.conn, Config.Timeout.lifetime, timeoutCall);
1202 fd_table[clientConn->fd].read_method = &default_read_method;
1203 fd_table[clientConn->fd].write_method = &default_write_method;
1204
1205 tunnelState->request->hier.note(srvConn, tunnelState->getHost());
1206
1207 tunnelState->server.conn = srvConn;
1208 tunnelState->request->peer_host = srvConn->getPeer() ? srvConn->getPeer()->host : NULL;
1209 comm_add_close_handler(srvConn->fd, tunnelServerClosed, tunnelState);
1210
1211 debugs(26, 4, "determine post-connect handling pathway.");
1212 if (srvConn->getPeer()) {
1213 tunnelState->request->peer_login = srvConn->getPeer()->login;
1214 tunnelState->request->flags.proxying = !(srvConn->getPeer()->options.originserver);
1215 } else {
1216 tunnelState->request->peer_login = NULL;
1217 tunnelState->request->flags.proxying = false;
1218 }
1219
1220 timeoutCall = commCbCall(5, 4, "tunnelTimeout",
1221 CommTimeoutCbPtrFun(tunnelTimeout, tunnelState));
1222 commSetConnTimeout(srvConn, Config.Timeout.read, timeoutCall);
1223 fd_table[srvConn->fd].read_method = &default_read_method;
1224 fd_table[srvConn->fd].write_method = &default_write_method;
1225
1226 SSL *ssl = fd_table[srvConn->fd].ssl;
1227 assert(ssl);
1228 BIO *b = SSL_get_rbio(ssl);
1229 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
1230 const MemBuf &buf = srvBio->rBufData();
1231
1232 AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone",
1233 CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState));
1234 Comm::Write(tunnelState->client.conn, buf.content(), buf.contentSize(), call, NULL);
1235 }
1236 #endif //USE_OPENSSL
1237