summaryrefslogtreecommitdiff
path: root/src/sessions.cpp
diff options
context:
space:
mode:
authorAdam <Adam@anope.org>2010-06-19 11:54:08 -0400
committerAdam <Adam@anope.org>2010-06-19 11:54:08 -0400
commit52058fe87b4b0475b1775198c725af14e540d355 (patch)
treec3597d6411a006ffbb670d2ce761101b9640e9b6 /src/sessions.cpp
parent43e951aed54f838ba55a4c1552214773aee2fb2f (diff)
parentdf9d291bcba9788e51d11424ebaf6f05c26cc80f (diff)
Merge remote branch 'origin/1.9.3' into 1.9
Diffstat (limited to 'src/sessions.cpp')
-rw-r--r--src/sessions.cpp316
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;
+}