diff options
-rw-r--r-- | data/operserv.example.conf | 7 | ||||
-rw-r--r-- | include/config.h | 3 | ||||
-rw-r--r-- | include/sockets.h | 1 | ||||
-rw-r--r-- | modules/commands/os_session.cpp | 21 | ||||
-rw-r--r-- | modules/commands/os_session.h | 6 | ||||
-rw-r--r-- | src/config.cpp | 5 | ||||
-rw-r--r-- | src/sockets.cpp | 32 |
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 |