]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/comm/TcpAcceptor.cc
transaction_initiator ACL for detecting various unusual transactions
[thirdparty/squid.git] / src / comm / TcpAcceptor.cc
index 95bbca92a3e6e08dec1cca28424f510000c896fb..890d13f654af0df035c119cd293f25595b4b4a2c 100644 (file)
@@ -1,58 +1,38 @@
 /*
- * 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
@@ -92,6 +82,8 @@ Comm::TcpAcceptor::start()
 
     setListen();
 
+    conn->noteStart();
+
     // if no error so far start accepting connections.
     if (errcode == 0)
         SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
@@ -119,6 +111,12 @@ Comm::TcpAcceptor::swanSong()
 {
     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();
@@ -132,11 +130,11 @@ Comm::TcpAcceptor::status() const
 
     static char ipbuf[MAX_IPSTRLEN] = {'\0'};
     if (ipbuf[0] == '\0')
-        conn->local.ToHostname(ipbuf, MAX_IPSTRLEN);
+        conn->local.toHostStr(ipbuf, MAX_IPSTRLEN);
 
     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));
@@ -154,10 +152,10 @@ Comm::TcpAcceptor::status() const
 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;
     }
 
@@ -167,18 +165,49 @@ Comm::TcpAcceptor::setListen()
         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());
 }
 
 /**
@@ -209,7 +238,7 @@ Comm::TcpAcceptor::doAccept(int fd, void *data)
     } 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");
     }
 }
 
@@ -229,6 +258,18 @@ Comm::TcpAcceptor::okToAccept()
     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()
 {
@@ -240,12 +281,12 @@ 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);
@@ -254,6 +295,8 @@ Comm::TcpAcceptor::acceptOne()
 
         // 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;
@@ -274,19 +317,21 @@ Comm::TcpAcceptor::acceptNext()
 }
 
 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 &params = 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);
@@ -297,37 +342,37 @@ Comm::TcpAcceptor::notify(const comm_err_t flag, const Comm::ConnectionPointer &
  * 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;
-    details->local.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.
 
-        details->local.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;
         }
     }
 
@@ -338,31 +383,35 @@ Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details)
     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.");
-            details->local.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
-    details->local.InitAddrInfo(gai);
-    details->local.SetEmpty();
-    getsockname(sock, gai->ai_addr, &gai->ai_addrlen);
+    Ip::Address::InitAddr(gai);
+    details->local.setEmpty();
+    if (getsockname(sock, gai->ai_addr, &gai->ai_addrlen) != 0) {
+        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;
-    details->local.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.NtoA(F->ipaddr,MAX_IPSTRLEN);
-    F->remote_port = details->remote.GetPort();
+    details->remote.toStr(F->ipaddr,MAX_IPSTRLEN);
+    F->remote_port = details->remote.port();
     F->local_addr = details->local;
-    F->sock_family = details->local.IsIPv6()?AF_INET6:AF_INET;
+    F->sock_family = details->local.isIPv6()?AF_INET6:AF_INET;
 
     // set socket flags
     commSetCloseOnExec(sock);
@@ -373,10 +422,23 @@ Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details)
 
     // 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 (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;
 }
+