diff options
author | Adam <Adam@anope.org> | 2010-06-19 11:54:08 -0400 |
---|---|---|
committer | Adam <Adam@anope.org> | 2010-06-19 11:54:08 -0400 |
commit | 52058fe87b4b0475b1775198c725af14e540d355 (patch) | |
tree | c3597d6411a006ffbb670d2ce761101b9640e9b6 /src/sessions.cpp | |
parent | 43e951aed54f838ba55a4c1552214773aee2fb2f (diff) | |
parent | df9d291bcba9788e51d11424ebaf6f05c26cc80f (diff) |
Merge remote branch 'origin/1.9.3' into 1.9
Diffstat (limited to 'src/sessions.cpp')
-rw-r--r-- | src/sessions.cpp | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/src/sessions.cpp b/src/sessions.cpp new file mode 100644 index 000000000..e0c20e185 --- /dev/null +++ b/src/sessions.cpp @@ -0,0 +1,316 @@ +/* Session Limiting functions. + * + * (C) 2003-2010 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + * + * Based on the original code of Epona by Lara. + * Based on the original code of Services by Andy Church. + * + * + */ + +#include "services.h" +#include "modules.h" +#include "language.h" + +/*************************************************************************/ + +/* SESSION LIMITING + * + * The basic idea of session limiting is to prevent one host from having more + * than a specified number of sessions (client connections/clones) on the + * network at any one time. To do this we have a list of sessions and + * exceptions. Each session structure records information about a single host, + * including how many clients (sessions) that host has on the network. When a + * host reaches it's session limit, no more clients from that host will be + * allowed to connect. + * + * When a client connects to the network, we check to see if their host has + * reached the default session limit per host, and thus whether it is allowed + * any more. If it has reached the limit, we kill the connecting client; all + * the other clients are left alone. Otherwise we simply increment the counter + * within the session structure. When a client disconnects, we decrement the + * counter. When the counter reaches 0, we free the session. + * + * Exceptions allow one to specify custom session limits for a specific host + * or a range thereof. The first exception that the host matches is the one + * used. + * + * "Session Limiting" is likely to slow down services when there are frequent + * client connects and disconnects. The size of the exception list can also + * play a large role in this performance decrease. It is therefore recommened + * that you keep the number of exceptions to a minimum. A very simple hashing + * method is currently used to store the list of sessions. I'm sure there is + * room for improvement and optimisation of this, along with the storage of + * exceptions. Comments and suggestions are more than welcome! + * + * -TheShadow (02 April 1999) + */ + +/*************************************************************************/ + +session_map SessionList; + +Exception *exceptions = NULL; +int16 nexceptions = 0; + +/*************************************************************************/ +/****************************** Statistics *******************************/ +/*************************************************************************/ + +void get_session_stats(long *nrec, long *memuse) +{ + long mem = sizeof(Session) * SessionList.size(); + + for (session_map::const_iterator it = SessionList.begin(); it != SessionList.end(); ++it) + { + Session *session = it->second; + + mem += strlen(session->host) + 1; + } + + *memuse = mem; + *nrec = SessionList.size(); +} + +void get_exception_stats(long *nrec, long *memuse) +{ + long mem; + int i; + + mem = sizeof(Exception) * nexceptions; + for (i = 0; i < nexceptions; i++) { + mem += strlen(exceptions[i].mask) + 1; + mem += strlen(exceptions[i].reason) + 1; + } + *nrec = nexceptions; + *memuse = mem; +} + +/*************************************************************************/ +/********************* Internal Session Functions ************************/ +/*************************************************************************/ + +Session *findsession(const std::string &host) +{ + session_map::const_iterator it = SessionList.find(host); + + if (it != SessionList.end()) + return it->second; + return NULL; +} + +/* Attempt to add a host to the session list. If the addition of the new host + * causes the the session limit to be exceeded, kill the connecting user. + * Returns 1 if the host was added or 0 if the user was killed. + */ + +int add_session(const char *nick, const char *host, char *hostip) +{ + Session *session; + Exception *exception; + int sessionlimit = 0; + + session = findsession(host); + + if (session) { + exception = find_hostip_exception(host, hostip); + + sessionlimit = exception ? exception->limit : Config.DefSessionLimit; + + if (sessionlimit != 0 && session->count >= sessionlimit) { + if (Config.SessionLimitExceeded) + ircdproto->SendMessage(OperServ, nick, Config.SessionLimitExceeded, host); + if (Config.SessionLimitDetailsLoc) + ircdproto->SendMessage(OperServ, nick, "%s", Config.SessionLimitDetailsLoc); + + /* Previously on IRCds that send a QUIT (InspIRCD) when a user is killed, the session for a host was + * decremented in do_quit, which caused problems and fixed here + * + * Now, we create the user struture before calling this (to fix some user tracking issues.. + * read users.c), so we must increment this here no matter what because it will either be + * decremented in do_kill or in do_quit - Adam + */ + session->count++; + kill_user(Config.s_OperServ, nick, "Session limit exceeded"); + + session->hits++; + if (Config.MaxSessionKill && session->hits >= Config.MaxSessionKill) { + char akillmask[BUFSIZE]; + snprintf(akillmask, sizeof(akillmask), "*@%s", host); + XLine *x = new XLine(ci::string("*@") + host, Config.s_OperServ, time(NULL) + Config.SessionAutoKillExpiry, "Session limit exceeded"); + if (x) + x->By = Config.s_OperServ; + ircdproto->SendGlobops(OperServ, "Added a temporary AKILL for \2%s\2 due to excessive connections", akillmask); + } + return 0; + } else { + session->count++; + return 1; + } + } + + session = new Session; + session->host = sstrdup(host); + session->count = 1; + session->hits = 0; + + SessionList[session->host] = session; + + return 1; +} + +void del_session(const char *host) +{ + if (!Config.LimitSessions) { + Alog(LOG_DEBUG) << "del_session called when LimitSessions is disabled"; + return; + } + + if (!host || !*host) { + Alog(LOG_DEBUG) << "del_session called with NULL values"; + return; + } + + Alog(LOG_DEBUG_2) << "del_session() called"; + + Session *session = findsession(host); + + if (!session) { + if (debug) + { + ircdproto->SendGlobops(OperServ, "WARNING: Tried to delete non-existant session: \2%s", host); + Alog(LOG_DEBUG) << "session: Tried to delete non-existant session: " << host; + } + return; + } + + if (session->count > 1) { + session->count--; + return; + } + + SessionList.erase(session->host); + + Alog(LOG_DEBUG_2) << "del_session(): free session structure"; + + delete [] session->host; + delete session; + + Alog(LOG_DEBUG_2) << "del_session() done"; +} + + +/*************************************************************************/ +/********************** Internal Exception Functions *********************/ +/*************************************************************************/ + +void expire_exceptions() +{ + int i; + time_t now = time(NULL); + + for (i = 0; i < nexceptions; i++) { + if (exceptions[i].expires == 0 || exceptions[i].expires > now) + continue; + if (Config.WallExceptionExpire) + ircdproto->SendGlobops(OperServ, + "Session limit exception for %s has expired.", + exceptions[i].mask); + delete [] exceptions[i].mask; + delete [] exceptions[i].reason; + delete [] exceptions[i].who; + nexceptions--; + memmove(exceptions + i, exceptions + i + 1, + sizeof(Exception) * (nexceptions - i)); + exceptions = static_cast<Exception *>(srealloc(exceptions, sizeof(Exception) * nexceptions)); + i--; + } +} + +/* Find the first exception this host matches and return it. */ +Exception *find_host_exception(const char *host) +{ + int i; + + for (i = 0; i < nexceptions; i++) { + if ((Anope::Match(host, exceptions[i].mask, false))) { + return &exceptions[i]; + } + } + + return NULL; +} + +/* Same as find_host_exception() except + * this tries to find the exception by IP also. */ +Exception *find_hostip_exception(const char *host, const char *hostip) +{ + int i; + + for (i = 0; i < nexceptions; i++) { + if ((Anope::Match(host, exceptions[i].mask, false)) + || ((ircd->nickip && hostip) + && (Anope::Match(hostip, exceptions[i].mask, false)))) { + return &exceptions[i]; + } + } + + return NULL; +} + + +/*************************************************************************/ +/************************ Exception Manipulation *************************/ +/*************************************************************************/ + +int exception_add(User * u, const char *mask, const int limit, + const char *reason, const char *who, + const time_t expires) +{ + int i; + + /* Check if an exception already exists for this mask */ + for (i = 0; i < nexceptions; i++) { + if (!stricmp(mask, exceptions[i].mask)) { + if (exceptions[i].limit != limit) { + exceptions[i].limit = limit; + if (u) + notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_CHANGED, + mask, exceptions[i].limit); + return -2; + } else { + if (u) + notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_EXISTS, + mask); + return -1; + } + } + } + + nexceptions++; + exceptions = static_cast<Exception *>(srealloc(exceptions, sizeof(Exception) * nexceptions)); + + exceptions[nexceptions - 1].mask = sstrdup(mask); + exceptions[nexceptions - 1].limit = limit; + exceptions[nexceptions - 1].reason = sstrdup(reason); + exceptions[nexceptions - 1].time = time(NULL); + exceptions[nexceptions - 1].who = sstrdup(who); + exceptions[nexceptions - 1].expires = expires; + exceptions[nexceptions - 1].num = nexceptions - 1; + + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnExceptionAdd, OnExceptionAdd(u, &exceptions[nexceptions - 1])); + if (MOD_RESULT == EVENT_STOP) + { + delete [] exceptions[nexceptions - 1].mask; + delete [] exceptions[nexceptions - 1].reason; + --nexceptions; + exceptions = static_cast<Exception *>(srealloc(exceptions, sizeof(Exception) * nexceptions)); + return -3; + } + + return 1; +} |