*/
#include "squid.h"
-#include "errorpage.h"
-#include "HttpRequest.h"
-#include "fde.h"
+#include "acl/FilledChecklist.h"
+#include "Array.h"
#include "comm.h"
+#include "comm/Connection.h"
+#include "comm/ConnOpener.h"
#include "comm/Write.h"
+#include "client_side.h"
#include "client_side_request.h"
- #if DELAY_POOLS
-#include "acl/FilledChecklist.h"
+ #if USE_DELAY_POOLS
#include "DelayId.h"
#endif
-#include "client_side.h"
-#include "MemBuf.h"
+#include "errorpage.h"
+#include "fde.h"
+#include "HttpRequest.h"
#include "http.h"
-#include "ip/tools.h"
+#include "MemBuf.h"
+#include "PeerSelectState.h"
class TunnelStateData
{
{
public:
- Connection() : len (0),buf ((char *)xmalloc(SQUID_TCP_SO_RCVBUF)), size_ptr(NULL), fd_(-1) {}
+ Connection() : len (0), buf ((char *)xmalloc(SQUID_TCP_SO_RCVBUF)), size_ptr(NULL) {}
~Connection();
- int const & fd() const { return fd_;}
- void fd(int const newFD);
int bytesWanted(int lower=0, int upper = INT_MAX) const;
void bytesIn(int const &);
- #if DELAY_POOLS
+ #if USE_DELAY_POOLS
void setDelayId(DelayId const &);
#endif
char *buf;
int64_t *size_ptr; /* pointer to size in an ConnStateData for logging */
+ Comm::ConnectionPointer conn; ///< The currently connected connection.
+
private:
- #if DELAY_POOLS
-
- int fd_;
+ #if USE_DELAY_POOLS
-
DelayId delayId;
#endif
}
} else if (cbdataReferenceValid(this)) {
AsyncCall::Pointer call = commCbCall(5,5, "SomeTunnelWriteHandler",
- CommIoCbPtrFun(completion, this));
+ CommIoCbPtrFun(completion, this));
- Comm::Write(to.fd(), from.buf, len, call, NULL);
+ Comm::Write(to.conn, from.buf, len, call, NULL);
}
cbdataInternalUnlock(this); /* ??? */
HttpRequest *request = tunnelState->request;
ErrorState *err = NULL;
- #if DELAY_POOLS
- request->recordLookup(dns);
++#if USE_DELAY_POOLS
+ /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */
+ if (conn->getPeer() && conn->getPeer()->options.no_delay)
+ tunnelState->server.setDelayId(DelayId());
+#endif
- if (tunnelState->servers->_peer)
- hierarchyNote(&tunnelState->request->hier, tunnelState->servers->code,
- tunnelState->servers->_peer->host);
+ if (conn != NULL && conn->getPeer())
+ hierarchyNote(&tunnelState->request->hier, conn->peerType, conn->getPeer()->host);
else if (Config.onoff.log_ip_on_direct)
- hierarchyNote(&tunnelState->request->hier, tunnelState->servers->code,
- fd_table[tunnelState->server.fd()].ipaddr);
+ hierarchyNote(&tunnelState->request->hier, conn->peerType, fd_table[conn->fd].ipaddr);
else
- hierarchyNote(&tunnelState->request->hier, tunnelState->servers->code,
- tunnelState->host);
-
- if (status == COMM_ERR_DNS) {
- debugs(26, 4, "tunnelConnect: Unknown host: " << tunnelState->host);
- err = errorCon(ERR_DNS_FAIL, HTTP_NOT_FOUND, request);
- *tunnelState->status_ptr = HTTP_NOT_FOUND;
- err->dnsError = dns.error;
- err->callback = tunnelErrorComplete;
- err->callback_data = tunnelState;
- errorSend(tunnelState->client.fd(), err);
- } else if (status != COMM_OK) {
- err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
- *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE;
- err->xerrno = xerrno;
- err->port = tunnelState->port;
- err->callback = tunnelErrorComplete;
- err->callback_data = tunnelState;
- errorSend(tunnelState->client.fd(), err);
- } else {
- if (tunnelState->servers->_peer)
- tunnelProxyConnected(tunnelState->server.fd(), tunnelState);
- else {
- tunnelConnected(tunnelState->server.fd(), tunnelState);
+ hierarchyNote(&tunnelState->request->hier, conn->peerType, tunnelState->getHost());
+
+ // TODO: merge this into hierarchyNote with a conn parameter instead of peerType
+ request->hier.peer_local_port = conn->local.GetPort(); // for %<lp logging
+
+ if (status != COMM_OK) {
+ /* At this point only the TCP handshake has failed. no data has been passed.
+ * we are allowed to re-try the TCP-level connection to alternate IPs for CONNECT.
+ */
+ tunnelState->serverDestinations.shift();
+ if (status != COMM_TIMEOUT && tunnelState->serverDestinations.size() > 0) {
+ /* Try another IP of this destination host */
+ AsyncCall::Pointer call = commCbCall(26,3, "tunnelConnectDone", CommConnectCbPtrFun(tunnelConnectDone, tunnelState));
+ Comm::ConnOpener *cs = new Comm::ConnOpener(tunnelState->serverDestinations[0], call, Config.Timeout.connect);
+ cs->setHost(tunnelState->url);
+ AsyncJob::Start(cs);
+ } else {
+ err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
+ *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE;
+ err->xerrno = xerrno;
+ // on timeout is this still: err->xerrno = ETIMEDOUT;
+ err->port = conn->remote.GetPort();
+ err->callback = tunnelErrorComplete;
+ err->callback_data = tunnelState;
+ errorSend(tunnelState->client.conn, err);
}
+ return;
+ }
+
+ tunnelState->server.conn = conn;
+ request->peer_host = conn->getPeer() ? conn->getPeer()->host : NULL;
+ comm_add_close_handler(conn->fd, tunnelServerClosed, tunnelState);
+
+ if (conn->getPeer()) {
+ tunnelState->request->peer_login = conn->getPeer()->login;
+ tunnelState->request->flags.proxying = 1;
+ } else {
+ tunnelState->request->peer_login = NULL;
+ tunnelState->request->flags.proxying = 0;
+ }
- commSetTimeout(tunnelState->server.fd(),
- Config.Timeout.read,
- tunnelTimeout,
- tunnelState);
+ if (conn->getPeer())
+ tunnelRelayConnectRequest(conn, tunnelState);
+ else {
+ tunnelConnected(conn, tunnelState);
}
+
+ commSetTimeout(conn->fd, Config.Timeout.read, tunnelTimeout, tunnelState);
}
extern tos_t GetTosToServer(HttpRequest * request);
debugs(26, 3, "tunnelStart: '" << RequestMethodStr(request->method) << " " << url << "'");
statCounter.server.all.requests++;
statCounter.server.other.requests++;
- /* Create socket. */
- Ip::Address temp = getOutgoingAddr(request,NULL);
-
- // if IPv6 is disabled try to force IPv4-only outgoing.
- if (!Ip::EnableIpv6 && !temp.SetIPv4()) {
- debugs(50, 4, "tunnelStart: IPv6 is Disabled. Tunnel failed from " << temp);
- ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
- anErr->xerrno = EAFNOSUPPORT;
- errorSend(fd, anErr);
- return;
- }
-
- // if IPv6 is split-stack, prefer IPv4
- if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK) {
- // NP: This is not a great choice of default,
- // but with the current Internet being IPv4-majority has a higher success rate.
- // if setting to IPv4 fails we dont care, that just means to use IPv6 outgoing.
- temp.SetIPv4();
- }
-
- int flags = COMM_NONBLOCKING;
- if (request->flags.spoof_client_ip) {
- flags |= COMM_TRANSPARENT;
- }
- sock = comm_openex(SOCK_STREAM,
- IPPROTO_TCP,
- temp,
- flags,
- GetTosToServer(request),
- GetNfmarkToServer(request),
- url);
-
- if (sock == COMM_ERROR) {
- debugs(26, 4, "tunnelStart: Failed because we're out of sockets.");
- err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
- *status_ptr = HTTP_INTERNAL_SERVER_ERROR;
- err->xerrno = errno;
- errorSend(fd, err);
- return;
- }
-
- request->hier.peer_local_port = comm_local_port(sock); // for %<lp logging
tunnelState = new TunnelStateData;
- #if DELAY_POOLS
+ #if USE_DELAY_POOLS
-
tunnelState->server.setDelayId(DelayId::DelayClient(http));
#endif
-
tunnelState->url = xstrdup(url);
tunnelState->request = HTTPMSGLOCK(request);
tunnelState->server.size_ptr = size_ptr;
bool
TunnelStateData::noConnections() const
{
- return fd_closed(server.fd()) && fd_closed(client.fd());
+ return !Comm::IsConnOpen(server.conn) && !Comm::IsConnOpen(client.conn);
}
- #if DELAY_POOLS
+ #if USE_DELAY_POOLS
void
TunnelStateData::Connection::setDelayId(DelayId const &newDelay)
{