/*
- * DEBUG: section 05 Listener Socket Handler
- * AUTHOR: Harvest Derived
+ * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
*
- * SQUID Web Proxy Cache http://www.squid-cache.org/
- * ----------------------------------------------------------
- *
- * Squid is the result of efforts by numerous individuals from
- * the Internet community; see the CONTRIBUTORS file for full
- * details. Many organizations have provided support for Squid's
- * development; see the SPONSORS file for full details. Squid is
- * Copyrighted (C) 2001 by the Regents of the University of
- * California; see the COPYRIGHT file for full details. Squid
- * incorporates software developed and/or copyrighted by other
- * sources; see the CREDITS file for full details.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
- *
- *
- * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
*/
+/* DEBUG: section 05 Listener Socket Handler */
+
#include "squid.h"
+#include "acl/FilledChecklist.h"
+#include "anyp/PortCfg.h"
#include "base/TextException.h"
#include "client_db.h"
#include "comm/AcceptLimiter.h"
-#include "CommCalls.h"
#include "comm/comm_internal.h"
#include "comm/Connection.h"
#include "comm/Loops.h"
#include "comm/TcpAcceptor.h"
+#include "CommCalls.h"
#include "eui/Config.h"
#include "fd.h"
#include "fde.h"
#include "globals.h"
#include "ip/Intercept.h"
+#include "ip/QosConfig.h"
+#include "log/access_log.h"
+#include "MasterXaction.h"
#include "profiler/Profiler.h"
#include "SquidConfig.h"
#include "SquidTime.h"
#include "StatCounters.h"
-#if HAVE_ERRNO_H
-#include <errno.h>
-#endif
+#include <cerrno>
#ifdef HAVE_NETINET_TCP_H
// required for accept_filter to build.
#include <netinet/tcp.h>
CBDATA_NAMESPACED_CLASS_INIT(Comm, TcpAcceptor);
-Comm::TcpAcceptor::TcpAcceptor(const Comm::ConnectionPointer &newConn, const char *note, const Subscription::Pointer &aSub) :
- AsyncJob("Comm::TcpAcceptor"),
- errcode(0),
- isLimited(0),
- theCallSub(aSub),
- conn(newConn)
+Comm::TcpAcceptor::TcpAcceptor(const Comm::ConnectionPointer &newConn, const char *, const Subscription::Pointer &aSub) :
+ AsyncJob("Comm::TcpAcceptor"),
+ errcode(0),
+ isLimited(0),
+ theCallSub(aSub),
+ conn(newConn),
+ listenPort_()
+{}
+
+Comm::TcpAcceptor::TcpAcceptor(const AnyP::PortCfgPointer &p, const char *, const Subscription::Pointer &aSub) :
+ AsyncJob("Comm::TcpAcceptor"),
+ errcode(0),
+ isLimited(0),
+ theCallSub(aSub),
+ conn(p->listenConn),
+ listenPort_(p)
{}
void
setListen();
+ conn->noteStart();
+
// if no error so far start accepting connections.
if (errcode == 0)
SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
{
debugs(5,5, HERE);
unsubscribe("swanSong");
+ if (IsConnOpen(conn)) {
+ if (closer_ != NULL)
+ comm_remove_close_handler(conn->fd, closer_);
+ conn->close();
+ }
+
conn = NULL;
AcceptLimiter::Instance().removeDead(this);
AsyncJob::swanSong();
static MemBuf buf;
buf.reset();
- buf.Printf(" FD %d, %s",conn->fd, ipbuf);
+ buf.appendf(" FD %d, %s",conn->fd, ipbuf);
const char *jobStatus = AsyncJob::status();
buf.append(jobStatus, strlen(jobStatus));
void
Comm::TcpAcceptor::setListen()
{
- errcode = 0; // reset local errno copy.
+ errcode = errno = 0;
if (listen(conn->fd, Squid_MaxFD >> 2) < 0) {
- debugs(50, DBG_CRITICAL, "ERROR: listen(" << status() << ", " << (Squid_MaxFD >> 2) << "): " << xstrerror());
errcode = errno;
+ debugs(50, DBG_CRITICAL, "ERROR: listen(" << status() << ", " << (Squid_MaxFD >> 2) << "): " << xstrerr(errcode));
return;
}
bzero(&afa, sizeof(afa));
debugs(5, DBG_IMPORTANT, "Installing accept filter '" << Config.accept_filter << "' on " << conn);
xstrncpy(afa.af_name, Config.accept_filter, sizeof(afa.af_name));
- if (setsockopt(conn->fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa)) < 0)
- debugs(5, DBG_CRITICAL, "WARNING: SO_ACCEPTFILTER '" << Config.accept_filter << "': '" << xstrerror());
+ if (setsockopt(conn->fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa)) < 0) {
+ int xerrno = errno;
+ debugs(5, DBG_CRITICAL, "WARNING: SO_ACCEPTFILTER '" << Config.accept_filter << "': '" << xstrerr(xerrno));
+ }
#elif defined(TCP_DEFER_ACCEPT)
int seconds = 30;
if (strncmp(Config.accept_filter, "data=", 5) == 0)
seconds = atoi(Config.accept_filter + 5);
- if (setsockopt(conn->fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &seconds, sizeof(seconds)) < 0)
- debugs(5, DBG_CRITICAL, "WARNING: TCP_DEFER_ACCEPT '" << Config.accept_filter << "': '" << xstrerror());
+ if (setsockopt(conn->fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &seconds, sizeof(seconds)) < 0) {
+ int xerrno = errno;
+ debugs(5, DBG_CRITICAL, "WARNING: TCP_DEFER_ACCEPT '" << Config.accept_filter << "': '" << xstrerr(xerrno));
+ }
#else
debugs(5, DBG_CRITICAL, "WARNING: accept_filter not supported on your OS");
#endif
}
+
+#if 0
+ // Untested code.
+ // Set TOS if needed.
+ // To correctly implement TOS values on listening sockets, probably requires
+ // more work to inherit TOS values to created connection objects.
+ if (conn->tos)
+ Ip::Qos::setSockTos(conn, conn->tos)
+#if SO_MARK
+ if (conn->nfmark)
+ Ip::Qos::setSockNfmark(conn, conn->nfmark);
+#endif
+#endif
+
+ typedef CommCbMemFunT<Comm::TcpAcceptor, CommCloseCbParams> Dialer;
+ closer_ = JobCallback(5, 4, Dialer, this, Comm::TcpAcceptor::handleClosure);
+ comm_add_close_handler(conn->fd, closer_);
+}
+
+/// called when listening descriptor is closed by an external force
+/// such as clientHttpConnectionsClose()
+void
+Comm::TcpAcceptor::handleClosure(const CommCloseCbParams &)
+{
+ closer_ = NULL;
+ conn = NULL;
+ Must(done());
}
/**
} catch (const std::exception &e) {
fatalf("FATAL: error while accepting new client connection: %s\n", e.what());
} catch (...) {
- fatal("FATAL: error while accepting new client connection: [unkown]\n");
+ fatal("FATAL: error while accepting new client connection: [unknown]\n");
}
}
return false;
}
+static void
+logAcceptError(const Comm::ConnectionPointer &conn)
+{
+ AccessLogEntry::Pointer al = new AccessLogEntry;
+ al->tcpClient = conn;
+ al->url = "error:accept-client-connection";
+ ACLFilledChecklist ch(nullptr, nullptr, nullptr);
+ ch.src_addr = conn->remote;
+ ch.my_addr = conn->local;
+ accessLogLog(al, &ch);
+}
+
void
Comm::TcpAcceptor::acceptOne()
{
/* Accept a new connection */
ConnectionPointer newConnDetails = new Connection();
- const comm_err_t flag = oldAccept(newConnDetails);
+ const Comm::Flag flag = oldAccept(newConnDetails);
/* Check for errors */
if (!newConnDetails->isOpen()) {
- if (flag == COMM_NOMESSAGE) {
+ if (flag == Comm::NOMESSAGE) {
/* register interest again */
debugs(5, 5, HERE << "try later: " << conn << " handler Subscription: " << theCallSub);
SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
// A non-recoverable error; notify the caller */
debugs(5, 5, HERE << "non-recoverable error:" << status() << " handler Subscription: " << theCallSub);
+ if (intendedForUserConnections())
+ logAcceptError(newConnDetails);
notify(flag, newConnDetails);
mustStop("Listener socket closed");
return;
}
void
-Comm::TcpAcceptor::notify(const comm_err_t flag, const Comm::ConnectionPointer &newConnDetails) const
+Comm::TcpAcceptor::notify(const Comm::Flag flag, const Comm::ConnectionPointer &newConnDetails) const
{
- // listener socket handlers just abandon the port with COMM_ERR_CLOSING
+ // listener socket handlers just abandon the port with Comm::ERR_CLOSING
// it should only happen when this object is deleted...
- if (flag == COMM_ERR_CLOSING) {
+ if (flag == Comm::ERR_CLOSING) {
return;
}
if (theCallSub != NULL) {
AsyncCall::Pointer call = theCallSub->callback();
CommAcceptCbParams ¶ms = GetCommParams<CommAcceptCbParams>(call);
+ params.xaction = new MasterXaction(XactionInitiator::initClient);
+ params.xaction->squidPort = listenPort_;
params.fd = conn->fd;
- params.conn = newConnDetails;
+ params.conn = params.xaction->tcpClient = newConnDetails;
params.flag = flag;
params.xerrno = errcode;
ScheduleCallHere(call);
* accept() and process
* Wait for an incoming connection on our listener socket.
*
- * \retval COMM_OK success. details parameter filled.
- * \retval COMM_NOMESSAGE attempted accept() but nothing useful came in.
- * \retval COMM_ERROR an outright failure occured.
+ * \retval Comm::OK success. details parameter filled.
+ * \retval Comm::NOMESSAGE attempted accept() but nothing useful came in.
+ * \retval Comm::COMM_ERROR an outright failure occured.
* Or if this client has too many connections already.
*/
-comm_err_t
+Comm::Flag
Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details)
{
PROF_start(comm_accept);
++statCounter.syscalls.sock.accepts;
int sock;
struct addrinfo *gai = NULL;
- Ip::Address::InitAddrInfo(gai);
+ Ip::Address::InitAddr(gai);
errcode = 0; // reset local errno copy.
if ((sock = accept(conn->fd, gai->ai_addr, &gai->ai_addrlen)) < 0) {
errcode = errno; // store last accept errno locally.
- Ip::Address::FreeAddrInfo(gai);
+ Ip::Address::FreeAddr(gai);
PROF_stop(comm_accept);
- if (ignoreErrno(errno)) {
- debugs(50, 5, HERE << status() << ": " << xstrerror());
- return COMM_NOMESSAGE;
+ if (ignoreErrno(errcode)) {
+ debugs(50, 5, status() << ": " << xstrerr(errcode));
+ return Comm::NOMESSAGE;
} else if (ENFILE == errno || EMFILE == errno) {
- debugs(50, 3, HERE << status() << ": " << xstrerror());
- return COMM_ERROR;
+ debugs(50, 3, status() << ": " << xstrerr(errcode));
+ return Comm::COMM_ERROR;
} else {
- debugs(50, DBG_IMPORTANT, HERE << status() << ": " << xstrerror());
- return COMM_ERROR;
+ debugs(50, DBG_IMPORTANT, MYNAME << status() << ": " << xstrerr(errcode));
+ return Comm::COMM_ERROR;
}
}
if ( Config.client_ip_max_connections >= 0) {
if (clientdbEstablished(details->remote, 0) > Config.client_ip_max_connections) {
debugs(50, DBG_IMPORTANT, "WARNING: " << details->remote << " attempting more than " << Config.client_ip_max_connections << " connections.");
- Ip::Address::FreeAddrInfo(gai);
- return COMM_ERROR;
+ Ip::Address::FreeAddr(gai);
+ PROF_stop(comm_accept);
+ return Comm::COMM_ERROR;
}
}
// lookup the local-end details of this new connection
- Ip::Address::InitAddrInfo(gai);
+ Ip::Address::InitAddr(gai);
details->local.setEmpty();
if (getsockname(sock, gai->ai_addr, &gai->ai_addrlen) != 0) {
- debugs(50, DBG_IMPORTANT, "ERROR: getsockname() failed to locate local-IP on " << details << ": " << xstrerror());
- Ip::Address::FreeAddrInfo(gai);
- return COMM_ERROR;
+ int xerrno = errno;
+ debugs(50, DBG_IMPORTANT, "ERROR: getsockname() failed to locate local-IP on " << details << ": " << xstrerr(xerrno));
+ Ip::Address::FreeAddr(gai);
+ PROF_stop(comm_accept);
+ return Comm::COMM_ERROR;
}
details->local = *gai;
- Ip::Address::FreeAddrInfo(gai);
+ Ip::Address::FreeAddr(gai);
/* fdstat update */
// XXX : these are not all HTTP requests. use a note about type and ip:port details->
// so we end up with a uniform "(HTTP|FTP-data|HTTPS|...) remote-ip:remote-port"
fd_open(sock, FD_SOCKET, "HTTP Request");
- fdd_table[sock].close_file = NULL;
- fdd_table[sock].close_line = 0;
-
fde *F = &fd_table[sock];
details->remote.toStr(F->ipaddr,MAX_IPSTRLEN);
F->remote_port = details->remote.port();
// Perform NAT or TPROXY operations to retrieve the real client/dest IP addresses
if (conn->flags&(COMM_TRANSPARENT|COMM_INTERCEPTION) && !Ip::Interceptor.Lookup(details, conn)) {
+ debugs(50, DBG_IMPORTANT, "ERROR: NAT/TPROXY lookup failed to locate original IPs on " << details);
// Failed.
- return COMM_ERROR;
+ PROF_stop(comm_accept);
+ return Comm::COMM_ERROR;
}
#if USE_SQUID_EUI
if (Eui::TheConfig.euiLookup) {
- if (conn->remote.isIPv4()) {
- conn->remoteEui48.lookup(conn->remote);
- } else if (conn->remote.isIPv6()) {
- conn->remoteEui64.lookup(conn->remote);
+ if (details->remote.isIPv4()) {
+ details->remoteEui48.lookup(details->remote);
+ } else if (details->remote.isIPv6()) {
+ details->remoteEui64.lookup(details->remote);
}
}
#endif
PROF_stop(comm_accept);
- return COMM_OK;
+ return Comm::OK;
}
+