*/
#include "squid.h"
+#include "base/AsyncCall.h"
#include "StoreIOBuffer.h"
#include "comm.h"
#include "event.h"
#include "fde.h"
#include "comm/AcceptLimiter.h"
#include "comm/comm_internal.h"
-#include "comm/ListenStateData.h"
+#include "comm/Connection.h"
#include "CommIO.h"
#include "CommRead.h"
-#include "ConnectionDetail.h"
#include "MemBuf.h"
#include "pconn.h"
#include "SquidTime.h"
{
}
-class ConnectStateData
-{
-
-public:
- void *operator new (size_t);
- void operator delete (void *);
- static void Connect (int fd, void *me);
- void connect();
- void callCallback(comm_err_t status, int xerrno);
- void defaults();
-
-// defaults given by client
- char *host;
- u_short default_port;
- Ip::Address default_addr;
- // NP: CANNOT store the default addr:port together as it gets set/reset differently.
-
- DnsLookupDetails dns; ///< host lookup details
- Ip::Address S;
- AsyncCall::Pointer callback;
-
- int fd;
- int tries;
- int addrcount;
- int connstart;
-
-private:
- int commResetFD();
- int commRetryConnect();
- CBDATA_CLASS(ConnectStateData);
-};
-
/* STATIC */
static DescriptorSet *TheHalfClosed = NULL; /// the set of half-closed FDs
static void commSetTcpNoDelay(int);
#endif
static void commSetTcpRcvbuf(int, int);
-static PF commConnectFree;
static PF commHandleWrite;
-static IPH commConnectDnsHandle;
typedef enum {
COMM_CB_READ = 1,
temp.FreeAddrInfo(addr);
- F->local_addr.SetPort(temp.GetPort());
-
-#if 0 // seems to undo comm_open actions on the FD ...
- // grab default socket information for this address
- temp.GetAddrInfo(addr);
-
- F->sock_family = addr->ai_family;
-
- temp.FreeAddrInfo(addr);
-#endif
+ if (F->local_addr.IsAnyAddr()) {
+ /* save the whole local address, not just the port. */
+ F->local_addr = temp;
+ } else {
+ F->local_addr.SetPort(temp.GetPort());
+ }
debugs(5, 6, "comm_local_port: FD " << fd << ": port " << F->local_addr.GetPort() << "(family=" << F->sock_family << ")");
return F->local_addr.GetPort();
*/
}
-
-CBDATA_CLASS_INIT(ConnectStateData);
-
-void *
-ConnectStateData::operator new (size_t size)
-{
- CBDATA_INIT_TYPE(ConnectStateData);
- return cbdataAlloc(ConnectStateData);
-}
-
-void
-ConnectStateData::operator delete (void *address)
-{
- cbdataFree(address);
-}
-
-
-
-void
-commConnectStart(int fd, const char *host, u_short port, AsyncCall::Pointer &cb)
-{
- debugs(cb->debugSection, cb->debugLevel, "commConnectStart: FD " << fd <<
- ", cb " << cb << ", " << host << ":" << port); // TODO: just print *cb
-
- ConnectStateData *cs;
- cs = new ConnectStateData;
- cs->fd = fd;
- cs->host = xstrdup(host);
- cs->default_port = port;
- cs->callback = cb;
-
- comm_add_close_handler(fd, commConnectFree, cs);
- ipcache_nbgethostbyname(host, commConnectDnsHandle, cs);
-}
-
-// TODO: Remove this and similar callback registration functions by replacing
-// (callback,data) parameters with an AsyncCall so that we do not have to use
-// a generic call name and debug level when creating an AsyncCall. This will
-// also cut the number of callback registration routines in half.
-void
-commConnectStart(int fd, const char *host, u_short port, CNCB * callback, void *data)
-{
- debugs(5, 5, "commConnectStart: FD " << fd << ", data " << data << ", " << host << ":" << port);
- AsyncCall::Pointer call = commCbCall(5,3,
- "SomeCommConnectHandler", CommConnectCbPtrFun(callback, data));
- commConnectStart(fd, host, port, call);
-}
-
-static void
-commConnectDnsHandle(const ipcache_addrs *ia, const DnsLookupDetails &details, void *data)
-{
- ConnectStateData *cs = (ConnectStateData *)data;
- cs->dns = details;
-
- if (ia == NULL) {
- debugs(5, 3, "commConnectDnsHandle: Unknown host: " << cs->host);
- cs->callCallback(COMM_ERR_DNS, 0);
- return;
- }
-
- assert(ia->cur < ia->count);
-
- cs->default_addr = ia->in_addrs[ia->cur];
-
- if (Config.onoff.balance_on_multiple_ip)
- ipcacheCycleAddr(cs->host, NULL);
-
- cs->addrcount = ia->count;
-
- cs->connstart = squid_curtime;
-
- cs->connect();
-}
-
-void
-ConnectStateData::callCallback(comm_err_t status, int xerrno)
-{
- debugs(5, 3, "commConnectCallback: FD " << fd);
-
- comm_remove_close_handler(fd, commConnectFree, this);
- commSetTimeout(fd, -1, NULL, NULL);
-
- typedef CommConnectCbParams Params;
- Params ¶ms = GetCommParams<Params>(callback);
- params.fd = fd;
- params.dns = dns;
- params.flag = status;
- params.xerrno = xerrno;
- ScheduleCallHere(callback);
- callback = NULL;
-
- commConnectFree(fd, this);
-}
-
-static void
-commConnectFree(int fd, void *data)
-{
- ConnectStateData *cs = (ConnectStateData *)data;
- debugs(5, 3, "commConnectFree: FD " << fd);
-// delete cs->callback;
- cs->callback = NULL;
- safe_free(cs->host);
- delete cs;
-}
-
-static void
-copyFDFlags(int to, fde *F)
-{
- if (F->flags.close_on_exec)
- commSetCloseOnExec(to);
-
- if (F->flags.nonblocking)
- commSetNonBlocking(to);
-
-#ifdef TCP_NODELAY
-
- if (F->flags.nodelay)
- commSetTcpNoDelay(to);
-
-#endif
-
- if (Config.tcpRcvBufsz > 0)
- commSetTcpRcvbuf(to, Config.tcpRcvBufsz);
-}
-
-/* Reset FD so that we can connect() again */
-int
-ConnectStateData::commResetFD()
-{
-
-// XXX: do we have to check this?
-//
-// if (!cbdataReferenceValid(callback.data))
-// return 0;
-
- statCounter.syscalls.sock.sockets++;
-
- fde *F = &fd_table[fd];
-
- struct addrinfo *AI = NULL;
- F->local_addr.GetAddrInfo(AI);
- int new_family = AI->ai_family;
-
- int fd2 = socket(new_family, AI->ai_socktype, AI->ai_protocol);
-
- if (fd2 < 0) {
- debugs(5, DBG_CRITICAL, HERE << "WARNING: FD " << fd2 << " socket failed to allocate: " << xstrerror());
-
- if (ENFILE == errno || EMFILE == errno)
- fdAdjustReserved();
-
- F->local_addr.FreeAddrInfo(AI);
- return 0;
- }
-
-#ifdef _SQUID_MSWIN_
-
- /* On Windows dup2() can't work correctly on Sockets, the */
- /* workaround is to close the destination Socket before call them. */
- close(fd);
-
-#endif
-
- if (dup2(fd2, fd) < 0) {
- debugs(5, DBG_CRITICAL, HERE << "WARNING: dup2(FD " << fd2 << ", FD " << fd << ") failed: " << xstrerror());
-
- if (ENFILE == errno || EMFILE == errno)
- fdAdjustReserved();
-
- close(fd2);
-
- F->local_addr.FreeAddrInfo(AI);
- return 0;
- }
- commResetSelect(fd);
-
- close(fd2);
-
- debugs(50, 3, "commResetFD: Reset socket FD " << fd << "->" << fd2 << " : family=" << new_family );
-
- /* INET6: copy the new sockets family type to the FDE table */
- F->sock_family = new_family;
-
- F->flags.called_connect = 0;
-
- /*
- * yuck, this has assumptions about comm_open() arguments for
- * the original socket
- */
-
- /* MUST be done before binding or face OS Error: "(99) Cannot assign requested address"... */
- if ( F->flags.transparent ) {
- comm_set_transparent(fd);
- }
-
- if (commBind(fd, *AI) != COMM_OK) {
- debugs(5, DBG_CRITICAL, "WARNING: Reset of FD " << fd << " for " << F->local_addr << " failed to bind: " << xstrerror());
- F->local_addr.FreeAddrInfo(AI);
- return 0;
- }
- F->local_addr.FreeAddrInfo(AI);
-
- if (F->tos)
- comm_set_tos(fd, F->tos);
-
- if ( Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && F->local_addr.IsIPv6() )
- comm_set_v6only(fd, 1);
-
- copyFDFlags(fd, F);
-
- return 1;
-}
-
-int
-ConnectStateData::commRetryConnect()
-{
- assert(addrcount > 0);
-
- if (addrcount == 1) {
- if (tries >= Config.retry.maxtries)
- return 0;
-
- if (squid_curtime - connstart > Config.Timeout.connect)
- return 0;
- } else {
- if (tries > addrcount) {
- /* Flush bad address count in case we are
- * skipping over incompatible protocol
- */
- ipcacheMarkAllGood(host);
- return 0;
- }
- }
-
- return commResetFD();
-}
-
-static void
-commReconnect(void *data)
-{
- ConnectStateData *cs = (ConnectStateData *)data;
- ipcache_nbgethostbyname(cs->host, commConnectDnsHandle, cs);
-}
-
-/** Connect SOCK to specified DEST_PORT at DEST_HOST. */
-void
-ConnectStateData::Connect(int fd, void *me)
-{
- ConnectStateData *cs = (ConnectStateData *)me;
- assert (cs->fd == fd);
- cs->connect();
-}
-
-void
-ConnectStateData::defaults()
-{
- S = default_addr;
- S.SetPort(default_port);
-}
-
-void
-ConnectStateData::connect()
-{
- defaults();
-
- debugs(5,5, HERE << "to " << S);
-
- switch (comm_connect_addr(fd, S) ) {
-
- case COMM_INPROGRESS:
- debugs(5, 5, HERE << "FD " << fd << ": COMM_INPROGRESS");
- commSetSelect(fd, COMM_SELECT_WRITE, ConnectStateData::Connect, this, 0);
- break;
-
- case COMM_OK:
- debugs(5, 5, HERE << "FD " << fd << ": COMM_OK - connected");
- ipcacheMarkGoodAddr(host, S);
- callCallback(COMM_OK, 0);
- break;
-
- case COMM_ERR_PROTOCOL:
- debugs(5, 5, HERE "FD " << fd << ": COMM_ERR_PROTOCOL - try again");
- /* problem using the desired protocol over this socket.
- * skip to the next address and hope it's more compatible
- * but do not mark the current address as bad
- */
- tries++;
- if (commRetryConnect()) {
- /* Force an addr cycle to move forward to the next possible address */
- ipcacheCycleAddr(host, NULL);
- eventAdd("commReconnect", commReconnect, this, this->addrcount == 1 ? 0.05 : 0.0, 0);
- } else {
- debugs(5, 5, HERE << "FD " << fd << ": COMM_ERR_PROTOCOL - ERR tried too many times already.");
- callCallback(COMM_ERR_CONNECT, errno);
- }
- break;
-
- default:
- debugs(5, 5, HERE "FD " << fd << ": * - try again");
- tries++;
- ipcacheMarkBadAddr(host, S);
-
-#if USE_ICMP
- if (Config.onoff.test_reachability)
- netdbDeleteAddrNetwork(S);
-#endif
-
- if (commRetryConnect()) {
- eventAdd("commReconnect", commReconnect, this, this->addrcount == 1 ? 0.05 : 0.0, 0);
- } else {
- debugs(5, 5, HERE << "FD " << fd << ": * - ERR tried too many times already.");
- callCallback(COMM_ERR_CONNECT, errno);
- }
- }
-}
-/*
+#if 0
int
commSetTimeout_old(int fd, int timeout, PF * handler, void *data)
{
return F->timeout;
}
-*/
+#endif
int
commSetTimeout(int fd, int timeout, PF * handler, void *data)
}
-int commSetTimeout(int fd, int timeout, AsyncCall::Pointer &callback)
+int
+commSetTimeout(int fd, int timeout, AsyncCall::Pointer &callback)
{
debugs(5, 3, HERE << "FD " << fd << " timeout " << timeout);
assert(fd >= 0);
#endif
-/*
+/**
* enable linger with time of 0 so that when the socket is
* closed, TCP generates a RESET
*/
void
-comm_reset_close(int fd)
+comm_reset_close(Comm::ConnectionPointer &conn)
{
+ struct linger L;
+ L.l_onoff = 1;
+ L.l_linger = 0;
+
+ if (setsockopt(conn->fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0)
+ debugs(50, DBG_CRITICAL, "ERROR: Closing FD " << conn->fd << " with TCP RST: " << xstrerror());
+
+ conn->close();
+}
+// Legacy close function.
+void
+old_comm_reset_close(int fd)
+{
struct linger L;
L.l_onoff = 1;
L.l_linger = 0;
ScheduleCallHere(callback);
} else {
debugs(5, 5, "commCloseAllSockets: FD " << fd << ": calling comm_reset_close()");
- comm_reset_close(fd);
+ old_comm_reset_close(fd);
}
}
}
cancelled = true;
}
-ConnectionDetail::ConnectionDetail() : me(), peer()
-{
-}
-
int
CommSelectEngine::checkEvents(int timeout)
{