From 4fbd6681fedbff9b4cb04cc774f785cbe8b5c35b Mon Sep 17 00:00:00 2001 From: linuxdaemon Date: Tue, 18 Dec 2018 19:06:56 -0600 Subject: Make more modules rehash atomically (#1535) Have each module validate the values it loads before setting them, so any errors don't result in partial application of the configs --- src/coremods/core_channel/core_channel.cpp | 39 +++++++------ src/coremods/core_info/core_info.cpp | 22 ++++---- src/coremods/core_loadmodule.cpp | 14 ++++- src/coremods/core_oper/cmd_die.cpp | 7 ++- src/coremods/core_oper/cmd_restart.cpp | 5 +- src/coremods/core_oper/core_oper.cpp | 31 ++++++----- src/coremods/core_oper/core_oper.h | 19 +++---- src/coremods/core_whois.cpp | 8 ++- src/listmode.cpp | 18 ++++-- src/modules/m_alias.cpp | 22 +++++--- src/modules/m_censor.cpp | 7 ++- src/modules/m_chanlog.cpp | 9 ++- src/modules/m_customtitle.cpp | 89 ++++++++++++++++++++++++------ src/modules/m_dccallow.cpp | 26 +++++---- src/modules/m_dnsbl.cpp | 22 ++++---- src/modules/m_flashpolicyd.cpp | 7 +-- src/modules/m_hidelist.cpp | 15 +++-- src/modules/m_hidemode.cpp | 20 ++++--- src/modules/m_httpd_acl.cpp | 5 +- src/modules/m_inviteexception.cpp | 2 +- src/modules/m_pbkdf2.cpp | 58 +++++++++++++------ src/modules/m_restrictchans.cpp | 15 +++-- src/modules/m_securelist.cpp | 15 ++++- src/modules/m_vhost.cpp | 82 +++++++++++++++++++++------ 24 files changed, 371 insertions(+), 186 deletions(-) diff --git a/src/coremods/core_channel/core_channel.cpp b/src/coremods/core_channel/core_channel.cpp index 05bf113ed..76e220765 100644 --- a/src/coremods/core_channel/core_channel.cpp +++ b/src/coremods/core_channel/core_channel.cpp @@ -148,16 +148,6 @@ class CoreModChannel : public Module, public CheckExemption::EventListener void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { ConfigTag* optionstag = ServerInstance->Config->ConfValue("options"); - Implementation events[] = { I_OnCheckKey, I_OnCheckLimit, I_OnCheckChannelBan }; - if (optionstag->getBool("invitebypassmodes", true)) - ServerInstance->Modules.Attach(events, this, sizeof(events)/sizeof(Implementation)); - else - { - for (unsigned int i = 0; i < sizeof(events)/sizeof(Implementation); i++) - ServerInstance->Modules.Detach(events[i], this); - } - - joinhook.modefromuser = optionstag->getBool("cyclehostsfromuser"); std::string current; irc::spacesepstream defaultstream(optionstag->getString("exemptchanops")); @@ -174,26 +164,41 @@ class CoreModChannel : public Module, public CheckExemption::EventListener ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Exempting prefix %c from %s", prefix, restriction.c_str()); exempts[restriction] = prefix; } - exemptions.swap(exempts); ConfigTag* securitytag = ServerInstance->Config->ConfValue("security"); const std::string announceinvites = securitytag->getString("announceinvites", "dynamic"); + Invite::AnnounceState newannouncestate; if (stdalgo::string::equalsci(announceinvites, "none")) - cmdinvite.announceinvites = Invite::ANNOUNCE_NONE; + newannouncestate = Invite::ANNOUNCE_NONE; else if (stdalgo::string::equalsci(announceinvites, "all")) - cmdinvite.announceinvites = Invite::ANNOUNCE_ALL; + newannouncestate = Invite::ANNOUNCE_ALL; else if (stdalgo::string::equalsci(announceinvites, "ops")) - cmdinvite.announceinvites = Invite::ANNOUNCE_OPS; + newannouncestate = Invite::ANNOUNCE_OPS; else if (stdalgo::string::equalsci(announceinvites, "dynamic")) - cmdinvite.announceinvites = Invite::ANNOUNCE_DYNAMIC; + newannouncestate = Invite::ANNOUNCE_DYNAMIC; else throw ModuleException(announceinvites + " is an invalid value, at " + securitytag->getTagLocation()); + // Config is valid, apply it + + // Validates and applies tags, so do it first + banmode.DoRehash(); + + exemptions.swap(exempts); // In 2.0 we allowed limits of 0 to be set. This is non-standard behaviour // and will be removed in the next major release. - limitmode.minlimit = optionstag->getBool("allowzerolimit", true) ? 0 : 1; + limitmode.minlimit = optionstag->getBool("allowzerolimit", true) ? 0 : 1;; + cmdinvite.announceinvites = newannouncestate; + joinhook.modefromuser = optionstag->getBool("cyclehostsfromuser"); - banmode.DoRehash(); + Implementation events[] = { I_OnCheckKey, I_OnCheckLimit, I_OnCheckChannelBan }; + if (optionstag->getBool("invitebypassmodes", true)) + ServerInstance->Modules.Attach(events, this, sizeof(events)/sizeof(Implementation)); + else + { + for (unsigned int i = 0; i < sizeof(events)/sizeof(Implementation); i++) + ServerInstance->Modules.Detach(events[i], this); + } } void On005Numeric(std::map& tokens) CXX11_OVERRIDE diff --git a/src/coremods/core_info/core_info.cpp b/src/coremods/core_info/core_info.cpp index 08e3df9bb..2b56e5e51 100644 --- a/src/coremods/core_info/core_info.cpp +++ b/src/coremods/core_info/core_info.cpp @@ -99,18 +99,14 @@ class CoreModInfo : public Module void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { - ConfigTag* tag = ServerInstance->Config->ConfValue("admin"); - cmdadmin.AdminName = tag->getString("name"); - cmdadmin.AdminEmail = tag->getString("email", "null@example.com"); - cmdadmin.AdminNick = tag->getString("nick", "admin"); - // Process the escape codes in the MOTDs. - cmdmotd.motds.clear(); + ConfigFileCache newmotds; for (ServerConfig::ClassVector::const_iterator iter = ServerInstance->Config->Classes.begin(); iter != ServerInstance->Config->Classes.end(); ++iter) { + ConfigTag* tag = (*iter)->config; // Don't process the file if it has already been processed. - const std::string motd = (*iter)->config->getString("motd", "motd"); - if (cmdmotd.motds.find(motd) != cmdmotd.motds.end()) + const std::string motd = tag->getString("motd", "motd"); + if (newmotds.find(motd) != newmotds.end()) continue; // We can't process the file if it doesn't exist. @@ -119,10 +115,16 @@ class CoreModInfo : public Module continue; // Process escape codes. - cmdmotd.motds[file->first] = file->second; - InspIRCd::ProcessColors(cmdmotd.motds[file->first]); + newmotds[file->first] = file->second; + InspIRCd::ProcessColors(newmotds[file->first]); } + cmdmotd.motds.swap(newmotds); + + ConfigTag* tag = ServerInstance->Config->ConfValue("admin"); + cmdadmin.AdminName = tag->getString("name"); + cmdadmin.AdminEmail = tag->getString("email", "null@example.com"); + cmdadmin.AdminNick = tag->getString("nick", "admin"); } void OnUserConnect(LocalUser* user) CXX11_OVERRIDE diff --git a/src/coremods/core_loadmodule.cpp b/src/coremods/core_loadmodule.cpp index 69c6bbcef..faecab213 100644 --- a/src/coremods/core_loadmodule.cpp +++ b/src/coremods/core_loadmodule.cpp @@ -58,10 +58,13 @@ CmdResult CommandLoadmodule::Handle(User* user, const Params& parameters) class CommandUnloadmodule : public Command { public: + bool allowcoreunload; + /** Constructor for unloadmodule. */ CommandUnloadmodule(Module* parent) - : Command(parent,"UNLOADMODULE", 1) + : Command(parent, "UNLOADMODULE", 1) + , allowcoreunload(false) { flags_needed = 'o'; syntax = ""; @@ -77,8 +80,7 @@ class CommandUnloadmodule : public Command CmdResult CommandUnloadmodule::Handle(User* user, const Params& parameters) { - if (!ServerInstance->Config->ConfValue("security")->getBool("allowcoreunload") && - InspIRCd::Match(parameters[0], "core_*.so", ascii_case_insensitive_map)) + if (!allowcoreunload && InspIRCd::Match(parameters[0], "core_*.so", ascii_case_insensitive_map)) { user->WriteNumeric(ERR_CANTUNLOADMODULE, parameters[0], "You cannot unload core commands!"); return CMD_FAILURE; @@ -120,6 +122,12 @@ class CoreModLoadModule : public Module { return Version("Provides the LOADMODULE and UNLOADMODULE commands", VF_VENDOR|VF_CORE); } + + void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE + { + ConfigTag* tag = ServerInstance->Config->ConfValue("security"); + cmdunloadmod.allowcoreunload = tag->getBool("allowcoreunload"); + } }; MODULE_INIT(CoreModLoadModule) diff --git a/src/coremods/core_oper/cmd_die.cpp b/src/coremods/core_oper/cmd_die.cpp index b25fe2407..8b80dd115 100644 --- a/src/coremods/core_oper/cmd_die.cpp +++ b/src/coremods/core_oper/cmd_die.cpp @@ -22,8 +22,9 @@ #include "exitcodes.h" #include "core_oper.h" -CommandDie::CommandDie(Module* parent) - : Command(parent, "DIE", 1) +CommandDie::CommandDie(Module* parent, std::string& hashref) + : Command(parent, "DIE", 1, 1) + , hash(hashref) { flags_needed = 'o'; syntax = ""; @@ -61,7 +62,7 @@ void DieRestart::SendError(const std::string& message) */ CmdResult CommandDie::Handle(User* user, const Params& parameters) { - if (DieRestart::CheckPass(user, parameters[0], "diepass")) + if (ServerInstance->PassCompare(user, password, parameters[0], hash)) { { std::string diebuf = "*** DIE command from " + user->GetFullHost() + ". Terminating."; diff --git a/src/coremods/core_oper/cmd_restart.cpp b/src/coremods/core_oper/cmd_restart.cpp index 936348f95..afadb911a 100644 --- a/src/coremods/core_oper/cmd_restart.cpp +++ b/src/coremods/core_oper/cmd_restart.cpp @@ -21,8 +21,9 @@ #include "inspircd.h" #include "core_oper.h" -CommandRestart::CommandRestart(Module* parent) +CommandRestart::CommandRestart(Module* parent, std::string& hashref) : Command(parent, "RESTART", 1, 1) + , hash(hashref) { flags_needed = 'o'; syntax = ""; @@ -31,7 +32,7 @@ CommandRestart::CommandRestart(Module* parent) CmdResult CommandRestart::Handle(User* user, const Params& parameters) { ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Restart: %s", user->nick.c_str()); - if (DieRestart::CheckPass(user, parameters[0], "restartpass")) + if (ServerInstance->PassCompare(user, password, parameters[0], hash)) { ServerInstance->SNO->WriteGlobalSno('a', "RESTART command from %s, restarting server.", user->GetFullRealHost().c_str()); diff --git a/src/coremods/core_oper/core_oper.cpp b/src/coremods/core_oper/core_oper.cpp index 54814b589..d4afab3b8 100644 --- a/src/coremods/core_oper/core_oper.cpp +++ b/src/coremods/core_oper/core_oper.cpp @@ -20,20 +20,10 @@ #include "inspircd.h" #include "core_oper.h" -namespace DieRestart -{ - bool CheckPass(User* user, const std::string& inputpass, const char* confentry) - { - ConfigTag* tag = ServerInstance->Config->ConfValue("power"); - // The hash method for *BOTH* the die and restart passwords - const std::string hash = tag->getString("hash"); - const std::string correctpass = tag->getString(confentry, ServerInstance->Config->ServerName); - return ServerInstance->PassCompare(user, correctpass, inputpass, hash); - } -} - class CoreModOper : public Module { + std::string powerhash; + CommandDie cmddie; CommandKill cmdkill; CommandOper cmdoper; @@ -42,12 +32,25 @@ class CoreModOper : public Module public: CoreModOper() - : cmddie(this), cmdkill(this), cmdoper(this), cmdrehash(this), cmdrestart(this) + : cmddie(this, powerhash) + , cmdkill(this) + , cmdoper(this) + , cmdrehash(this) + , cmdrestart(this, powerhash) { } void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { + + ConfigTag* tag = ServerInstance->Config->ConfValue("power"); + + // The hash method for *BOTH* the die and restart passwords + powerhash = tag->getString("hash"); + + cmddie.password = tag->getString("diepass", ServerInstance->Config->ServerName, 1); + cmdrestart.password = tag->getString("restartpass", ServerInstance->Config->ServerName, 1); + ConfigTag* security = ServerInstance->Config->ConfValue("security"); cmdkill.hidenick = security->getString("hidekills"); cmdkill.hideuline = security->getBool("hideulinekills"); @@ -55,7 +58,7 @@ class CoreModOper : public Module Version GetVersion() CXX11_OVERRIDE { - return Version("Provides the DIE, KILL, OPER, REHASH, and RESTART commands", VF_VENDOR|VF_CORE); + return Version("Provides the DIE, KILL, OPER, REHASH, and RESTART commands", VF_VENDOR | VF_CORE); } }; diff --git a/src/coremods/core_oper/core_oper.h b/src/coremods/core_oper/core_oper.h index bdb1ae9ee..7589b86d6 100644 --- a/src/coremods/core_oper/core_oper.h +++ b/src/coremods/core_oper/core_oper.h @@ -23,14 +23,6 @@ namespace DieRestart { - /** Checks a die or restart password - * @param user The user executing /DIE or /RESTART - * @param inputpass The password given by the user - * @param confkey The name of the key in the power tag containing the correct password - * @return True if the given password was correct, false if it was not - */ - bool CheckPass(User* user, const std::string& inputpass, const char* confkey); - /** Send an ERROR to unregistered users and a NOTICE to all registered local users * @param message Message to send */ @@ -42,9 +34,12 @@ namespace DieRestart class CommandDie : public Command { public: + std::string& hash; + std::string password; + /** Constructor for die. */ - CommandDie(Module* parent); + CommandDie(Module* parent, std::string& hashref); /** Handle command. * @param parameters The parameters to the command @@ -79,6 +74,7 @@ class CommandKill : public Command * @return A value from CmdResult to indicate command success or failure. */ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE; + RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE; void EncodeParameter(std::string& param, unsigned int index) CXX11_OVERRIDE; @@ -123,9 +119,12 @@ class CommandRehash : public Command class CommandRestart : public Command { public: + std::string& hash; + std::string password; + /** Constructor for restart. */ - CommandRestart(Module* parent); + CommandRestart(Module* parent, std::string& hashref); /** Handle command. * @param user User issuing the command diff --git a/src/coremods/core_whois.cpp b/src/coremods/core_whois.cpp index 455260051..0db634810 100644 --- a/src/coremods/core_whois.cpp +++ b/src/coremods/core_whois.cpp @@ -357,17 +357,19 @@ class CoreModWhois : public Module { ConfigTag* tag = ServerInstance->Config->ConfValue("options"); const std::string splitwhois = tag->getString("splitwhois", "no"); + SplitWhoisState newsplitstate; if (stdalgo::string::equalsci(splitwhois, "no")) - cmd.splitwhois = SPLITWHOIS_NONE; + newsplitstate = SPLITWHOIS_NONE; else if (stdalgo::string::equalsci(splitwhois, "split")) - cmd.splitwhois = SPLITWHOIS_SPLIT; + newsplitstate = SPLITWHOIS_SPLIT; else if (stdalgo::string::equalsci(splitwhois, "splitmsg")) - cmd.splitwhois = SPLITWHOIS_SPLITMSG; + newsplitstate = SPLITWHOIS_SPLITMSG; else throw ModuleException(splitwhois + " is an invalid value, at " + tag->getTagLocation()); ConfigTag* security = ServerInstance->Config->ConfValue("security"); cmd.genericoper = security->getBool("genericoper"); + cmd.splitwhois = newsplitstate; } Version GetVersion() CXX11_OVERRIDE diff --git a/src/listmode.cpp b/src/listmode.cpp index 61fadcade..6b5fb13c5 100644 --- a/src/listmode.cpp +++ b/src/listmode.cpp @@ -62,8 +62,7 @@ void ListModeBase::DoRehash() { ConfigTagList tags = ServerInstance->Config->ConfTags(configtag); - limitlist oldlimits = chanlimits; - chanlimits.clear(); + limitlist newlimits; for (ConfigIter i = tags.first; i != tags.second; i++) { @@ -71,18 +70,25 @@ void ListModeBase::DoRehash() ConfigTag* c = i->second; ListLimit limit(c->getString("chan"), c->getUInt("limit", 0)); - if (limit.mask.size() && limit.limit > 0) - chanlimits.push_back(limit); + if (limit.mask.empty()) + throw ModuleException(InspIRCd::Format("<%s:chan> is empty at %s", configtag.c_str(), c->getTagLocation().c_str())); + + if (limit.limit <= 0) + throw ModuleException(InspIRCd::Format("<%s:limit> must be greater than 0, at %s", configtag.c_str(), c->getTagLocation().c_str())); + + newlimits.push_back(limit); } // Add the default entry. This is inserted last so if the user specifies a // wildcard record in the config it will take precedence over this entry. - chanlimits.push_back(ListLimit("*", DEFAULT_LIST_SIZE)); + newlimits.push_back(ListLimit("*", DEFAULT_LIST_SIZE)); // Most of the time our settings are unchanged, so we can avoid iterating the chanlist - if (oldlimits == chanlimits) + if (chanlimits == newlimits) return; + chanlimits.swap(newlimits); + const chan_hash& chans = ServerInstance->GetChans(); for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i) { diff --git a/src/modules/m_alias.cpp b/src/modules/m_alias.cpp index 6f27ecc09..4d1dd65c9 100644 --- a/src/modules/m_alias.cpp +++ b/src/modules/m_alias.cpp @@ -74,27 +74,35 @@ class ModuleAlias : public Module public: void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { - ConfigTag* fantasy = ServerInstance->Config->ConfValue("fantasy"); - AllowBots = fantasy->getBool("allowbots", false); - fprefix = fantasy->getString("prefix", "!", 1, ServerInstance->Config->Limits.MaxLine); - - Aliases.clear(); + AliasMap newAliases; ConfigTagList tags = ServerInstance->Config->ConfTags("alias"); for(ConfigIter i = tags.first; i != tags.second; ++i) { ConfigTag* tag = i->second; Alias a; a.AliasedCommand = tag->getString("text"); - std::transform(a.AliasedCommand.begin(), a.AliasedCommand.end(), a.AliasedCommand.begin(), ::toupper); + if (a.AliasedCommand.empty()) + throw ModuleException(" is empty! at " + tag->getTagLocation()); + tag->readString("replace", a.ReplaceFormat, true); + if (a.ReplaceFormat.empty()) + throw ModuleException(" is empty! at " + tag->getTagLocation()); + a.RequiredNick = tag->getString("requires"); a.ULineOnly = tag->getBool("uline"); a.ChannelCommand = tag->getBool("channelcommand", false); a.UserCommand = tag->getBool("usercommand", true); a.OperOnly = tag->getBool("operonly"); a.format = tag->getString("format"); - Aliases.insert(std::make_pair(a.AliasedCommand, a)); + + std::transform(a.AliasedCommand.begin(), a.AliasedCommand.end(), a.AliasedCommand.begin(), ::toupper); + newAliases.insert(std::make_pair(a.AliasedCommand, a)); } + + ConfigTag* fantasy = ServerInstance->Config->ConfValue("fantasy"); + AllowBots = fantasy->getBool("allowbots", false); + fprefix = fantasy->getString("prefix", "!", 1, ServerInstance->Config->Limits.MaxLine); + Aliases.swap(newAliases); } ModuleAlias() diff --git a/src/modules/m_censor.cpp b/src/modules/m_censor.cpp index a9c55386c..31309ed2f 100644 --- a/src/modules/m_censor.cpp +++ b/src/modules/m_censor.cpp @@ -104,7 +104,7 @@ class ModuleCensor : public Module * reload our config file on rehash - we must destroy and re-allocate the classes * to call the constructor again and re-read our data. */ - censors.clear(); + censor_t newcensors; ConfigTagList badwords = ServerInstance->Config->ConfTags("badword"); for (ConfigIter i = badwords.first; i != badwords.second; ++i) @@ -112,11 +112,12 @@ class ModuleCensor : public Module ConfigTag* tag = i->second; const std::string text = tag->getString("text"); if (text.empty()) - continue; + throw ModuleException(" is empty! at " + tag->getTagLocation()); const std::string replace = tag->getString("replace"); - censors[text] = replace; + newcensors[text] = replace; } + censors.swap(newcensors); } Version GetVersion() CXX11_OVERRIDE diff --git a/src/modules/m_chanlog.cpp b/src/modules/m_chanlog.cpp index 85e7ca2eb..afe213f61 100644 --- a/src/modules/m_chanlog.cpp +++ b/src/modules/m_chanlog.cpp @@ -33,8 +33,7 @@ class ModuleChanLog : public Module { std::string snomasks; std::string channel; - - logstreams.clear(); + ChanLogTargets newlogs; ConfigTagList tags = ServerInstance->Config->ConfTags("chanlog"); for (ConfigIter i = tags.first; i != tags.second; ++i) @@ -44,16 +43,16 @@ class ModuleChanLog : public Module if (channel.empty() || snomasks.empty()) { - ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Malformed chanlog tag, ignoring"); - continue; + throw ModuleException("Malformed chanlog tag at " + i->second->getTagLocation()); } for (std::string::const_iterator it = snomasks.begin(); it != snomasks.end(); it++) { - logstreams.insert(std::make_pair(*it, channel)); + newlogs.insert(std::make_pair(*it, channel)); ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Logging %c to %s", *it, channel.c_str()); } } + logstreams.swap(newlogs); } diff --git a/src/modules/m_customtitle.cpp b/src/modules/m_customtitle.cpp index c16b1eda2..2dd378062 100644 --- a/src/modules/m_customtitle.cpp +++ b/src/modules/m_customtitle.cpp @@ -28,12 +28,49 @@ enum RPL_WHOISSPECIAL = 320 }; +struct CustomTitle +{ + const std::string name; + const std::string password; + const std::string hash; + const std::string host; + const std::string title; + const std::string vhost; + + CustomTitle(const std::string& n, const std::string& p, const std::string& h, const std::string& hst, const std::string& t, const std::string& v) + : name(n) + , password(p) + , hash(h) + , host(hst) + , title(t) + , vhost(v) + { + } + + bool MatchUser(User* user) const + { + const std::string userHost = user->ident + "@" + user->GetRealHost(); + const std::string userIP = user->ident + "@" + user->GetIPString(); + return InspIRCd::MatchMask(host, userHost, userIP); + } + + bool CheckPass(User* user, const std::string& pass) const + { + return ServerInstance->PassCompare(user, password, pass, hash); + } +}; + +typedef std::multimap CustomVhostMap; +typedef std::pair MatchingConfigs; + /** Handle /TITLE */ class CommandTitle : public Command { public: StringExtItem ctitle; + CustomVhostMap configs; + CommandTitle(Module* Creator) : Command(Creator,"TITLE", 2), ctitle("ctitle", ExtensionItem::EXT_USER, Creator) { @@ -42,30 +79,21 @@ class CommandTitle : public Command CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE { - const std::string userHost = user->ident + "@" + user->GetRealHost(); - const std::string userIP = user->ident + "@" + user->GetIPString(); + MatchingConfigs matching = configs.equal_range(parameters[0]); - ConfigTagList tags = ServerInstance->Config->ConfTags("title"); - for (ConfigIter i = tags.first; i != tags.second; ++i) + for (MatchingConfigs::first_type i = matching.first; i != matching.second; ++i) { - std::string Name = i->second->getString("name"); - std::string pass = i->second->getString("password"); - std::string hash = i->second->getString("hash"); - std::string host = i->second->getString("host", "*@*"); - std::string title = i->second->getString("title"); - std::string vhost = i->second->getString("vhost"); - - if (Name == parameters[0] && ServerInstance->PassCompare(user, pass, parameters[1], hash) && - InspIRCd::MatchMask(host, userHost, userIP) && !title.empty()) + CustomTitle config = i->second; + if (config.MatchUser(user) && config.CheckPass(user, parameters[1])) { - ctitle.set(user, title); + ctitle.set(user, config.title); - ServerInstance->PI->SendMetaData(user, "ctitle", title); + ServerInstance->PI->SendMetaData(user, "ctitle", config.title); - if (!vhost.empty()) - user->ChangeDisplayedHost(vhost); + if (!config.vhost.empty()) + user->ChangeDisplayedHost(config.vhost); - user->WriteNotice("Custom title set to '" + title + "'"); + user->WriteNotice("Custom title set to '" + config.title + "'"); return CMD_SUCCESS; } @@ -88,6 +116,31 @@ class ModuleCustomTitle : public Module, public Whois::LineEventListener { } + void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE + { + ConfigTagList tags = ServerInstance->Config->ConfTags("title"); + CustomVhostMap newtitles; + for (ConfigIter i = tags.first; i != tags.second; ++i) + { + reference tag = i->second; + std::string name = tag->getString("name", "", 1); + if (name.empty()) + throw ModuleException(" is empty at " + tag->getTagLocation()); + + std::string pass = tag->getString("password"); + if (pass.empty()) + throw ModuleException(" is empty at " + tag->getTagLocation()); + + std::string hash = tag->getString("hash"); + std::string host = tag->getString("host", "*@*"); + std::string title = tag->getString("title"); + std::string vhost = tag->getString("vhost"); + CustomTitle config(name, pass, hash, host, title, vhost); + newtitles.insert(std::make_pair(name, config)); + } + cmd.configs.swap(newtitles); + } + // :kenny.chatspike.net 320 Brain Azhrarn :is getting paid to play games. ModResult OnWhoisLine(Whois::Context& whois, Numeric::Numeric& numeric) CXX11_OVERRIDE { diff --git a/src/modules/m_dccallow.cpp b/src/modules/m_dccallow.cpp index eb364089a..85f9d20d0 100644 --- a/src/modules/m_dccallow.cpp +++ b/src/modules/m_dccallow.cpp @@ -106,6 +106,7 @@ class CommandDccallow : public Command public: unsigned int maxentries; + unsigned long defaultlength; CommandDccallow(Module* parent, DCCAllowExt& Ext) : Command(parent, "DCCALLOW", 0) , ext(Ext) @@ -206,12 +207,10 @@ class CommandDccallow : public Command } std::string mask = target->nick+"!"+target->ident+"@"+target->GetDisplayedHost(); - std::string default_length = ServerInstance->Config->ConfValue("dccallow")->getString("length"); - unsigned long length; if (parameters.size() < 2) { - length = InspIRCd::Duration(default_length); + length = defaultlength; } else if (!InspIRCd::IsValidDuration(parameters[1])) { @@ -293,11 +292,14 @@ class ModuleDCCAllow : public Module { DCCAllowExt ext; CommandDccallow cmd; + bool blockchat; + std::string defaultaction; public: ModuleDCCAllow() : ext("dccallow", ExtensionItem::EXT_USER, this) , cmd(this, ext) + , blockchat(false) { } @@ -356,9 +358,6 @@ class ModuleDCCAllow : public Module const std::string type = buf.substr(0, s); - ConfigTag* conftag = ServerInstance->Config->ConfValue("dccallow"); - bool blockchat = conftag->getBool("blockchat"); - if (stdalgo::string::equalsci(type, "SEND")) { size_t first; @@ -384,7 +383,6 @@ class ModuleDCCAllow : public Module if (s == std::string::npos) return MOD_RES_PASSTHRU; - std::string defaultaction = conftag->getString("action"); std::string filename = buf.substr(first, s); bool found = false; @@ -507,18 +505,22 @@ class ModuleDCCAllow : public Module void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { - ConfigTag* tag = ServerInstance->Config->ConfValue("dccallow"); - cmd.maxentries = tag->getUInt("maxentries", 20); - - bfl.clear(); + bannedfilelist newbfl; ConfigTagList tags = ServerInstance->Config->ConfTags("banfile"); for (ConfigIter i = tags.first; i != tags.second; ++i) { BannedFileList bf; bf.filemask = i->second->getString("pattern"); bf.action = i->second->getString("action"); - bfl.push_back(bf); + newbfl.push_back(bf); } + bfl.swap(newbfl); + + ConfigTag* tag = ServerInstance->Config->ConfValue("dccallow"); + cmd.maxentries = tag->getUInt("maxentries", 20); + cmd.defaultlength = tag->getDuration("length", 0); + blockchat = tag->getBool("blockchat"); + defaultaction = tag->getString("action"); } Version GetVersion() CXX11_OVERRIDE diff --git a/src/modules/m_dnsbl.cpp b/src/modules/m_dnsbl.cpp index 7b88bc961..d1ca800b3 100644 --- a/src/modules/m_dnsbl.cpp +++ b/src/modules/m_dnsbl.cpp @@ -228,9 +228,11 @@ class DNSBLResolver : public DNS::Request } }; +typedef std::vector > DNSBLConfList; + class ModuleDNSBL : public Module, public Stats::EventListener { - std::vector > DNSBLConfEntries; + DNSBLConfList DNSBLConfEntries; dynamic_reference DNS; LocalStringExt nameExt; LocalIntExt countExt; @@ -276,7 +278,7 @@ class ModuleDNSBL : public Module, public Stats::EventListener */ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { - DNSBLConfEntries.clear(); + DNSBLConfList newentries; ConfigTagList dnsbls = ServerInstance->Config->ConfTags("dnsbl"); for(ConfigIter i = dnsbls.first; i != dnsbls.second; ++i) @@ -313,23 +315,19 @@ class ModuleDNSBL : public Module, public Stats::EventListener /* yeah, logic here is a little messy */ if ((e->bitmask <= 0) && (DNSBLConfEntry::A_BITMASK == e->type)) { - std::string location = tag->getTagLocation(); - ServerInstance->SNO->WriteGlobalSno('d', "DNSBL(%s): invalid bitmask", location.c_str()); + throw ModuleException("Invalid at " + tag->getTagLocation()); } else if (e->name.empty()) { - std::string location = tag->getTagLocation(); - ServerInstance->SNO->WriteGlobalSno('d', "DNSBL(%s): Invalid name", location.c_str()); + throw ModuleException("Empty at " + tag->getTagLocation()); } else if (e->domain.empty()) { - std::string location = tag->getTagLocation(); - ServerInstance->SNO->WriteGlobalSno('d', "DNSBL(%s): Invalid domain", location.c_str()); + throw ModuleException("Empty at " + tag->getTagLocation()); } else if (e->banaction == DNSBLConfEntry::I_UNKNOWN) { - std::string location = tag->getTagLocation(); - ServerInstance->SNO->WriteGlobalSno('d', "DNSBL(%s): Invalid banaction", location.c_str()); + throw ModuleException("Unknown at " + tag->getTagLocation()); } else { @@ -341,9 +339,11 @@ class ModuleDNSBL : public Module, public Stats::EventListener } /* add it, all is ok */ - DNSBLConfEntries.push_back(e); + newentries.push_back(e); } } + + DNSBLConfEntries.swap(newentries); } void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE diff --git a/src/modules/m_flashpolicyd.cpp b/src/modules/m_flashpolicyd.cpp index d7f9a793b..db0eeb1f1 100644 --- a/src/modules/m_flashpolicyd.cpp +++ b/src/modules/m_flashpolicyd.cpp @@ -97,7 +97,6 @@ class ModuleFlashPD : public Module void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { ConfigTag* tag = ServerInstance->Config->ConfValue("flashpolicyd"); - timeout = tag->getDuration("timeout", 5, 1); std::string file = tag->getString("file"); if (!file.empty()) @@ -109,10 +108,7 @@ class ModuleFlashPD : public Module } catch (CoreException&) { - const std::string error_message = "A file was specified for FlashPD, but it could not be loaded."; - ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, error_message); - ServerInstance->SNO->WriteGlobalSno('a', error_message); - policy_reply.clear(); + throw ModuleException("A file was specified for FlashPD, but it could not be loaded at " + tag->getTagLocation()); } return; } @@ -144,6 +140,7 @@ class ModuleFlashPD : public Module \ \ "; + timeout = tag->getDuration("timeout", 5, 1); } CullResult cull() CXX11_OVERRIDE diff --git a/src/modules/m_hidelist.cpp b/src/modules/m_hidelist.cpp index 2d3f0be7c..9c8811fb8 100644 --- a/src/modules/m_hidelist.cpp +++ b/src/modules/m_hidelist.cpp @@ -58,19 +58,26 @@ class ModuleHideList : public Module public: void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { - stdalgo::delete_all(watchers); - watchers.clear(); - ConfigTagList tags = ServerInstance->Config->ConfTags("hidelist"); + typedef std::vector > NewConfigs; + NewConfigs newconfigs; for (ConfigIter i = tags.first; i != tags.second; ++i) { ConfigTag* tag = i->second; std::string modename = tag->getString("mode"); + if (modename.empty()) + throw ModuleException("Empty at " + tag->getTagLocation()); // If rank is set to 0 everyone inside the channel can view the list, // but non-members may not unsigned int rank = tag->getUInt("rank", HALFOP_VALUE); - watchers.push_back(new ListWatcher(this, modename, rank)); + newconfigs.push_back(std::make_pair(modename, rank)); } + + stdalgo::delete_all(watchers); + watchers.clear(); + + for (NewConfigs::const_iterator i = newconfigs.begin(); i != newconfigs.end(); ++i) + watchers.push_back(new ListWatcher(this, i->first, i->second)); } ~ModuleHideList() diff --git a/src/modules/m_hidemode.cpp b/src/modules/m_hidemode.cpp index d6ae05801..d5ac57115 100644 --- a/src/modules/m_hidemode.cpp +++ b/src/modules/m_hidemode.cpp @@ -37,20 +37,24 @@ class Settings void Load() { - rankstosee.clear(); + RanksToSeeMap newranks; ConfigTagList tags = ServerInstance->Config->ConfTags("hidemode"); for (ConfigIter i = tags.first; i != tags.second; ++i) { ConfigTag* tag = i->second; - std::string modename = tag->getString("mode"); - unsigned int rank = tag->getInt("rank", 0, 0); - if (!modename.empty() && rank) - { - ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Hiding the %s mode from users below rank %u", modename.c_str(), rank); - rankstosee.insert(std::make_pair(modename, rank)); - } + const std::string modename = tag->getString("mode"); + if (modename.empty()) + throw ModuleException(" is empty at " + tag->getTagLocation()); + + unsigned int rank = tag->getUInt("rank", 0); + if (!rank) + throw ModuleException(" must be greater than 0 at " + tag->getTagLocation()); + + ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Hiding the %s mode from users below rank %u", modename.c_str(), rank); + newranks.insert(std::make_pair(modename, rank)); } + rankstosee.swap(newranks); } }; diff --git a/src/modules/m_httpd_acl.cpp b/src/modules/m_httpd_acl.cpp index 2dbc1be69..49710c219 100644 --- a/src/modules/m_httpd_acl.cpp +++ b/src/modules/m_httpd_acl.cpp @@ -51,7 +51,7 @@ class ModuleHTTPAccessList : public Module, public HTTPACLEventListener void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { - acl_list.clear(); + std::vector new_acls; ConfigTagList acls = ServerInstance->Config->ConfTags("httpdacl"); for (ConfigIter i = acls.first; i != acls.second; i++) { @@ -89,8 +89,9 @@ class ModuleHTTPAccessList : public Module, public HTTPACLEventListener ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Read ACL: path=%s pass=%s whitelist=%s blacklist=%s", path.c_str(), password.c_str(), whitelist.c_str(), blacklist.c_str()); - acl_list.push_back(HTTPACL(path, username, password, whitelist, blacklist)); + new_acls.push_back(HTTPACL(path, username, password, whitelist, blacklist)); } + acl_list.swap(new_acls); } void BlockAccess(HTTPRequest* http, unsigned int returnval, const std::string &extraheaderkey = "", const std::string &extraheaderval="") diff --git a/src/modules/m_inviteexception.cpp b/src/modules/m_inviteexception.cpp index 1317e6e57..bae8f7184 100644 --- a/src/modules/m_inviteexception.cpp +++ b/src/modules/m_inviteexception.cpp @@ -82,8 +82,8 @@ public: void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { - invite_bypass_key = ServerInstance->Config->ConfValue("inviteexception")->getBool("bypasskey", true); ie.DoRehash(); + invite_bypass_key = ServerInstance->Config->ConfValue("inviteexception")->getBool("bypasskey", true); } Version GetVersion() CXX11_OVERRIDE diff --git a/src/modules/m_pbkdf2.cpp b/src/modules/m_pbkdf2.cpp index 86530c5dd..036538a39 100644 --- a/src/modules/m_pbkdf2.cpp +++ b/src/modules/m_pbkdf2.cpp @@ -139,39 +139,65 @@ class PBKDF2Provider : public HashProvider } }; +struct ProviderConfig +{ + unsigned long dkey_length; + unsigned long iterations; +}; + +typedef std::map ProviderConfigMap; + class ModulePBKDF2 : public Module { std::vector providers; + ProviderConfig globalconfig; + ProviderConfigMap providerconfigs; - void GetConfig() + ProviderConfig GetConfigForProvider(const std::string& name) const + { + ProviderConfigMap::const_iterator it = providerconfigs.find(name); + if (it == providerconfigs.end()) + return globalconfig; + + return it->second; + } + + void ConfigureProviders() { - // First set the common values - ConfigTag* tag = ServerInstance->Config->ConfValue("pbkdf2"); - unsigned int global_iterations = tag->getUInt("iterations", 12288, 1); - unsigned int global_dkey_length = tag->getUInt("length", 32, 1, 1024); for (std::vector::iterator i = providers.begin(); i != providers.end(); ++i) { PBKDF2Provider* pi = *i; - pi->iterations = global_iterations; - pi->dkey_length = global_dkey_length; + ProviderConfig config = GetConfigForProvider(pi->name); + pi->iterations = config.iterations; + pi->dkey_length = config.dkey_length; } + } + + void GetConfig() + { + // First set the common values + ConfigTag* tag = ServerInstance->Config->ConfValue("pbkdf2"); + ProviderConfig newglobal; + newglobal.iterations = tag->getUInt("iterations", 12288, 1); + newglobal.dkey_length = tag->getUInt("length", 32, 1, 1024); // Then the specific values + ProviderConfigMap newconfigs; ConfigTagList tags = ServerInstance->Config->ConfTags("pbkdf2prov"); for (ConfigIter i = tags.first; i != tags.second; ++i) { tag = i->second; std::string hash_name = "hash/" + tag->getString("hash"); - for (std::vector::iterator j = providers.begin(); j != providers.end(); ++j) - { - PBKDF2Provider* pi = *j; - if (pi->provider->name != hash_name) - continue; + ProviderConfig& config = newconfigs[hash_name]; - pi->iterations = tag->getUInt("iterations", global_iterations, 1); - pi->dkey_length = tag->getUInt("length", global_dkey_length, 1, 1024); - } + config.iterations = tag->getUInt("iterations", newglobal.iterations, 1); + config.dkey_length = tag->getUInt("length", newglobal.dkey_length, 1, 1024); } + + // Config is valid, apply it + providerconfigs.swap(newconfigs); + std::swap(globalconfig, newglobal); + ConfigureProviders(); } public: @@ -194,7 +220,7 @@ class ModulePBKDF2 : public Module providers.push_back(prov); ServerInstance->Modules.AddService(*prov); - GetConfig(); + ConfigureProviders(); } void OnServiceDel(ServiceProvider& prov) CXX11_OVERRIDE diff --git a/src/modules/m_restrictchans.cpp b/src/modules/m_restrictchans.cpp index 9c7ed1213..348beed2c 100644 --- a/src/modules/m_restrictchans.cpp +++ b/src/modules/m_restrictchans.cpp @@ -22,21 +22,26 @@ #include "inspircd.h" +typedef insp::flat_set AllowChans; + class ModuleRestrictChans : public Module { - insp::flat_set allowchans; + AllowChans allowchans; public: void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { - allowchans.clear(); + AllowChans newallows; ConfigTagList tags = ServerInstance->Config->ConfTags("allowchannel"); for(ConfigIter i = tags.first; i != tags.second; ++i) { - ConfigTag* tag = i->second; - std::string txt = tag->getString("name"); - allowchans.insert(txt); + const std::string name = i->second->getString("name"); + if (name.empty()) + throw ModuleException("Empty at " + i->second->getTagLocation()); + + newallows.insert(name); } + allowchans.swap(newallows); } ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE diff --git a/src/modules/m_securelist.cpp b/src/modules/m_securelist.cpp index aa14707b1..6d9b64df2 100644 --- a/src/modules/m_securelist.cpp +++ b/src/modules/m_securelist.cpp @@ -22,9 +22,11 @@ #include "inspircd.h" #include "modules/account.h" +typedef std::vector AllowList; + class ModuleSecureList : public Module { - std::vector allowlist; + AllowList allowlist; bool exemptregistered; unsigned int WaitTime; @@ -36,15 +38,22 @@ class ModuleSecureList : public Module void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { - allowlist.clear(); + AllowList newallows; ConfigTagList tags = ServerInstance->Config->ConfTags("securehost"); for (ConfigIter i = tags.first; i != tags.second; ++i) - allowlist.push_back(i->second->getString("exception")); + { + std::string host = i->second->getString("exception"); + if (host.empty()) + throw ModuleException(" is a required field at " + i->second->getTagLocation()); + newallows.push_back(host); + } ConfigTag* tag = ServerInstance->Config->ConfValue("securelist"); + exemptregistered = tag->getBool("exemptregistered"); WaitTime = tag->getDuration("waittime", 60, 1); + allowlist.swap(newallows); } diff --git a/src/modules/m_vhost.cpp b/src/modules/m_vhost.cpp index 01df33cae..478a5100a 100644 --- a/src/modules/m_vhost.cpp +++ b/src/modules/m_vhost.cpp @@ -22,35 +22,55 @@ #include "inspircd.h" +struct CustomVhost +{ + const std::string name; + const std::string password; + const std::string hash; + const std::string vhost; + + CustomVhost(const std::string& n, const std::string& p, const std::string& h, const std::string& v) + : name(n) + , password(p) + , hash(h) + , vhost(v) + { + } + + bool CheckPass(User* user, const std::string& pass) const + { + return ServerInstance->PassCompare(user, password, pass, hash); + } +}; + +typedef std::multimap CustomVhostMap; +typedef std::pair MatchingConfigs; + /** Handle /VHOST */ class CommandVhost : public Command { public: - CommandVhost(Module* Creator) : Command(Creator,"VHOST", 2) + CustomVhostMap vhosts; + + CommandVhost(Module* Creator) + : Command(Creator, "VHOST", 2) { syntax = " "; } CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE { - ConfigTagList tags = ServerInstance->Config->ConfTags("vhost"); - for(ConfigIter i = tags.first; i != tags.second; ++i) - { - ConfigTag* tag = i->second; - std::string mask = tag->getString("host"); - std::string username = tag->getString("user"); - std::string pass = tag->getString("pass"); - std::string hash = tag->getString("hash"); + MatchingConfigs matching = vhosts.equal_range(parameters[0]); - if (parameters[0] == username && ServerInstance->PassCompare(user, pass, parameters[1], hash)) + for (MatchingConfigs::first_type i = matching.first; i != matching.second; ++i) + { + CustomVhost config = i->second; + if (config.CheckPass(user, parameters[1])) { - if (!mask.empty()) - { - user->WriteNotice("Setting your VHost: " + mask); - user->ChangeDisplayedHost(mask); - return CMD_SUCCESS; - } + user->WriteNotice("Setting your VHost: " + config.vhost); + user->ChangeDisplayedHost(config.vhost); + return CMD_SUCCESS; } } @@ -64,13 +84,39 @@ class ModuleVHost : public Module CommandVhost cmd; public: - ModuleVHost() : cmd(this) + ModuleVHost() + : cmd(this) + { + } + + void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { + CustomVhostMap newhosts; + ConfigTagList tags = ServerInstance->Config->ConfTags("vhost"); + for (ConfigIter i = tags.first; i != tags.second; ++i) + { + ConfigTag* tag = i->second; + std::string mask = tag->getString("host"); + if (mask.empty()) + throw ModuleException(" is empty! at " + tag->getTagLocation()); + std::string username = tag->getString("user"); + if (username.empty()) + throw ModuleException(" is empty! at " + tag->getTagLocation()); + std::string pass = tag->getString("pass"); + if (pass.empty()) + throw ModuleException(" is empty! at " + tag->getTagLocation()); + std::string hash = tag->getString("hash"); + + CustomVhost vhost(username, pass, hash, mask); + newhosts.insert(std::make_pair(username, vhost)); + } + + cmd.vhosts.swap(newhosts); } Version GetVersion() CXX11_OVERRIDE { - return Version("Provides masking of user hostnames via traditional /VHOST command",VF_VENDOR); + return Version("Provides masking of user hostnames via traditional /VHOST command", VF_VENDOR); } }; -- cgit v1.2.3