]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Merge from trunk
authorAmos Jeffries <squid3@treenet.co.nz>
Sat, 2 Jan 2010 10:17:00 +0000 (23:17 +1300)
committerAmos Jeffries <squid3@treenet.co.nz>
Sat, 2 Jan 2010 10:17:00 +0000 (23:17 +1300)
1  2 
src/comm/ListenStateData.cc

index 7fca3f673b67ce86cac41575ef232574dc2311fc,0000000000000000000000000000000000000000..5e90c69f55d2525c221f3b68cd1fa5502efb91ff
mode 100644,000000..100644
--- /dev/null
@@@ -1,276 -1,0 +1,285 @@@
 +/*
 + * DEBUG: section 5     Listener Socket Handler
 + * AUTHOR: Harvest Derived
 + *
 + * 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>
 + */
 +
 +#include "squid.h"
 +#include "CommCalls.h"
 +#include "comm/AcceptLimiter.h"
 +#include "comm/comm_internal.h"
 +#include "comm/ListenStateData.h"
 +#include "ConnectionDetail.h"
 +#include "fde.h"
++#include "protos.h"
 +#include "SquidTime.h"
 +
 +/**
 + * New-style listen and accept routines
 + *
 + * Listen simply registers our interest in an FD for listening,
 + * and accept takes a callback to call when an FD has been
 + * accept()ed.
 + */
 +void
 +Comm::ListenStateData::setListen()
 +{
 +    int x;
 +
 +    if ((x = listen(fd, Squid_MaxFD >> 2)) < 0) {
 +        debugs(50, 0, HERE << "listen(FD " << fd << ", " << (Squid_MaxFD >> 2) << "): " << xstrerror());
 +        errcode = x;
 +        return;
 +    }
 +
 +    if (Config.accept_filter && strcmp(Config.accept_filter, "none") != 0) {
 +#ifdef SO_ACCEPTFILTER
 +        struct accept_filter_arg afa;
 +        bzero(&afa, sizeof(afa));
 +        debugs(5, DBG_IMPORTANT, "Installing accept filter '" << Config.accept_filter << "' on FD " << fd);
 +        xstrncpy(afa.af_name, Config.accept_filter, sizeof(afa.af_name));
 +        x = setsockopt(fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa));
 +        if (x < 0)
 +            debugs(5, DBG_CRITICAL, "SO_ACCEPTFILTER '" << Config.accept_filter << "': '" << xstrerror());
 +#elif defined(TCP_DEFER_ACCEPT)
 +        int seconds = 30;
 +        if (strncmp(Config.accept_filter, "data=", 5) == 0)
 +            seconds = atoi(Config.accept_filter + 5);
 +        x = setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &seconds, sizeof(seconds));
 +        if (x < 0)
 +            debugs(5, DBG_CRITICAL, "TCP_DEFER_ACCEPT '" << Config.accept_filter << "': '" << xstrerror());
 +#else
 +        debugs(5, DBG_CRITICAL, "accept_filter not supported on your OS");
 +#endif
 +    }
 +}
 +
 +Comm::ListenStateData::ListenStateData(int aFd, AsyncCall::Pointer &call, bool accept_many) :
 +    fd(aFd),
 +    theCallback(call),
 +    mayAcceptMore(accept_many)
 +{
 +    assert(aFd >= 0);
 +    debugs(5, 5, HERE << "FD " << fd << " AsyncCall: " << call);
 +    assert(isOpen(aFd));
 +    setListen();
 +    commSetSelect(fd, COMM_SELECT_READ, doAccept, this, 0);
 +}
 +
 +Comm::ListenStateData::~ListenStateData()
 +{
 +    comm_close(fd);
 +    fd = -1;
 +}
 +
 +/**
 + * This private callback is called whenever a filedescriptor is ready
 + * to dupe itself and fob off an accept()ed connection
 + *
 + * It will either do that accept operation. Or if there are not enough FD
 + * available to do the clone safely will push the listening FD into a list
 + * of deferred operations. The list gets kicked and the dupe/accept() actually
 + * done later when enough sockets become available.
 + */
 +void
 +Comm::ListenStateData::doAccept(int fd, void *data)
 +{
 +    debugs(5, 2, HERE << "New connection on FD " << fd);
 +
 +    assert(isOpen(fd));
 +    ListenStateData *afd = static_cast<ListenStateData*>(data);
 +
 +    if (!okToAccept()) {
 +        AcceptLimiter::Instance().defer(afd);
 +    }
 +    else {
 +        afd->acceptNext();
 +    }
 +    commSetSelect(fd, COMM_SELECT_READ, Comm::ListenStateData::doAccept, afd, 0);
 +}
 +
 +bool
 +Comm::ListenStateData::okToAccept()
 +{
 +    static time_t last_warn = 0;
 +
 +    if (fdNFree() >= RESERVED_FD)
 +        return true;
 +
 +    if (last_warn + 15 < squid_curtime) {
 +        debugs(5, DBG_CRITICAL, "WARNING! Your cache is running out of filedescriptors");
 +        last_warn = squid_curtime;
 +    }
 +
 +    return false;
 +}
 +
 +bool
 +Comm::ListenStateData::acceptOne()
 +{
 +    /*
 +     * We don't worry about running low on FDs here.  Instead,
 +     * doAccept() will use AcceptLimiter if we reach the limit
 +     * there.
 +     */
 +
 +    /* Accept a new connection */
 +    ConnectionDetail connDetails;
 +    int newfd = oldAccept(connDetails);
 +
 +    /* Check for errors */
 +    if (newfd < 0) {
 +
 +        if (newfd == COMM_NOMESSAGE) {
 +            /* register interest again */
 +            debugs(5, 5, HERE << "try later: FD " << fd << " handler: " << *theCallback);
 +            commSetSelect(fd, COMM_SELECT_READ, doAccept, this, 0);
 +            return false;
 +        }
 +
 +        // A non-recoverable error; notify the caller */
 +        debugs(5, 5, HERE << "non-recoverable error: FD " << fd << " handler: " << *theCallback);
 +        notify(-1, COMM_ERROR, errno, connDetails);
 +        return false;
 +    }
 +
 +    debugs(5, 5, HERE << "accepted: FD " << fd <<
 +           " newfd: " << newfd << " from: " << connDetails.peer <<
 +           " handler: " << *theCallback);
 +    notify(newfd, COMM_OK, 0, connDetails);
 +    return true;
 +}
 +
 +void
 +Comm::ListenStateData::acceptNext()
 +{
 +    assert(isOpen(fd));
 +    debugs(5, 2, HERE << "connection on FD " << fd);
 +    mayAcceptMore = acceptOne();
 +}
 +
 +void
 +Comm::ListenStateData::notify(int newfd, comm_err_t errcode, int xerrno, const ConnectionDetail &connDetails)
 +{
 +    // listener socket handlers just abandon the port with COMM_ERR_CLOSING
 +    // it should only happen when this object is deleted...
 +    if (errcode == COMM_ERR_CLOSING) {
 +        return;
 +    }
 +
 +    if (theCallback != NULL) {
 +        typedef CommAcceptCbParams Params;
 +        Params &params = GetCommParams<Params>(theCallback);
 +        params.fd = fd;
 +        params.nfd = newfd;
 +        params.details = connDetails;
 +        params.flag = errcode;
 +        params.xerrno = xerrno;
 +        ScheduleCallHere(theCallback);
 +        if (!mayAcceptMore)
 +            theCallback = NULL;
 +    }
 +}
 +
 +/**
 + * accept() and process 
 + * Wait for an incoming connection on FD.
 + */
 +int
 +Comm::ListenStateData::oldAccept(ConnectionDetail &details)
 +{
 +    PROF_start(comm_accept);
 +    statCounter.syscalls.sock.accepts++;
 +    int sock;
 +    struct addrinfo *gai = NULL;
 +    details.me.InitAddrInfo(gai);
 +
 +    if ((sock = accept(fd, gai->ai_addr, &gai->ai_addrlen)) < 0) {
 +
 +        details.me.FreeAddrInfo(gai);
 +
 +        PROF_stop(comm_accept);
 +
 +        if (ignoreErrno(errno)) {
 +            debugs(50, 5, HERE << "FD " << fd << ": " << xstrerror());
 +            return COMM_NOMESSAGE;
 +        } else if (ENFILE == errno || EMFILE == errno) {
 +            debugs(50, 3, HERE << "FD " << fd << ": " << xstrerror());
 +            return COMM_ERROR;
 +        } else {
 +            debugs(50, 1, HERE << "FD " << fd << ": " << xstrerror());
 +            return COMM_ERROR;
 +        }
 +    }
 +
 +    details.peer = *gai;
 +
++    if ( Config.client_ip_max_connections >= 0) {
++        if (clientdbEstablished(details.peer, 0) > Config.client_ip_max_connections) {
++            debugs(50, DBG_IMPORTANT, "WARNING: " << details.peer << " attempting more than " << Config.client_ip_max_connections << " connections.");
++            details.me.FreeAddrInfo(gai);
++            return COMM_ERROR;
++        }
++    }
++
 +    details.me.InitAddrInfo(gai);
 +
 +    details.me.SetEmpty();
 +    getsockname(sock, gai->ai_addr, &gai->ai_addrlen);
 +    details.me = *gai;
 +
 +    commSetCloseOnExec(sock);
 +
 +    /* fdstat update */
 +    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.peer.NtoA(F->ipaddr,MAX_IPSTRLEN);
 +    F->remote_port = details.peer.GetPort();
 +    F->local_addr.SetPort(details.me.GetPort());
 +#if USE_IPV6
 +    F->sock_family = AF_INET;
 +#else
 +    F->sock_family = details.me.IsIPv4()?AF_INET:AF_INET6;
 +#endif
 +    details.me.FreeAddrInfo(gai);
 +
 +    commSetNonBlocking(sock);
 +
 +    /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */
 +    F->flags.transparent = fd_table[fd].flags.transparent;
 +
 +    PROF_stop(comm_accept);
 +    return sock;
 +}