summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/operserv.example.conf7
-rw-r--r--include/config.h3
-rw-r--r--include/sockets.h1
-rw-r--r--modules/commands/os_session.cpp21
-rw-r--r--modules/commands/os_session.h6
-rw-r--r--src/config.cpp5
-rw-r--r--src/sockets.cpp32
7 files changed, 60 insertions, 15 deletions
diff --git a/data/operserv.example.conf b/data/operserv.example.conf
index 334d39a82..c6e69782e 100644
--- a/data/operserv.example.conf
+++ b/data/operserv.example.conf
@@ -185,6 +185,13 @@ operserv
sessionautokillexpiry = 30m
/*
+ * Sets the CIDR value used to determine which IP addresses represent the same person.
+ * By default this would limit 3 connections per IPv4 IP and 3 conenctions per IPv6 IP.
+ */
+ session_ipv4_cidr = 32
+ session_ipv6_cidr = 128
+
+ /*
* Adds the nickname of the IRC Operator issuing an AKILL to the kill reason.
*
* This directive is optional.
diff --git a/include/config.h b/include/config.h
index 775f03aa8..d9ec11abd 100644
--- a/include/config.h
+++ b/include/config.h
@@ -659,6 +659,9 @@ class CoreExport ServerConfig
unsigned MaxSessionLimit;
/* How long session akills should last */
time_t SessionAutoKillExpiry;
+ /* Number of bits to use when comparing session IPs */
+ unsigned SessionIPv4CIDR;
+ unsigned SessionIPv6CIDR;
/* Reason to use for session kills */
Anope::string SessionLimitExceeded;
/* Optional second reason */
diff --git a/include/sockets.h b/include/sockets.h
index 8b669bbde..6b1fca441 100644
--- a/include/sockets.h
+++ b/include/sockets.h
@@ -90,6 +90,7 @@ class CoreExport cidr
cidr(const Anope::string &ip, unsigned char len);
Anope::string mask() const;
bool match(sockaddrs &other);
+ bool operator<(const cidr &other) const;
};
class SocketException : public CoreException
diff --git a/modules/commands/os_session.cpp b/modules/commands/os_session.cpp
index 0de82d05a..91db78da2 100644
--- a/modules/commands/os_session.cpp
+++ b/modules/commands/os_session.cpp
@@ -63,17 +63,18 @@ class MySessionService : public SessionService
void AddSession(Session *s) anope_override
{
- this->Sessions[s->host] = s;
+ this->Sessions[s->addr] = s;
}
void DelSession(Session *s) anope_override
{
- this->Sessions.erase(s->host);
+ this->Sessions.erase(s->addr);
}
- Session *FindSession(const Anope::string &mask) anope_override
+ Session *FindSession(const Anope::string &ip) anope_override
{
- SessionMap::iterator it = this->Sessions.find(mask);
+ cidr c(ip, ip.find(':') != Anope::string::npos ? Config->SessionIPv6CIDR : Config->SessionIPv4CIDR);
+ SessionMap::iterator it = this->Sessions.find(c);
if (it != this->Sessions.end())
return it->second;
return NULL;
@@ -176,7 +177,7 @@ class CommandOSSession : public Command
{
ListFormatter::ListEntry entry;
entry["Session"] = stringify(session->count);
- entry["Host"] = session->host;
+ entry["Host"] = session->addr.mask();
list.addEntry(entry);
}
}
@@ -204,7 +205,7 @@ class CommandOSSession : public Command
else
{
Exception *exception = session_service->FindException(param);
- source.Reply(_("The host \002%s\002 currently has \002%d\002 sessions with a limit of \002%d\002."), param.c_str(), session->count, exception && exception->limit > Config->DefSessionLimit ? exception->limit : Config->DefSessionLimit);
+ source.Reply(_("The host \002%s\002 currently has \002%d\002 sessions with a limit of \002%d\002."), session->addr.mask().c_str(), session->count, exception && exception->limit > Config->DefSessionLimit ? exception->limit : Config->DefSessionLimit);
}
return;
@@ -606,7 +607,7 @@ class OSSession : public Module
void AddSession(User *u, bool exempt)
{
- Session *session = this->ss.FindSession(u->host);
+ Session *session = this->ss.FindSession(u->ip);
if (session)
{
@@ -661,11 +662,7 @@ class OSSession : public Module
}
else
{
- session = new Session();
- session->host = u->host;
- session->count = 1;
- session->hits = 0;
-
+ session = new Session(u->ip, u->ip.find(':') != Anope::string::npos ? Config->SessionIPv6CIDR : Config->SessionIPv4CIDR);
this->ss.AddSession(session);
}
}
diff --git a/modules/commands/os_session.h b/modules/commands/os_session.h
index 75764b12d..aa32d9d58 100644
--- a/modules/commands/os_session.h
+++ b/modules/commands/os_session.h
@@ -3,9 +3,11 @@
struct Session
{
- Anope::string host; /* Host of the session */
+ cidr addr; /* A cidr (sockaddrs + len) representing this session */
unsigned count; /* Number of clients with this host */
unsigned hits; /* Number of subsequent kills for a host */
+
+ Session(const Anope::string &ip, int len) : addr(ip, len), count(1), hits(0) { }
};
struct Exception : Serializable
@@ -25,7 +27,7 @@ struct Exception : Serializable
class SessionService : public Service
{
public:
- typedef Anope::map<Session *> SessionMap;
+ typedef std::map<cidr, Session *> SessionMap;
typedef std::vector<Exception *> ExceptionVector;
SessionService(Module *m) : Service(m, "SessionService", "session") { }
diff --git a/src/config.cpp b/src/config.cpp
index 91cf8a805..fb1849e6c 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -214,6 +214,9 @@ ServerConfig::ServerConfig() : config_data(), NSDefFlags(NickCoreFlagStrings), C
Log() << "Unknown casemap " << this->CaseMap << " - casemap not changed";
}
}
+
+ if (this->SessionIPv4CIDR > 32 || this->SessionIPv6CIDR > 128)
+ throw ConfigException("Session CIDR value out of range");
}
bool ServerConfig::CheckOnce(const Anope::string &tag)
@@ -1284,6 +1287,8 @@ ConfigItems::ConfigItems(ServerConfig *conf)
{"operserv", "sessionlimitdetailsloc", "", new ValueContainerString(&conf->SessionLimitDetailsLoc), DT_STRING, NoValidation},
{"operserv", "maxsessionkill", "0", new ValueContainerUInt(&conf->MaxSessionKill), DT_UINTEGER, NoValidation},
{"operserv", "sessionautokillexpiry", "0", new ValueContainerTime(&conf->SessionAutoKillExpiry), DT_TIME, NoValidation},
+ {"operserv", "session_ipv4_cidr", "32", new ValueContainerUInt(&conf->SessionIPv4CIDR), DT_UINTEGER | DT_NORELOAD, NoValidation},
+ {"operserv", "session_ipv6_cidr", "128", new ValueContainerUInt(&conf->SessionIPv6CIDR), DT_UINTEGER | DT_NORELOAD, NoValidation},
{"operserv", "addakiller", "no", new ValueContainerBool(&conf->AddAkiller), DT_BOOLEAN, NoValidation},
{"operserv", "akillids", "no", new ValueContainerBool(&conf->AkillIds), DT_BOOLEAN, NoValidation},
{"operserv", "opersonly", "no", new ValueContainerBool(&conf->OSOpersOnly), DT_BOOLEAN, NoValidation},
diff --git a/src/sockets.cpp b/src/sockets.cpp
index 3d2df8dc9..7bb07f5c5 100644
--- a/src/sockets.cpp
+++ b/src/sockets.cpp
@@ -227,7 +227,7 @@ cidr::cidr(const Anope::string &ip, unsigned char len)
Anope::string cidr::mask() const
{
- return this->cidr_ip + "/" + this->cidr_len;
+ return Anope::printf("%s/%d", this->cidr_ip.c_str(), this->cidr_len);
}
bool cidr::match(sockaddrs &other)
@@ -265,6 +265,36 @@ bool cidr::match(sockaddrs &other)
return true;
}
+bool cidr::operator<(const cidr &other) const
+{
+ if (this->addr.sa.sa_family != other.addr.sa.sa_family)
+ return this->addr.sa.sa_family < other.addr.sa.sa_family;
+
+ switch (this->addr.sa.sa_family)
+ {
+ case AF_INET:
+ {
+ unsigned int m = 0xFFFFFFFFU >> (32 - this->cidr_len);
+
+ return (this->addr.sa4.sin_addr.s_addr & m) < (other.addr.sa4.sin_addr.s_addr & m);
+ }
+ case AF_INET6:
+ {
+ int i = memcmp(&this->addr.sa6.sin6_addr.s6_addr, &other.addr.sa6.sin6_addr.s6_addr, this->cidr_len / 8);
+ if (i || this->cidr_len >= 128)
+ return i < 0;
+
+ // Now all thats left is to compare 'remainig' bits at offset this->cidr_len / 8
+ int remaining = this->cidr_len % 8;
+ unsigned char m = 0xFF << (8 - remaining);
+
+ return (this->addr.sa6.sin6_addr.s6_addr[this->cidr_len / 8] & m) < (other.addr.sa6.sin6_addr.s6_addr[this->cidr_len / 8] & m);
+ }
+ default:
+ throw CoreException("Unknown AFTYPE for cidr");
+ }
+}
+
/** Receive something from the buffer
* @param s The socket
* @param buf The buf to read to