2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 26 Secure Sockets Layer Proxy */
12 #include "acl/FilledChecklist.h"
13 #include "base/CbcPointer.h"
14 #include "CachePeer.h"
15 #include "client_side.h"
16 #include "client_side_request.h"
18 #include "comm/Connection.h"
19 #include "comm/ConnOpener.h"
20 #include "comm/Read.h"
21 #include "comm/Write.h"
22 #include "errorpage.h"
27 #include "HttpRequest.h"
28 #include "HttpStateFlags.h"
29 #include "ip/QosConfig.h"
32 #include "PeerSelectState.h"
33 #include "SquidConfig.h"
34 #include "SquidTime.h"
35 #include "StatCounters.h"
38 #include "ssl/PeerConnector.h"
39 #include "ssl/ServerBump.h"
50 * TunnelStateData is the state engine performing the tasks for
51 * setup of a TCP tunnel from an existing open client FD to a server
52 * then shuffling binary data between the resulting FD pair.
55 * TODO 1: implement a read/write API on ConnStateData to send/receive blocks
56 * of pre-formatted data. Then we can use that as the client side of the tunnel
57 * instead of re-implementing it here and occasionally getting the ConnStateData
58 * read/write state wrong.
60 * TODO 2: then convert this into a AsyncJob, possibly a child of 'Server'
64 CBDATA_CLASS(TunnelStateData
);
69 TunnelStateData(const TunnelStateData
&); // do not implement
70 TunnelStateData
&operator =(const TunnelStateData
&); // do not implement
73 static void ReadClient(const Comm::ConnectionPointer
&, char *buf
, size_t len
, Comm::Flag errcode
, int xerrno
, void *data
);
74 static void ReadServer(const Comm::ConnectionPointer
&, char *buf
, size_t len
, Comm::Flag errcode
, int xerrno
, void *data
);
75 static void WriteClientDone(const Comm::ConnectionPointer
&, char *buf
, size_t len
, Comm::Flag flag
, int xerrno
, void *data
);
76 static void WriteServerDone(const Comm::ConnectionPointer
&, char *buf
, size_t len
, Comm::Flag flag
, int xerrno
, void *data
);
78 /// Starts reading peer response to our CONNECT request.
79 void readConnectResponse();
81 /// Called when we may be done handling a CONNECT exchange with the peer.
82 void connectExchangeCheckpoint();
84 bool noConnections() const;
86 CbcPointer
<ClientHttpRequest
> http
;
87 HttpRequest::Pointer request
;
88 AccessLogEntryPointer al
;
89 Comm::ConnectionList serverDestinations
;
91 const char * getHost() const {
92 return (server
.conn
!= NULL
&& server
.conn
->getPeer() ? server
.conn
->getPeer()->host
: request
->GetHost());
95 /// Whether we are writing a CONNECT request to a peer.
96 bool waitingForConnectRequest() const { return connectReqWriting
; }
97 /// Whether we are reading a CONNECT response from a peer.
98 bool waitingForConnectResponse() const { return connectRespBuf
; }
99 /// Whether we are waiting for the CONNECT request/response exchange with the peer.
100 bool waitingForConnectExchange() const { return waitingForConnectRequest() || waitingForConnectResponse(); }
102 /// Whether the client sent a CONNECT request to us.
103 bool clientExpectsConnectResponse() const {
105 // We are bumping and we had already send "OK CONNECTED"
106 if (http
.valid() && http
->getConn() && http
->getConn()->serverBump() && http
->getConn()->serverBump()->step
> Ssl::bumpStep1
)
109 return !(request
!= NULL
&&
110 (request
->flags
.interceptTproxy
|| request
->flags
.intercepted
));
117 Connection() : len (0), buf ((char *)xmalloc(SQUID_TCP_SO_RCVBUF
)), size_ptr(NULL
) {}
121 int bytesWanted(int lower
=0, int upper
= INT_MAX
) const;
122 void bytesIn(int const &);
125 void setDelayId(DelayId
const &);
128 void error(int const xerrno
);
129 int debugLevelForError(int const xerrno
) const;
130 /// handles a non-I/O error associated with this Connection
131 void logicError(const char *errMsg
);
133 void dataSent (size_t amount
);
136 int64_t *size_ptr
; /* pointer to size in an ConnStateData for logging */
138 Comm::ConnectionPointer conn
; ///< The currently connected connection.
148 Connection client
, server
;
149 int *status_ptr
; ///< pointer for logging HTTP status
150 LogTags
*logTag_ptr
; ///< pointer for logging Squid processing code
151 MemBuf
*connectRespBuf
; ///< accumulates peer CONNECT response when we need it
152 bool connectReqWriting
; ///< whether we are writing a CONNECT request to a peer
154 void copyRead(Connection
&from
, IOCB
*completion
);
156 /// continue to set up connection to a peer, going async for SSL peers
157 void connectToPeer();
161 /// Gives PeerConnector access to Answer in the TunnelStateData callback dialer.
162 class MyAnswerDialer
: public CallDialer
, public Ssl::PeerConnector::CbDialer
165 typedef void (TunnelStateData::*Method
)(Ssl::PeerConnectorAnswer
&);
167 MyAnswerDialer(Method method
, TunnelStateData
*tunnel
):
168 method_(method
), tunnel_(tunnel
), answer_() {}
171 virtual bool canDial(AsyncCall
&call
) { return tunnel_
.valid(); }
172 void dial(AsyncCall
&call
) { ((&(*tunnel_
))->*method_
)(answer_
); }
173 virtual void print(std::ostream
&os
) const {
174 os
<< '(' << tunnel_
.get() << ", " << answer_
<< ')';
177 /* Ssl::PeerConnector::CbDialer API */
178 virtual Ssl::PeerConnectorAnswer
&answer() { return answer_
; }
182 CbcPointer
<TunnelStateData
> tunnel_
;
183 Ssl::PeerConnectorAnswer answer_
;
186 void connectedToPeer(Ssl::PeerConnectorAnswer
&answer
);
190 bool keepGoingAfterRead(size_t len
, Comm::Flag errcode
, int xerrno
, Connection
&from
, Connection
&to
);
191 void copy(size_t len
, Connection
&from
, Connection
&to
, IOCB
*);
192 void handleConnectResponse(const size_t chunkSize
);
193 void readServer(char *buf
, size_t len
, Comm::Flag errcode
, int xerrno
);
194 void readClient(char *buf
, size_t len
, Comm::Flag errcode
, int xerrno
);
195 void writeClientDone(char *buf
, size_t len
, Comm::Flag flag
, int xerrno
);
196 void writeServerDone(char *buf
, size_t len
, Comm::Flag flag
, int xerrno
);
198 static void ReadConnectResponseDone(const Comm::ConnectionPointer
&, char *buf
, size_t len
, Comm::Flag errcode
, int xerrno
, void *data
);
199 void readConnectResponseDone(char *buf
, size_t len
, Comm::Flag errcode
, int xerrno
);
202 static const char *const conn_established
= "HTTP/1.1 200 Connection established\r\n\r\n";
204 static CNCB tunnelConnectDone
;
205 static ERCB tunnelErrorComplete
;
206 static CLCB tunnelServerClosed
;
207 static CLCB tunnelClientClosed
;
208 static CTCB tunnelTimeout
;
209 static PSC tunnelPeerSelectComplete
;
210 static void tunnelConnected(const Comm::ConnectionPointer
&server
, void *);
211 static void tunnelRelayConnectRequest(const Comm::ConnectionPointer
&server
, void *);
214 tunnelServerClosed(const CommCloseCbParams
¶ms
)
216 TunnelStateData
*tunnelState
= (TunnelStateData
*)params
.data
;
217 debugs(26, 3, HERE
<< tunnelState
->server
.conn
);
218 tunnelState
->server
.conn
= NULL
;
220 if (tunnelState
->request
!= NULL
)
221 tunnelState
->request
->hier
.stopPeerClock(false);
223 if (tunnelState
->noConnections()) {
228 if (!tunnelState
->server
.len
) {
229 tunnelState
->client
.conn
->close();
235 tunnelClientClosed(const CommCloseCbParams
¶ms
)
237 TunnelStateData
*tunnelState
= (TunnelStateData
*)params
.data
;
238 debugs(26, 3, HERE
<< tunnelState
->client
.conn
);
239 tunnelState
->client
.conn
= NULL
;
241 if (tunnelState
->noConnections()) {
246 if (!tunnelState
->client
.len
) {
247 tunnelState
->server
.conn
->close();
252 TunnelStateData::TunnelStateData() :
258 connectRespBuf(NULL
),
259 connectReqWriting(false)
261 debugs(26, 3, "TunnelStateData constructed this=" << this);
264 TunnelStateData::~TunnelStateData()
266 debugs(26, 3, "TunnelStateData destructed this=" << this);
267 assert(noConnections());
269 serverDestinations
.clear();
270 delete connectRespBuf
;
273 TunnelStateData::Connection::~Connection()
279 TunnelStateData::Connection::bytesWanted(int lowerbound
, int upperbound
) const
282 return delayId
.bytesWanted(lowerbound
, upperbound
);
290 TunnelStateData::Connection::bytesIn(int const &count
)
292 debugs(26, 3, HERE
<< "len=" << len
<< " + count=" << count
);
294 delayId
.bytesIn(count
);
301 TunnelStateData::Connection::debugLevelForError(int const xerrno
) const
305 if (xerrno
== ECONNRESET
)
310 if (ignoreErrno(xerrno
))
316 /* Read from server side and queue it for writing to the client */
318 TunnelStateData::ReadServer(const Comm::ConnectionPointer
&c
, char *buf
, size_t len
, Comm::Flag errcode
, int xerrno
, void *data
)
320 TunnelStateData
*tunnelState
= (TunnelStateData
*)data
;
321 assert(cbdataReferenceValid(tunnelState
));
322 debugs(26, 3, HERE
<< c
);
324 tunnelState
->readServer(buf
, len
, errcode
, xerrno
);
328 TunnelStateData::readServer(char *, size_t len
, Comm::Flag errcode
, int xerrno
)
330 debugs(26, 3, HERE
<< server
.conn
<< ", read " << len
<< " bytes, err=" << errcode
);
333 * Bail out early on Comm::ERR_CLOSING
334 * - close handlers will tidy up for us
337 if (errcode
== Comm::ERR_CLOSING
)
342 kb_incr(&(statCounter
.server
.all
.kbytes_in
), len
);
343 kb_incr(&(statCounter
.server
.other
.kbytes_in
), len
);
346 if (keepGoingAfterRead(len
, errcode
, xerrno
, server
, client
))
347 copy(len
, server
, client
, WriteClientDone
);
350 /// Called when we read [a part of] CONNECT response from the peer
352 TunnelStateData::readConnectResponseDone(char *, size_t len
, Comm::Flag errcode
, int xerrno
)
354 debugs(26, 3, server
.conn
<< ", read " << len
<< " bytes, err=" << errcode
);
355 assert(waitingForConnectResponse());
357 if (errcode
== Comm::ERR_CLOSING
)
361 connectRespBuf
->appended(len
);
363 kb_incr(&(statCounter
.server
.all
.kbytes_in
), len
);
364 kb_incr(&(statCounter
.server
.other
.kbytes_in
), len
);
367 if (keepGoingAfterRead(len
, errcode
, xerrno
, server
, client
))
368 handleConnectResponse(len
);
371 /* Read from client side and queue it for writing to the server */
373 TunnelStateData::ReadConnectResponseDone(const Comm::ConnectionPointer
&, char *buf
, size_t len
, Comm::Flag errcode
, int xerrno
, void *data
)
375 TunnelStateData
*tunnelState
= (TunnelStateData
*)data
;
376 assert (cbdataReferenceValid (tunnelState
));
378 tunnelState
->readConnectResponseDone(buf
, len
, errcode
, xerrno
);
381 /// Parses [possibly incomplete] CONNECT response and reacts to it.
382 /// If the tunnel is being closed or more response data is needed, returns false.
383 /// Otherwise, the caller should handle the remaining read data, if any.
385 TunnelStateData::handleConnectResponse(const size_t chunkSize
)
387 assert(waitingForConnectResponse());
389 // Ideally, client and server should use MemBuf or better, but current code
390 // never accumulates more than one read when shoveling data (XXX) so it does
391 // not need to deal with MemBuf complexity. To keep it simple, we use a
392 // dedicated MemBuf for accumulating CONNECT responses. TODO: When shoveling
393 // is optimized, reuse server.buf for CONNEC response accumulation instead.
395 /* mimic the basic parts of HttpStateData::processReplyHeader() */
397 Http::StatusCode parseErr
= Http::scNone
;
398 const bool eof
= !chunkSize
;
399 const bool parsed
= rep
.parse(connectRespBuf
, eof
, &parseErr
);
401 if (parseErr
> 0) { // unrecoverable parsing error
402 server
.logicError("malformed CONNECT response from peer");
410 if (!connectRespBuf
->hasSpace()) {
411 server
.logicError("huge CONNECT response from peer");
416 readConnectResponse();
420 // CONNECT response was successfully parsed
421 *status_ptr
= rep
.sline
.status();
423 // bail if we did not get an HTTP 200 (Connection Established) response
424 if (rep
.sline
.status() != Http::scOkay
) {
425 server
.logicError("unsupported CONNECT response status code");
429 if (rep
.hdr_sz
< connectRespBuf
->contentSize()) {
430 // preserve bytes that the server already sent after the CONNECT response
431 server
.len
= connectRespBuf
->contentSize() - rep
.hdr_sz
;
432 memcpy(server
.buf
, connectRespBuf
->content()+rep
.hdr_sz
, server
.len
);
434 // reset; delay pools were using this field to throttle CONNECT response
438 delete connectRespBuf
;
439 connectRespBuf
= NULL
;
440 connectExchangeCheckpoint();
444 TunnelStateData::Connection::logicError(const char *errMsg
)
446 debugs(50, 3, conn
<< " closing on error: " << errMsg
);
451 TunnelStateData::Connection::error(int const xerrno
)
453 /* XXX fixme xstrerror and xerrno... */
456 debugs(50, debugLevelForError(xerrno
), HERE
<< conn
<< ": read/write failure: " << xstrerror());
458 if (!ignoreErrno(xerrno
))
462 /* Read from client side and queue it for writing to the server */
464 TunnelStateData::ReadClient(const Comm::ConnectionPointer
&, char *buf
, size_t len
, Comm::Flag errcode
, int xerrno
, void *data
)
466 TunnelStateData
*tunnelState
= (TunnelStateData
*)data
;
467 assert (cbdataReferenceValid (tunnelState
));
469 tunnelState
->readClient(buf
, len
, errcode
, xerrno
);
473 TunnelStateData::readClient(char *, size_t len
, Comm::Flag errcode
, int xerrno
)
475 debugs(26, 3, HERE
<< client
.conn
<< ", read " << len
<< " bytes, err=" << errcode
);
478 * Bail out early on Comm::ERR_CLOSING
479 * - close handlers will tidy up for us
482 if (errcode
== Comm::ERR_CLOSING
)
487 kb_incr(&(statCounter
.client_http
.kbytes_in
), len
);
490 if (keepGoingAfterRead(len
, errcode
, xerrno
, client
, server
))
491 copy(len
, client
, server
, WriteServerDone
);
494 /// Updates state after reading from client or server.
495 /// Returns whether the caller should use the data just read.
497 TunnelStateData::keepGoingAfterRead(size_t len
, Comm::Flag errcode
, int xerrno
, Connection
&from
, Connection
&to
)
499 debugs(26, 3, HERE
<< "from={" << from
.conn
<< "}, to={" << to
.conn
<< "}");
501 /* I think this is to prevent free-while-in-a-callback behaviour
503 * from.conn->close() / to.conn->close() done here trigger close callbacks which may free TunnelStateData
505 const CbcPointer
<TunnelStateData
> safetyLock(this);
507 /* Bump the source connection read timeout on any activity */
508 if (Comm::IsConnOpen(from
.conn
)) {
509 AsyncCall::Pointer timeoutCall
= commCbCall(5, 4, "tunnelTimeout",
510 CommTimeoutCbPtrFun(tunnelTimeout
, this));
511 commSetConnTimeout(from
.conn
, Config
.Timeout
.read
, timeoutCall
);
514 /* Bump the dest connection read timeout on any activity */
515 /* see Bug 3659: tunnels can be weird, with very long one-way transfers */
516 if (Comm::IsConnOpen(to
.conn
)) {
517 AsyncCall::Pointer timeoutCall
= commCbCall(5, 4, "tunnelTimeout",
518 CommTimeoutCbPtrFun(tunnelTimeout
, this));
519 commSetConnTimeout(to
.conn
, Config
.Timeout
.read
, timeoutCall
);
524 else if (len
== 0 || !Comm::IsConnOpen(to
.conn
)) {
525 debugs(26, 3, HERE
<< "Nothing to write or client gone. Terminate the tunnel.");
528 /* Only close the remote end if we've finished queueing data to it */
529 if (from
.len
== 0 && Comm::IsConnOpen(to
.conn
) ) {
532 } else if (cbdataReferenceValid(this)) {
540 TunnelStateData::copy(size_t len
, Connection
&from
, Connection
&to
, IOCB
*completion
)
542 debugs(26, 3, HERE
<< "Schedule Write");
543 AsyncCall::Pointer call
= commCbCall(5,5, "TunnelBlindCopyWriteHandler",
544 CommIoCbPtrFun(completion
, this));
545 Comm::Write(to
.conn
, from
.buf
, len
, call
, NULL
);
548 /* Writes data from the client buffer to the server side */
550 TunnelStateData::WriteServerDone(const Comm::ConnectionPointer
&, char *buf
, size_t len
, Comm::Flag flag
, int xerrno
, void *data
)
552 TunnelStateData
*tunnelState
= (TunnelStateData
*)data
;
553 assert (cbdataReferenceValid (tunnelState
));
555 tunnelState
->writeServerDone(buf
, len
, flag
, xerrno
);
559 TunnelStateData::writeServerDone(char *, size_t len
, Comm::Flag flag
, int xerrno
)
561 debugs(26, 3, HERE
<< server
.conn
<< ", " << len
<< " bytes written, flag=" << flag
);
564 if (flag
!= Comm::OK
) {
565 if (flag
!= Comm::ERR_CLOSING
) {
566 debugs(26, 4, HERE
<< "calling TunnelStateData::server.error(" << xerrno
<<")");
567 server
.error(xerrno
); // may call comm_close
574 debugs(26, 4, HERE
<< "No read input. Closing server connection.");
575 server
.conn
->close();
580 kb_incr(&(statCounter
.server
.all
.kbytes_out
), len
);
581 kb_incr(&(statCounter
.server
.other
.kbytes_out
), len
);
582 client
.dataSent(len
);
584 /* If the other end has closed, so should we */
585 if (!Comm::IsConnOpen(client
.conn
)) {
586 debugs(26, 4, HERE
<< "Client gone away. Shutting down server connection.");
587 server
.conn
->close();
591 const CbcPointer
<TunnelStateData
> safetyLock(this); /* ??? should be locked by the caller... */
593 if (cbdataReferenceValid(this))
594 copyRead(client
, ReadClient
);
597 /* Writes data from the server buffer to the client side */
599 TunnelStateData::WriteClientDone(const Comm::ConnectionPointer
&, char *buf
, size_t len
, Comm::Flag flag
, int xerrno
, void *data
)
601 TunnelStateData
*tunnelState
= (TunnelStateData
*)data
;
602 assert (cbdataReferenceValid (tunnelState
));
604 tunnelState
->writeClientDone(buf
, len
, flag
, xerrno
);
608 TunnelStateData::Connection::dataSent(size_t amount
)
610 debugs(26, 3, HERE
<< "len=" << len
<< " - amount=" << amount
);
611 assert(amount
== (size_t)len
);
613 /* increment total object size */
620 TunnelStateData::writeClientDone(char *, size_t len
, Comm::Flag flag
, int xerrno
)
622 debugs(26, 3, HERE
<< client
.conn
<< ", " << len
<< " bytes written, flag=" << flag
);
625 if (flag
!= Comm::OK
) {
626 if (flag
!= Comm::ERR_CLOSING
) {
627 debugs(26, 4, HERE
<< "Closing client connection due to comm flags.");
628 client
.error(xerrno
); // may call comm_close
635 debugs(26, 4, HERE
<< "Closing client connection due to 0 byte read.");
636 client
.conn
->close();
641 kb_incr(&(statCounter
.client_http
.kbytes_out
), len
);
642 server
.dataSent(len
);
644 /* If the other end has closed, so should we */
645 if (!Comm::IsConnOpen(server
.conn
)) {
646 debugs(26, 4, HERE
<< "Server has gone away. Terminating client connection.");
647 client
.conn
->close();
651 CbcPointer
<TunnelStateData
> safetyLock(this); /* ??? should be locked by the caller... */
653 if (cbdataReferenceValid(this))
654 copyRead(server
, ReadServer
);
658 tunnelTimeout(const CommTimeoutCbParams
&io
)
660 TunnelStateData
*tunnelState
= static_cast<TunnelStateData
*>(io
.data
);
661 debugs(26, 3, HERE
<< io
.conn
);
662 /* Temporary lock to protect our own feets (comm_close -> tunnelClientClosed -> Free) */
663 CbcPointer
<TunnelStateData
> safetyLock(tunnelState
);
665 tunnelState
->client
.closeIfOpen();
666 tunnelState
->server
.closeIfOpen();
670 TunnelStateData::Connection::closeIfOpen()
672 if (Comm::IsConnOpen(conn
))
677 TunnelStateData::copyRead(Connection
&from
, IOCB
*completion
)
679 assert(from
.len
== 0);
680 AsyncCall::Pointer call
= commCbCall(5,4, "TunnelBlindCopyReadHandler",
681 CommIoCbPtrFun(completion
, this));
682 comm_read(from
.conn
, from
.buf
, from
.bytesWanted(1, SQUID_TCP_SO_RCVBUF
), call
);
686 TunnelStateData::readConnectResponse()
688 assert(waitingForConnectResponse());
690 AsyncCall::Pointer call
= commCbCall(5,4, "readConnectResponseDone",
691 CommIoCbPtrFun(ReadConnectResponseDone
, this));
692 comm_read(server
.conn
, connectRespBuf
->space(),
693 server
.bytesWanted(1, connectRespBuf
->spaceSize()), call
);
697 * Set the HTTP status for this request and sets the read handlers for client
698 * and server side connections.
701 tunnelStartShoveling(TunnelStateData
*tunnelState
)
703 assert(!tunnelState
->waitingForConnectExchange());
704 *tunnelState
->status_ptr
= Http::scOkay
;
705 if (tunnelState
->logTag_ptr
)
706 *tunnelState
->logTag_ptr
= LOG_TCP_TUNNEL
;
707 if (cbdataReferenceValid(tunnelState
)) {
709 // Shovel any payload already pushed into reply buffer by the server response
710 if (!tunnelState
->server
.len
)
711 tunnelState
->copyRead(tunnelState
->server
, TunnelStateData::ReadServer
);
713 debugs(26, DBG_DATA
, "Tunnel server PUSH Payload: \n" << Raw("", tunnelState
->server
.buf
, tunnelState
->server
.len
) << "\n----------");
714 tunnelState
->copy(tunnelState
->server
.len
, tunnelState
->server
, tunnelState
->client
, TunnelStateData::WriteClientDone
);
717 // Bug 3371: shovel any payload already pushed into ConnStateData by the client request
718 if (tunnelState
->http
.valid() && tunnelState
->http
->getConn() && !tunnelState
->http
->getConn()->in
.buf
.isEmpty()) {
719 struct ConnStateData::In
*in
= &tunnelState
->http
->getConn()->in
;
720 debugs(26, DBG_DATA
, "Tunnel client PUSH Payload: \n" << in
->buf
<< "\n----------");
722 // We just need to ensure the bytes from ConnStateData are in client.buf already to deliver
723 memcpy(tunnelState
->client
.buf
, in
->buf
.rawContent(), in
->buf
.length());
724 // NP: readClient() takes care of buffer length accounting.
725 tunnelState
->readClient(tunnelState
->client
.buf
, in
->buf
.length(), Comm::OK
, 0);
726 in
->buf
.consume(); // ConnStateData buffer accounting after the shuffle.
728 tunnelState
->copyRead(tunnelState
->client
, TunnelStateData::ReadClient
);
733 * All the pieces we need to write to client and/or server connection
735 * Call the tunnelStartShoveling to start the blind pump.
738 tunnelConnectedWriteDone(const Comm::ConnectionPointer
&conn
, char *, size_t, Comm::Flag flag
, int, void *data
)
740 TunnelStateData
*tunnelState
= (TunnelStateData
*)data
;
741 debugs(26, 3, HERE
<< conn
<< ", flag=" << flag
);
743 if (flag
!= Comm::OK
) {
744 *tunnelState
->status_ptr
= Http::scInternalServerError
;
745 tunnelErrorComplete(conn
->fd
, data
, 0);
749 tunnelStartShoveling(tunnelState
);
752 /// Called when we are done writing CONNECT request to a peer.
754 tunnelConnectReqWriteDone(const Comm::ConnectionPointer
&conn
, char *, size_t, Comm::Flag flag
, int, void *data
)
756 TunnelStateData
*tunnelState
= (TunnelStateData
*)data
;
757 debugs(26, 3, conn
<< ", flag=" << flag
);
758 assert(tunnelState
->waitingForConnectRequest());
760 if (flag
!= Comm::OK
) {
761 *tunnelState
->status_ptr
= Http::scInternalServerError
;
762 tunnelErrorComplete(conn
->fd
, data
, 0);
766 tunnelState
->connectReqWriting
= false;
767 tunnelState
->connectExchangeCheckpoint();
771 TunnelStateData::connectExchangeCheckpoint()
773 if (waitingForConnectResponse()) {
774 debugs(26, 5, "still reading CONNECT response on " << server
.conn
);
775 } else if (waitingForConnectRequest()) {
776 debugs(26, 5, "still writing CONNECT request on " << server
.conn
);
778 assert(!waitingForConnectExchange());
779 debugs(26, 3, "done with CONNECT exchange on " << server
.conn
);
780 tunnelConnected(server
.conn
, this);
785 * handle the write completion from a proxy request to an upstream origin
788 tunnelConnected(const Comm::ConnectionPointer
&server
, void *data
)
790 TunnelStateData
*tunnelState
= (TunnelStateData
*)data
;
791 debugs(26, 3, HERE
<< server
<< ", tunnelState=" << tunnelState
);
793 if (!tunnelState
->clientExpectsConnectResponse())
794 tunnelStartShoveling(tunnelState
); // ssl-bumped connection, be quiet
796 AsyncCall::Pointer call
= commCbCall(5,5, "tunnelConnectedWriteDone",
797 CommIoCbPtrFun(tunnelConnectedWriteDone
, tunnelState
));
798 Comm::Write(tunnelState
->client
.conn
, conn_established
, strlen(conn_established
), call
, NULL
);
803 tunnelErrorComplete(int fd
/*const Comm::ConnectionPointer &*/, void *data
, size_t)
805 TunnelStateData
*tunnelState
= (TunnelStateData
*)data
;
806 debugs(26, 3, HERE
<< "FD " << fd
);
807 assert(tunnelState
!= NULL
);
808 /* temporary lock to save our own feets (comm_close -> tunnelClientClosed -> Free) */
809 CbcPointer
<TunnelStateData
> safetyLock(tunnelState
);
811 if (Comm::IsConnOpen(tunnelState
->client
.conn
))
812 tunnelState
->client
.conn
->close();
814 if (Comm::IsConnOpen(tunnelState
->server
.conn
))
815 tunnelState
->server
.conn
->close();
819 tunnelConnectDone(const Comm::ConnectionPointer
&conn
, Comm::Flag status
, int xerrno
, void *data
)
821 TunnelStateData
*tunnelState
= (TunnelStateData
*)data
;
823 if (status
!= Comm::OK
) {
824 debugs(26, 4, HERE
<< conn
<< ", comm failure recovery.");
825 /* At this point only the TCP handshake has failed. no data has been passed.
826 * we are allowed to re-try the TCP-level connection to alternate IPs for CONNECT.
828 tunnelState
->serverDestinations
.erase(tunnelState
->serverDestinations
.begin());
829 if (status
!= Comm::TIMEOUT
&& tunnelState
->serverDestinations
.size() > 0) {
830 /* Try another IP of this destination host */
831 GetMarkingsToServer(tunnelState
->request
.getRaw(), *tunnelState
->serverDestinations
[0]);
832 debugs(26, 4, HERE
<< "retry with : " << tunnelState
->serverDestinations
[0]);
833 AsyncCall::Pointer call
= commCbCall(26,3, "tunnelConnectDone", CommConnectCbPtrFun(tunnelConnectDone
, tunnelState
));
834 Comm::ConnOpener
*cs
= new Comm::ConnOpener(tunnelState
->serverDestinations
[0], call
, Config
.Timeout
.connect
);
835 cs
->setHost(tunnelState
->url
);
838 debugs(26, 4, HERE
<< "terminate with error.");
839 ErrorState
*err
= new ErrorState(ERR_CONNECT_FAIL
, Http::scServiceUnavailable
, tunnelState
->request
.getRaw());
840 *tunnelState
->status_ptr
= Http::scServiceUnavailable
;
841 err
->xerrno
= xerrno
;
842 // on timeout is this still: err->xerrno = ETIMEDOUT;
843 err
->port
= conn
->remote
.port();
844 err
->callback
= tunnelErrorComplete
;
845 err
->callback_data
= tunnelState
;
846 errorSend(tunnelState
->client
.conn
, err
);
847 if (tunnelState
->request
!= NULL
)
848 tunnelState
->request
->hier
.stopPeerClock(false);
854 /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */
855 if (conn
->getPeer() && conn
->getPeer()->options
.no_delay
)
856 tunnelState
->server
.setDelayId(DelayId());
859 tunnelState
->request
->hier
.note(conn
, tunnelState
->getHost());
861 tunnelState
->server
.conn
= conn
;
862 tunnelState
->request
->peer_host
= conn
->getPeer() ? conn
->getPeer()->host
: NULL
;
863 comm_add_close_handler(conn
->fd
, tunnelServerClosed
, tunnelState
);
865 debugs(26, 4, HERE
<< "determine post-connect handling pathway.");
866 if (conn
->getPeer()) {
867 tunnelState
->request
->peer_login
= conn
->getPeer()->login
;
868 tunnelState
->request
->flags
.proxying
= !(conn
->getPeer()->options
.originserver
);
870 tunnelState
->request
->peer_login
= NULL
;
871 tunnelState
->request
->flags
.proxying
= false;
874 if (tunnelState
->request
->flags
.proxying
)
875 tunnelState
->connectToPeer();
877 tunnelConnected(conn
, tunnelState
);
880 AsyncCall::Pointer timeoutCall
= commCbCall(5, 4, "tunnelTimeout",
881 CommTimeoutCbPtrFun(tunnelTimeout
, tunnelState
));
882 commSetConnTimeout(conn
, Config
.Timeout
.read
, timeoutCall
);
886 tunnelStart(ClientHttpRequest
* http
, int64_t * size_ptr
, int *status_ptr
, const AccessLogEntryPointer
&al
)
889 /* Create state structure. */
890 TunnelStateData
*tunnelState
= NULL
;
891 ErrorState
*err
= NULL
;
892 HttpRequest
*request
= http
->request
;
893 char *url
= http
->uri
;
896 * client_addr.isNoAddr() indicates this is an "internal" request
897 * from peer_digest.c, asn.c, netdb.c, etc and should always
898 * be allowed. yuck, I know.
901 if (Config
.accessList
.miss
&& !request
->client_addr
.isNoAddr()) {
903 * Check if this host is allowed to fetch MISSES from us (miss_access)
904 * default is to allow.
906 ACLFilledChecklist
ch(Config
.accessList
.miss
, request
, NULL
);
907 ch
.src_addr
= request
->client_addr
;
908 ch
.my_addr
= request
->my_addr
;
909 if (ch
.fastCheck() == ACCESS_DENIED
) {
910 debugs(26, 4, HERE
<< "MISS access forbidden.");
911 err
= new ErrorState(ERR_FORWARDING_DENIED
, Http::scForbidden
, request
);
912 *status_ptr
= Http::scForbidden
;
913 errorSend(http
->getConn()->clientConnection
, err
);
918 debugs(26, 3, request
->method
<< ' ' << url
<< ' ' << request
->http_ver
);
919 ++statCounter
.server
.all
.requests
;
920 ++statCounter
.server
.other
.requests
;
922 tunnelState
= new TunnelStateData
;
924 tunnelState
->server
.setDelayId(DelayId::DelayClient(http
));
926 tunnelState
->url
= xstrdup(url
);
927 tunnelState
->request
= request
;
928 tunnelState
->server
.size_ptr
= size_ptr
;
929 tunnelState
->status_ptr
= status_ptr
;
930 tunnelState
->logTag_ptr
= &http
->logType
;
931 tunnelState
->client
.conn
= http
->getConn()->clientConnection
;
932 tunnelState
->http
= http
;
933 tunnelState
->al
= al
;
935 comm_add_close_handler(tunnelState
->client
.conn
->fd
,
939 AsyncCall::Pointer timeoutCall
= commCbCall(5, 4, "tunnelTimeout",
940 CommTimeoutCbPtrFun(tunnelTimeout
, tunnelState
));
941 commSetConnTimeout(tunnelState
->client
.conn
, Config
.Timeout
.lifetime
, timeoutCall
);
943 peerSelect(&(tunnelState
->serverDestinations
), request
, al
,
945 tunnelPeerSelectComplete
,
950 TunnelStateData::connectToPeer()
952 const Comm::ConnectionPointer
&srv
= server
.conn
;
955 if (CachePeer
*p
= srv
->getPeer()) {
957 AsyncCall::Pointer callback
= asyncCall(5,4,
958 "TunnelStateData::ConnectedToPeer",
959 MyAnswerDialer(&TunnelStateData::connectedToPeer
, this));
960 Ssl::PeerConnector
*connector
=
961 new Ssl::PeerConnector(request
, srv
, client
.conn
, callback
);
962 AsyncJob::Start(connector
); // will call our callback
968 tunnelRelayConnectRequest(srv
, this);
972 /// Ssl::PeerConnector callback
974 TunnelStateData::connectedToPeer(Ssl::PeerConnectorAnswer
&answer
)
976 if (ErrorState
*error
= answer
.error
.get()) {
977 *status_ptr
= error
->httpStatus
;
978 error
->callback
= tunnelErrorComplete
;
979 error
->callback_data
= this;
980 errorSend(client
.conn
, error
);
981 answer
.error
.clear(); // preserve error for errorSendComplete()
985 tunnelRelayConnectRequest(server
.conn
, this);
990 tunnelRelayConnectRequest(const Comm::ConnectionPointer
&srv
, void *data
)
992 TunnelStateData
*tunnelState
= (TunnelStateData
*)data
;
993 assert(!tunnelState
->waitingForConnectExchange());
994 HttpHeader
hdr_out(hoRequest
);
996 HttpStateFlags flags
;
997 debugs(26, 3, HERE
<< srv
<< ", tunnelState=" << tunnelState
);
998 memset(&flags
, '\0', sizeof(flags
));
999 flags
.proxying
= tunnelState
->request
->flags
.proxying
;
1002 mb
.Printf("CONNECT %s HTTP/1.1\r\n", tunnelState
->url
);
1003 HttpStateData::httpBuildRequestHeader(tunnelState
->request
.getRaw(),
1004 NULL
, /* StoreEntry */
1005 tunnelState
->al
, /* AccessLogEntry */
1008 packerToMemInit(&p
, &mb
);
1009 hdr_out
.packInto(&p
);
1012 mb
.append("\r\n", 2);
1014 debugs(11, 2, "Tunnel Server REQUEST: " << tunnelState
->server
.conn
<< ":\n----------\n" <<
1015 Raw("tunnelRelayConnectRequest", mb
.content(), mb
.contentSize()) << "\n----------");
1017 if (tunnelState
->clientExpectsConnectResponse()) {
1018 // hack: blindly tunnel peer response (to our CONNECT request) to the client as ours.
1019 AsyncCall::Pointer writeCall
= commCbCall(5,5, "tunnelConnectedWriteDone",
1020 CommIoCbPtrFun(tunnelConnectedWriteDone
, tunnelState
));
1021 Comm::Write(srv
, &mb
, writeCall
);
1023 // we have to eat the connect response from the peer (so that the client
1024 // does not see it) and only then start shoveling data to the client
1025 AsyncCall::Pointer writeCall
= commCbCall(5,5, "tunnelConnectReqWriteDone",
1026 CommIoCbPtrFun(tunnelConnectReqWriteDone
,
1028 Comm::Write(srv
, &mb
, writeCall
);
1029 tunnelState
->connectReqWriting
= true;
1031 tunnelState
->connectRespBuf
= new MemBuf
;
1032 // SQUID_TCP_SO_RCVBUF: we should not accumulate more than regular I/O buffer
1033 // can hold since any CONNECT response leftovers have to fit into server.buf.
1034 // 2*SQUID_TCP_SO_RCVBUF: HttpMsg::parse() zero-terminates, which uses space.
1035 tunnelState
->connectRespBuf
->init(SQUID_TCP_SO_RCVBUF
, 2*SQUID_TCP_SO_RCVBUF
);
1036 tunnelState
->readConnectResponse();
1038 assert(tunnelState
->waitingForConnectExchange());
1041 AsyncCall::Pointer timeoutCall
= commCbCall(5, 4, "tunnelTimeout",
1042 CommTimeoutCbPtrFun(tunnelTimeout
, tunnelState
));
1043 commSetConnTimeout(srv
, Config
.Timeout
.read
, timeoutCall
);
1047 tunnelPeerSelectComplete(Comm::ConnectionList
*peer_paths
, ErrorState
*err
, void *data
)
1049 TunnelStateData
*tunnelState
= (TunnelStateData
*)data
;
1051 if (peer_paths
== NULL
|| peer_paths
->size() < 1) {
1052 debugs(26, 3, HERE
<< "No paths found. Aborting CONNECT");
1054 err
= new ErrorState(ERR_CANNOT_FORWARD
, Http::scServiceUnavailable
, tunnelState
->request
.getRaw());
1056 *tunnelState
->status_ptr
= err
->httpStatus
;
1057 err
->callback
= tunnelErrorComplete
;
1058 err
->callback_data
= tunnelState
;
1059 errorSend(tunnelState
->client
.conn
, err
);
1064 GetMarkingsToServer(tunnelState
->request
.getRaw(), *tunnelState
->serverDestinations
[0]);
1066 if (tunnelState
->request
!= NULL
)
1067 tunnelState
->request
->hier
.startPeerClock();
1069 debugs(26, 3, HERE
<< "paths=" << peer_paths
->size() << ", p[0]={" << (*peer_paths
)[0] << "}, serverDest[0]={" <<
1070 tunnelState
->serverDestinations
[0] << "}");
1072 AsyncCall::Pointer call
= commCbCall(26,3, "tunnelConnectDone", CommConnectCbPtrFun(tunnelConnectDone
, tunnelState
));
1073 Comm::ConnOpener
*cs
= new Comm::ConnOpener(tunnelState
->serverDestinations
[0], call
, Config
.Timeout
.connect
);
1074 cs
->setHost(tunnelState
->url
);
1075 AsyncJob::Start(cs
);
1078 CBDATA_CLASS_INIT(TunnelStateData
);
1081 TunnelStateData::noConnections() const
1083 return !Comm::IsConnOpen(server
.conn
) && !Comm::IsConnOpen(client
.conn
);
1088 TunnelStateData::Connection::setDelayId(DelayId
const &newDelay
)
1097 switchToTunnel(HttpRequest
*request
, Comm::ConnectionPointer
&clientConn
, Comm::ConnectionPointer
&srvConn
)
1099 debugs(26,5, "Revert to tunnel FD " << clientConn
->fd
<< " with FD " << srvConn
->fd
);
1100 /* Create state structure. */
1101 TunnelStateData
*tunnelState
= NULL
;
1102 const char *url
= urlCanonical(request
);
1104 debugs(26, 3, request
->method
<< " " << url
<< " " << request
->http_ver
);
1105 ++statCounter
.server
.all
.requests
;
1106 ++statCounter
.server
.other
.requests
;
1108 tunnelState
= new TunnelStateData
;
1109 tunnelState
->url
= xstrdup(url
);
1110 tunnelState
->request
= request
;
1111 tunnelState
->server
.size_ptr
= NULL
; //Set later if ClientSocketContext is available
1113 // Temporary static variable to store the unneeded for our case status code
1114 static int status_code
= 0;
1115 tunnelState
->status_ptr
= &status_code
;
1116 tunnelState
->client
.conn
= clientConn
;
1118 ConnStateData
*conn
;
1119 if ((conn
= request
->clientConnectionManager
.get())) {
1120 ClientSocketContext::Pointer context
= conn
->getCurrentContext();
1121 if (context
!= NULL
&& context
->http
!= NULL
) {
1122 tunnelState
->logTag_ptr
= &context
->http
->logType
;
1123 tunnelState
->server
.size_ptr
= &context
->http
->out
.size
;
1126 /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */
1127 if (srvConn
->getPeer() && srvConn
->getPeer()->options
.no_delay
)
1128 tunnelState
->server
.setDelayId(DelayId::DelayClient(context
->http
));
1133 comm_add_close_handler(tunnelState
->client
.conn
->fd
,
1137 AsyncCall::Pointer timeoutCall
= commCbCall(5, 4, "tunnelTimeout",
1138 CommTimeoutCbPtrFun(tunnelTimeout
, tunnelState
));
1139 commSetConnTimeout(tunnelState
->client
.conn
, Config
.Timeout
.lifetime
, timeoutCall
);
1140 fd_table
[clientConn
->fd
].read_method
= &default_read_method
;
1141 fd_table
[clientConn
->fd
].write_method
= &default_write_method
;
1143 tunnelState
->request
->hier
.note(srvConn
, tunnelState
->getHost());
1145 tunnelState
->server
.conn
= srvConn
;
1146 tunnelState
->request
->peer_host
= srvConn
->getPeer() ? srvConn
->getPeer()->host
: NULL
;
1147 comm_add_close_handler(srvConn
->fd
, tunnelServerClosed
, tunnelState
);
1149 debugs(26, 4, "determine post-connect handling pathway.");
1150 if (srvConn
->getPeer()) {
1151 tunnelState
->request
->peer_login
= srvConn
->getPeer()->login
;
1152 tunnelState
->request
->flags
.proxying
= !(srvConn
->getPeer()->options
.originserver
);
1154 tunnelState
->request
->peer_login
= NULL
;
1155 tunnelState
->request
->flags
.proxying
= false;
1158 timeoutCall
= commCbCall(5, 4, "tunnelTimeout",
1159 CommTimeoutCbPtrFun(tunnelTimeout
, tunnelState
));
1160 commSetConnTimeout(srvConn
, Config
.Timeout
.read
, timeoutCall
);
1161 fd_table
[srvConn
->fd
].read_method
= &default_read_method
;
1162 fd_table
[srvConn
->fd
].write_method
= &default_write_method
;
1164 SSL
*ssl
= fd_table
[srvConn
->fd
].ssl
;
1166 BIO
*b
= SSL_get_rbio(ssl
);
1167 Ssl::ServerBio
*srvBio
= static_cast<Ssl::ServerBio
*>(b
->ptr
);
1168 const MemBuf
&buf
= srvBio
->rBufData();
1170 AsyncCall::Pointer call
= commCbCall(5,5, "tunnelConnectedWriteDone",
1171 CommIoCbPtrFun(tunnelConnectedWriteDone
, tunnelState
));
1172 Comm::Write(tunnelState
->client
.conn
, buf
.content(), buf
.contentSize(), call
, NULL
);
1174 #endif //USE_OPENSSL