diff options
| author | 2023-01-08 16:12:43 +0000 | |
|---|---|---|
| committer | 2023-01-08 16:28:40 +0000 | |
| commit | 8831595e1a5306f4204fedc6ecbc4a3c9299fb35 (patch) | |
| tree | f9f27322d35d199240dfeca82415ec94724f2ff9 /src/users.cpp | |
| parent | Switch sqloper to use a local user lookup instead of IS_LOCAL. (diff) | |
Rework how users are assigned to connect classes.
- Move core connect class checks and <performance:clonesonconnect>
to the core_user module.
- Add pre-change and post-change events for when a connect class
changes.
- Split explicit class changing out into its own method.
- Remove the need to almost always call CheckClass after SetClass.
- Add use counting to the connect class instead of relying on the
shared_ptr use count.
Diffstat (limited to 'src/users.cpp')
| -rw-r--r-- | src/users.cpp | 247 |
1 files changed, 69 insertions, 178 deletions
diff --git a/src/users.cpp b/src/users.cpp index 869f6b281..6c89669e0 100644 --- a/src/users.cpp +++ b/src/users.cpp @@ -378,51 +378,6 @@ void User::OperLogout() FOREACH_MOD(OnPostOperLogout, (this, account)); } -/* - * Check class restrictions - */ -void LocalUser::CheckClass(bool clone_count) -{ - const ConnectClass::Ptr& a = GetClass(); - if (!a) - { - ServerInstance->Users.QuitUser(this, "Access denied by configuration"); - return; - } - else if (a->type == ConnectClass::DENY) - { - ServerInstance->Users.QuitUser(this, a->config->getString("reason", "Unauthorised connection", 1)); - return; - } - else if (clone_count) - { - const UserManager::CloneCounts& clonecounts = ServerInstance->Users.GetCloneCounts(this); - if (a->maxlocal && clonecounts.local > a->maxlocal) - { - ServerInstance->Users.QuitUser(this, "No more connections allowed from your host via this connect class (local)"); - if (a->maxconnwarn) - { - ServerInstance->SNO.WriteToSnoMask('a', "WARNING: maximum local connections for the %s class (%ld) exceeded by %s", - a->name.c_str(), a->maxlocal, this->GetIPString().c_str()); - } - return; - } - else if (a->maxglobal && clonecounts.global > a->maxglobal) - { - ServerInstance->Users.QuitUser(this, "No more connections allowed from your host via this connect class (global)"); - if (a->maxconnwarn) - { - ServerInstance->SNO.WriteToSnoMask('a', "WARNING: maximum global connections for the %s class (%ld) exceeded by %s", - a->name.c_str(), a->maxglobal, this->GetIPString().c_str()); - } - return; - } - } - - this->nextping = ServerInstance->Time() + a->pingtime; - this->uniqueusername = a->uniqueusername; -} - bool LocalUser::CheckLines(bool doZline) { const char* check[] = { "G" , "K", (doZline) ? "Z" : nullptr, nullptr }; @@ -455,11 +410,10 @@ void LocalUser::FullConnect() * may put the user into a totally separate class with different restrictions! so we *must* check again. * Don't remove this! -- w00t */ - connectclass = nullptr; - SetClass(); - CheckClass(); - CheckLines(); + if (!FindConnectClass()) + return; // User does not match any connect classes. + CheckLines(); if (quitting) return; @@ -651,14 +605,71 @@ void LocalUser::ChangeRemoteAddress(const irc::sockets::sockaddrs& sa) ServerInstance->Users.AddClone(this); // Recheck the connect class. - this->connectclass = nullptr; - this->SetClass(); - this->CheckClass(); - - if (!quitting) + if (FindConnectClass()) FOREACH_MOD(OnChangeRemoteAddress, (this)); } +bool LocalUser::FindConnectClass() +{ + ServerInstance->Logs.Debug("CONNECTCLASS", "Finding a connect class for %s (%s) ...", + uuid.c_str(), GetFullRealHost().c_str()); + + for (const auto& klass : ServerInstance->Config->Classes) + { + ServerInstance->Logs.Debug("CONNECTCLASS", "Checking the %s connect class ...", + klass->GetName().c_str()); + + // Users can not be automatically assigned to a named class. + if (klass->type == ConnectClass::NAMED) + { + ServerInstance->Logs.Debug("CONNECTCLASS", "The %s connect class is not suitable as neither <connect:allow> nor <connect:deny> are set.", + klass->GetName().c_str()); + continue; + } + + ModResult modres; + FIRST_MOD_RESULT(OnPreChangeConnectClass, modres, (this, klass)); + if (modres != MOD_RES_DENY) + { + ServerInstance->Logs.Debug("CONNECTCLASS", "The %s connect class is suitable for %s (%s).", + klass->GetName().c_str(), uuid.c_str(), GetFullRealHost().c_str()); + + ChangeConnectClass(klass, false); + return !quitting; + } + } + + // The user didn't match any connect classes. + if (connectclass) + { + connectclass->use_count--; + connectclass = nullptr; + } + ServerInstance->Users.QuitUser(this, "You are not allowed to connect to this server"); + return false; +} + +void LocalUser::ChangeConnectClass(const std::shared_ptr<ConnectClass>& klass, bool force) +{ + // Let modules know the class is about to be changed. + FOREACH_MOD(OnChangeConnectClass, (this, klass, force)); + if (quitting) + return; // User hit some kind of restriction. + + // Assign the new connect class. + if (connectclass) + connectclass->use_count--; + connectclass = klass; + connectclass->use_count++; + + // Update the core user data that depends on connect class. + nextping = ServerInstance->Time() + klass->pingtime; + uniqueusername = klass->uniqueusername; + + // Let modules know the class has been changed. + FOREACH_MOD(OnPostChangeConnectClass, (this, force)); +} + void LocalUser::Write(const ClientProtocol::SerializedMessage& text) { if (!eh.HasFd()) @@ -952,126 +963,6 @@ bool User::ChangeIdent(const std::string& newident) return true; } -/* - * Sets a user's connection class. - * If the class name is provided, it will be used. Otherwise, the class will be guessed using host/ip/ident/etc. - * NOTE: If the <ALLOW> or <DENY> tag specifies an ip, and this user resolves, - * then their ip will be taken as 'priority' anyway, so for example, - * <connect allow="127.0.0.1"> will match joe!bloggs@localhost - */ -void LocalUser::SetClass(const std::string& explicit_name) -{ - ServerInstance->Logs.Debug("CONNECTCLASS", "Setting connect class for %s (%s) ...", - this->uuid.c_str(), this->GetFullRealHost().c_str()); - - ConnectClass::Ptr found; - if (!explicit_name.empty()) - { - for (const auto& c : ServerInstance->Config->Classes) - { - if (explicit_name == c->name) - { - ServerInstance->Logs.Debug("CONNECTCLASS", "Connect class explicitly set to %s", - explicit_name.c_str()); - found = c; - } - } - } - else - { - for (const auto& c : ServerInstance->Config->Classes) - { - ServerInstance->Logs.Debug("CONNECTCLASS", "Checking the %s connect class ...", - c->GetName().c_str()); - - ModResult MOD_RESULT; - FIRST_MOD_RESULT(OnSetConnectClass, MOD_RESULT, (this, c)); - if (MOD_RESULT == MOD_RES_DENY) - continue; - - if (MOD_RESULT == MOD_RES_ALLOW) - { - ServerInstance->Logs.Debug("CONNECTCLASS", "The %s connect class was explicitly chosen by a module", - c->GetName().c_str()); - found = c; - break; - } - - if (c->type == ConnectClass::NAMED) - { - ServerInstance->Logs.Debug("CONNECTCLASS", "The %s connect class is not suitable as neither <connect:allow> nor <connect:deny> are set", - c->GetName().c_str()); - continue; - } - - bool conndone = connected != User::CONN_NONE; - if (c->config->getBool("connected", c->config->getBool("registered", conndone)) != conndone) - { - ServerInstance->Logs.Debug("CONNECTCLASS", "The %s connect class is not suitable as it requires that the user is %s", - c->GetName().c_str(), conndone ? "not fully connected" : "fully connected"); - continue; - } - - bool hostmatches = false; - for (const auto& host : c->GetHosts()) - { - if (InspIRCd::MatchCIDR(this->GetIPString(), host) || InspIRCd::MatchCIDR(this->GetRealHost(), host)) - { - hostmatches = true; - break; - } - } - if (!hostmatches) - { - const std::string hosts = stdalgo::string::join(c->GetHosts()); - ServerInstance->Logs.Debug("CONNECTCLASS", "The %s connect class is not suitable as neither the host (%s) nor the IP (%s) matches %s", - c->GetName().c_str(), this->GetRealHost().c_str(), this->GetIPString().c_str(), hosts.c_str()); - continue; - } - - /* - * deny change if change will take class over the limit check it HERE, not after we found a matching class, - * because we should attempt to find another class if this one doesn't match us. -- w00t - */ - if (c->limit && (c.use_count() >= static_cast<long>(c->limit))) - { - // HACK: using use_count() is awful and should be removed before v4 is released. - ServerInstance->Logs.Debug("CONNECTCLASS", "The %s connect class is not suitable as it has reached its user limit (%lu)", - c->GetName().c_str(), c->limit); - continue; - } - - /* if it requires a port and our port doesn't match, fail */ - if (!c->ports.empty() && !c->ports.count(this->server_sa.port())) - { - ServerInstance->Logs.Debug("CONNECTCLASS", "The %s connect class is not suitable as the connection port (%hu) is not any of %s", - c->GetName().c_str(), this->server_sa.port(), stdalgo::string::join(c->ports).c_str()); - continue; - } - - if (conndone && !c->password.empty() && !ServerInstance->PassCompare(c->password, password, c->passwordhash)) - { - ServerInstance->Logs.Debug("CONNECTCLASS", "The %s connect class is not suitable as requires a password and %s", - c->GetName().c_str(), password.empty() ? "one was not provided" : "the provided password was incorrect"); - continue; - } - - /* we stop at the first class that meets ALL criteria. */ - ServerInstance->Logs.Debug("CONNECTCLASS", "The %s connect class is suitable for %s (%s)", - c->GetName().c_str(), this->uuid.c_str(), this->GetFullRealHost().c_str()); - found = c; - break; - } - } - - /* - * Okay, assuming we found a class that matches.. switch us into that class, keeping refcounts up to date. - */ - if (found) - { - connectclass = found; - } -} void User::PurgeEmptyChannels() { @@ -1116,7 +1007,7 @@ ConnectClass::ConnectClass(std::shared_ptr<ConfigTag> tag, Type t, const std::ve { } -ConnectClass::ConnectClass(std::shared_ptr<ConfigTag> tag, Type t, const std::vector<std::string>& masks, const ConnectClass::Ptr& parent) +ConnectClass::ConnectClass(std::shared_ptr<ConfigTag> tag, Type t, const std::vector<std::string>& masks, const std::shared_ptr<ConnectClass>& parent) { Update(parent); name = "unnamed"; @@ -1170,8 +1061,8 @@ void ConnectClass::Configure(const std::string& classname, std::shared_ptr<Confi limit = tag->getUInt("limit", limit, 1); maxchans = tag->getUInt("maxchans", maxchans); maxconnwarn = tag->getBool("maxconnwarn", maxconnwarn); - maxglobal = tag->getUInt("globalmax", maxglobal, 1); - maxlocal = tag->getUInt("localmax", maxlocal, 1); + maxlocal = tag->getUInt("localmax", maxlocal); + maxglobal = tag->getUInt("globalmax", maxglobal, maxlocal); penaltythreshold = tag->getUInt("threshold", penaltythreshold, 1); pingtime = tag->getDuration("pingfreq", pingtime); recvqmax = tag->getUInt("recvq", recvqmax, ServerInstance->Config->Limits.MaxLine); @@ -1181,7 +1072,7 @@ void ConnectClass::Configure(const std::string& classname, std::shared_ptr<Confi uniqueusername = tag->getBool("uniqueusername", uniqueusername); } -void ConnectClass::Update(const ConnectClass::Ptr& src) +void ConnectClass::Update(const std::shared_ptr<ConnectClass>& src) { ServerInstance->Logs.Debug("CONNECTCLASS", "Updating %s from %s", name.c_str(), src->name.c_str()); commandrate = src->commandrate; |
