diff --git a/docs/conf/helpop.conf.example b/docs/conf/helpop.conf.example index b09ed6832..a5b9db775 100644 --- a/docs/conf/helpop.conf.example +++ b/docs/conf/helpop.conf.example @@ -833,7 +833,7 @@ using their cloak when they quit. r Marks as a having a registered nickname (requires the account module). w Receives wallops messages. - x Gives a cloaked hostname (requires the cloaking module). + x Gives a cloaked hostname (requires the cloak module). z Only allow private messages from TLS users (requires the sslmodes module). B Marks as a bot (requires the botmode module). diff --git a/docs/conf/inspircd.conf.example b/docs/conf/inspircd.conf.example index 8eee33021..468b53a22 100644 --- a/docs/conf/inspircd.conf.example +++ b/docs/conf/inspircd.conf.example @@ -319,7 +319,7 @@ # modes: User modes that are set on users in this block on connect. # Enabling this option requires that the conn_umodes module be loaded. # This entry is highly recommended to use for/with IP cloaking/masking. - # For the example to work, this also requires that the cloaking + # For the example to work, this also requires that the cloak # module be loaded as well. modes="+x" diff --git a/docs/conf/modules.conf.example b/docs/conf/modules.conf.example index d019906b6..2aad9bd70 100644 --- a/docs/conf/modules.conf.example +++ b/docs/conf/modules.conf.example @@ -31,7 +31,7 @@ # cryptographic uses and security. # # IMPORTANT: -# Other modules such as cloaking and password_hash may rely on +# Other modules such as cloak_md5 and password_hash may rely on # this module being loaded to function. # # @@ -518,21 +518,23 @@ # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# -# Cloaking module: Adds user mode +x and cloaking support. -# Relies on the md5 module being loaded. -# To cloak users when they connect, load the conn_umodes module and set -# to include the +x mode. The example tag -# shows this. See the conn_umodes module for more information. -# +# MD5 cloak module: Adds the "half" and "full" cloak methods. These +# methods are obsolete and should only be used on a network which is +# upgrading from v3 and wishes to keep ban compatibility. New networks +# should should use the "hmac-sha256" method (see below) instead. # -#-#-#-#-#-#-#-#-#-#-#- CLOAKING CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-# +# IMPORTANT: If you are using this module you should also load the md5 +# module. Failure to do so will result in users not being cloaked. +# +# +#-#-#-#-#-#-#-#-#-#-#- MD5 CLOAK CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-# # # -# To use cloaking, you must define a cloak key, and optionally a # +# To use cloak_md5, you must define a cloak key, and optionally a # # cloak prefix as shown below. The cloak key must be shared across # -# the network for consistent cloaking and must be at least thirty # +# the network for consistent hostnames and must be at least thirty # # characters long. # # # -# There are two methods of cloaking: # +# There are two methods provided by this module: # # # # half Cloak only the "unique" portion of a host; by # # default show the last 2 parts of the domain, # @@ -549,17 +551,17 @@ # # # IMPORTANT: Changing these details will break all of your existing # # bans. If you do not want this to happen you can define multiple # -# cloak tags. The first will be used for cloaking and the rest will # +# cloak tags. The first will be used for hostnames and the rest will # # be used for checking if a user is banned in a channel. # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # -# # -# diff --git a/src/configreader.cpp b/src/configreader.cpp index 9446695f2..487e80ac1 100644 --- a/src/configreader.cpp +++ b/src/configreader.cpp @@ -613,6 +613,11 @@ std::vector ServerConfig::GetModules() const // Rewrite the old names of renamed modules. if (stdalgo::string::equalsci(shortname, "cgiirc")) modules.push_back("gateway"); + else if (stdalgo::string::equalsci(shortname, "cloaking")) + { + modules.push_back("cloak"); + modules.push_back("cloak_md5"); + } else if (stdalgo::string::equalsci(shortname, "gecosban")) modules.push_back("realnameban"); else if (stdalgo::string::equalsci(shortname, "regex_pcre2")) diff --git a/src/modules/m_cloak_md5.cpp b/src/modules/m_cloak_md5.cpp index 098f29d2e..9eca2189b 100644 --- a/src/modules/m_cloak_md5.cpp +++ b/src/modules/m_cloak_md5.cpp @@ -32,8 +32,7 @@ #include "inspircd.h" -#include "clientprotocolevent.h" -#include "extension.h" +#include "modules/cloak.h" #include "modules/hash.h" enum CloakMode @@ -52,6 +51,7 @@ static constexpr char base32[] = "0123456789abcdefghijklmnopqrstuv"; static constexpr size_t minkeylen = 30; struct CloakInfo final + : public Cloak::Method { // The method used for cloaking users. CloakMode mode; @@ -71,206 +71,34 @@ struct CloakInfo final // The suffix for IP cloaks (e.g. .IP). std::string suffix; - CloakInfo(CloakMode Mode, const std::string& Key, const std::string& Prefix, const std::string& Suffix, bool IgnoreCase, unsigned int DomainParts = 0) - : mode(Mode) + dynamic_reference Hash; + + CloakInfo(Cloak::Engine* engine, CloakMode Mode, const std::string& Key, const std::string& Prefix, const std::string& Suffix, bool IgnoreCase, unsigned int DomainParts = 0) + : Cloak::Method(engine) + , mode(Mode) , domainparts(DomainParts) , ignorecase(IgnoreCase) , key(Key) , prefix(Prefix) , suffix(Suffix) - { - } -}; - -typedef std::vector CloakList; - -class CloakUser final - : public ModeHandler -{ -public: - bool active = false; - ListExtItem ext; - std::string debounce_uid; - time_t debounce_ts = 0; - int debounce_count = 0; - - CloakUser(Module* source) - : ModeHandler(source, "cloak", 'x', PARAM_NONE, MODETYPE_USER) - , ext(source, "cloaks", ExtensionType::USER) + , Hash(engine->creator, "hash/md5") { } - ModeAction OnModeChange(User* source, User* dest, Channel* channel, Modes::Change& change) override - { - LocalUser* user = IS_LOCAL(dest); - - /* For remote clients, we don't take any action, we just allow it. - * The local server where they are will set their cloak instead. - * This is fine, as we will receive it later. - */ - if (!user) - { - // Remote setters broadcast mode before host while local setters do the opposite, so this takes that into account - active = IS_LOCAL(source) ? change.adding : !change.adding; - dest->SetMode(this, change.adding); - return MODEACTION_ALLOW; - } - - if (user->uuid == debounce_uid && debounce_ts == ServerInstance->Time()) - { - // prevent spamming using /mode user +x-x+x-x+x-x - if (++debounce_count > 2) - return MODEACTION_DENY; - } - else - { - debounce_uid = user->uuid; - debounce_count = 1; - debounce_ts = ServerInstance->Time(); - } - - if (change.adding == user->IsModeSet(this)) - return MODEACTION_DENY; - - /* don't allow this user to spam modechanges */ - if (source == dest) - user->CommandFloodPenalty += 5000; - - if (change.adding) - { - // assume this is more correct - if (!user->IsFullyConnected() && user->GetRealHost() != user->GetDisplayedHost()) - return MODEACTION_DENY; - - CloakList* cloaks = ext.Get(user); - if (!cloaks) - { - /* Force creation of missing cloak */ - try - { - creator->OnUserConnect(user); - cloaks = ext.Get(user); - } - catch (const CoreException& modexcept) - { - ServerInstance->Logs.Normal(MODNAME, "Exception caught when generating cloak: " + modexcept.GetReason()); - return MODEACTION_DENY; - } - } - - // If we have a cloak then set the hostname. - if (cloaks && !cloaks->empty()) - { - user->ChangeDisplayedHost(cloaks->front()); - user->SetMode(this, true); - return MODEACTION_ALLOW; - } - else - return MODEACTION_DENY; - } - else - { - /* User is removing the mode, so restore their real host - * and make it match the displayed one. - */ - user->SetMode(this, false); - user->ChangeDisplayedHost(user->GetRealHost()); - return MODEACTION_ALLOW; - } - } -}; - -class CommandCloak final - : public Command -{ -public: - CommandCloak(Module* Creator) - : Command(Creator, "CLOAK", 1) - { - access_needed = CmdAccess::OPERATOR; - syntax = { "" }; - } - - CmdResult Handle(User* user, const Params& parameters) override; -}; - -class ModuleCloaking final - : public Module -{ -public: - CloakUser cu; - CommandCloak ck; - std::vector cloaks; - dynamic_reference Hash; - - ModuleCloaking() - : Module(VF_VENDOR | VF_COMMON, "Adds user mode x (cloak) which allows user hostnames to be hidden.") - , cu(this) - , ck(this) - , Hash(this, "hash/md5") - { - } - - /** Takes a domain name and retrieves the subdomain which should be visible. - * This is usually the last \p domainparts labels but if not enough are - * present then all but the most specific label are used. If the domain name - * consists of one label only then none are used. - * - * Here are some examples for how domain names will be shortened assuming - * \p domainparts is set to the default of 3. - * - * "this.is.an.example.com" => ".an.example.com" - * "an.example.com" => ".example.com" - * "example.com" => ".com" - * "localhost" => "" - * - * @param host The hostname to cloak. - * @param domainparts The number of domain labels that should be visible. - * @return The visible segment of the hostname. - */ - static std::string VisibleDomainParts(const std::string& host, unsigned int domainparts) - { - // The position at which we found the last dot. - std::string::const_reverse_iterator dotpos; - - // The number of dots we have seen so far. - unsigned int seendots = 0; - - for (std::string::const_reverse_iterator iter = host.rbegin(); iter != host.rend(); ++iter) - { - if (*iter != '.') - continue; - - // We have found a dot! - dotpos = iter; - seendots += 1; - - // Do we have enough segments to stop? - if (seendots >= domainparts) - break; - } - - // We only returns a domain part if more than one label is - // present. See above for a full explanation. - if (!seendots) - return ""; - return std::string(dotpos.base() - 1, host.end()); - } - - /** + /* * 2.0-style cloaking function * @param item The item to cloak (part of an IP or hostname) * @param id A unique ID for this type of item (to make it unique if the item matches) * @param len The length of the output. Maximum for MD5 is 16 characters. */ - std::string SegmentCloak(const CloakInfo& info, const std::string& item, char id, size_t len) + std::string SegmentCloak(const std::string& item, char id, size_t len) { std::string input; - input.reserve(info.key.length() + 3 + item.length()); + input.reserve(key.length() + 3 + item.length()); input.append(1, id); - input.append(info.key); + input.append(key); input.append(1, '\0'); // null does not terminate a C++ string - if (info.ignorecase) + if (ignorecase) std::transform(item.begin(), item.end(), std::back_inserter(input), ::tolower); else input.append(item); @@ -286,7 +114,7 @@ public: return rv; } - std::string SegmentIP(const CloakInfo& info, const irc::sockets::sockaddrs& ip, bool full) + std::string SegmentIP(const irc::sockets::sockaddrs& ip, bool full) { std::string bindata; size_t hop1; @@ -305,7 +133,7 @@ public: len2 = 4; // pfx s1.s2.s3. (xxxx.xxxx or s4) sfx // 6 4 4 9/6 - rv.reserve(info.prefix.length() + 26 + info.suffix.length()); + rv.reserve(prefix.length() + 26 + suffix.length()); } else { @@ -315,27 +143,27 @@ public: hop3 = 2; len1 = len2 = 3; // pfx s1.s2. (xxx.xxx or s3) sfx - rv.reserve(info.prefix.length() + 15 + info.suffix.length()); + rv.reserve(prefix.length() + 15 + suffix.length()); } - rv.append(info.prefix); - rv.append(SegmentCloak(info, bindata, 10, len1)); + rv.append(prefix); + rv.append(SegmentCloak(bindata, 10, len1)); rv.append(1, '.'); bindata.erase(hop1); - rv.append(SegmentCloak(info, bindata, 11, len2)); + rv.append(SegmentCloak(bindata, 11, len2)); if (hop2) { rv.append(1, '.'); bindata.erase(hop2); - rv.append(SegmentCloak(info, bindata, 12, len2)); + rv.append(SegmentCloak(bindata, 12, len2)); } if (full) { rv.append(1, '.'); bindata.erase(hop3); - rv.append(SegmentCloak(info, bindata, 13, 6)); - rv.append(info.suffix); + rv.append(SegmentCloak(bindata, 13, 6)); + rv.append(suffix); } else { @@ -343,102 +171,45 @@ public: { rv.append(InspIRCd::Format(".%02x%02x.%02x%02x%s", ip.in6.sin6_addr.s6_addr[2], ip.in6.sin6_addr.s6_addr[3], - ip.in6.sin6_addr.s6_addr[0], ip.in6.sin6_addr.s6_addr[1], info.suffix.c_str())); + ip.in6.sin6_addr.s6_addr[0], ip.in6.sin6_addr.s6_addr[1], suffix.c_str())); } else { const unsigned char* ip4 = (const unsigned char*)&ip.in4.sin_addr; - rv.append(InspIRCd::Format(".%d.%d%s", ip4[1], ip4[0], info.suffix.c_str())); + rv.append(InspIRCd::Format(".%d.%d%s", ip4[1], ip4[0], suffix.c_str())); } } return rv; } - ModResult OnCheckBan(User* user, Channel* chan, const std::string& mask) override - { - LocalUser* lu = IS_LOCAL(user); - if (!lu) - return MOD_RES_PASSTHRU; - - // Force the creation of cloaks if not already set. - OnUserConnect(lu); - - // If the user has no cloaks (i.e. UNIX socket) then we do nothing here. - CloakList* cloaklist = cu.ext.Get(user); - if (!cloaklist || cloaklist->empty()) - return MOD_RES_PASSTHRU; - - // Check if they have a cloaked host but are not using it. - for (const auto& cloak : *cloaklist) - { - if (cloak != user->GetDisplayedHost()) - { - const std::string cloakMask = user->nick + "!" + user->ident + "@" + cloak; - if (InspIRCd::Match(cloakMask, mask)) - return MOD_RES_DENY; - } - } - return MOD_RES_PASSTHRU; - } - - void Prioritize() override - { - /* Needs to be after m_banexception etc. */ - ServerInstance->Modules.SetPriority(this, I_OnCheckBan, PRIORITY_LAST); - } - - // this unsets umode +x on every host change. If we are actually doing a +x - // mode change, we will call SetMode back to true AFTER the host change is done. - void OnChangeHost(User* u, const std::string& host) override - { - if (u->IsModeSet(cu) && !cu.active) - { - u->SetMode(cu, false); - - LocalUser* luser = IS_LOCAL(u); - if (!luser) - return; - - Modes::ChangeList modechangelist; - modechangelist.push_remove(&cu); - ClientProtocol::Events::Mode modeevent(ServerInstance->FakeClient, nullptr, u, modechangelist); - luser->Send(modeevent); - } - cu.active = false; - } - - std::string GetCompatLinkData(const CloakInfo& info) + std::string GetCompatLinkData() { std::string data = "broken"; if (Hash) { - switch (info.mode) + switch (mode) { case MODE_HALF_CLOAK: // Use old cloaking verification to stay compatible with 2.0 // But verify domainparts and ignorecase when use 3.0-only features - if (info.domainparts == 3 && !info.ignorecase) - data = info.prefix + SegmentCloak(info, "*", 3, 8) + info.suffix; + if (domainparts == 3 && !ignorecase) + data = prefix + SegmentCloak("*", 3, 8) + suffix; else { irc::sockets::sockaddrs sa; - data = GenCloak(info, sa, "", data + ConvToStr(info.domainparts)) + (info.ignorecase ? "-ci" : ""); + data = GenCloak(sa, "", data + ConvToStr(domainparts)) + (ignorecase ? "-ci" : ""); } break; case MODE_OPAQUE: - data = info.prefix + SegmentCloak(info, "*", 4, 8) + info.suffix + (info.ignorecase ? "-ci" : ""); + data = prefix + SegmentCloak("*", 4, 8) + suffix + (ignorecase ? "-ci" : ""); } } return data; } - void GetLinkData(LinkData& data, std::string& compatdata) override + void GetLinkData(Module::LinkData& data, std::string& compatdata) override { - if (cloaks.empty()) - return; - - const CloakInfo& info = cloaks.front(); - switch (info.mode) + switch (mode) { case MODE_HALF_CLOAK: data["mode"] = "half"; @@ -447,127 +218,116 @@ public: data["mode"] = "full"; break; } - data["domain-parts"] = ConvToStr(info.domainparts); + data["domain-parts"] = ConvToStr(domainparts); data["hash"] = Hash ? Hash->name : "broken"; - data["ignore-case"] = info.ignorecase ? "yes" : "no"; - data["key"] = info.key; - data["prefix"] = info.prefix; - data["suffix"] = info.suffix; - compatdata = GetCompatLinkData(info); + data["ignore-case"] = ignorecase ? "yes" : "no"; + data["key"] = key; + data["prefix"] = prefix; + data["suffix"] = suffix; + compatdata = GetCompatLinkData(); } - void ReadConfig(ConfigStatus& status) override - { - auto tags = ServerInstance->Config->ConfTags("cloak"); - if (tags.empty()) - throw ModuleException(this, "You have loaded the cloaking module but not configured any tags!"); - - bool firstcloak = true; - std::vector newcloaks; - for (const auto& [_, tag] : tags) - { - // Ensure that we have the parameter. - const std::string key = tag->getString("key"); - if (key.empty()) - throw ModuleException(this, "You have not defined a cloaking key. Define as a " + ConvToStr(minkeylen) + "+ character network-wide secret, at " + tag->source.str()); - - // If we are the first cloak method then mandate a strong key. - if (firstcloak && key.length() < minkeylen) - throw ModuleException(this, "Your cloaking key is not secure. It should be at least " + ConvToStr(minkeylen) + " characters long, at " + tag->source.str()); - - firstcloak = false; - const bool ignorecase = tag->getBool("ignorecase"); - const std::string mode = tag->getString("mode"); - const std::string prefix = tag->getString("prefix"); - const std::string suffix = tag->getString("suffix", ".IP"); - if (stdalgo::string::equalsci(mode, "half")) - { - unsigned int domainparts = static_cast(tag->getUInt("domainparts", 3, 1, 10)); - newcloaks.emplace_back(MODE_HALF_CLOAK, key, prefix, suffix, ignorecase, domainparts); - } - else if (stdalgo::string::equalsci(mode, "full")) - newcloaks.emplace_back(MODE_OPAQUE, key, prefix, suffix, ignorecase); - else - throw ModuleException(this, mode + " is an invalid value for ; acceptable values are 'half' and 'full', at " + tag->source.str()); - } - - // The cloak configuration was valid so we can apply it. - cloaks.swap(newcloaks); - } - - std::string GenCloak(const CloakInfo& info, const irc::sockets::sockaddrs& ip, const std::string& ipstr, const std::string& host) + std::string GenCloak(const irc::sockets::sockaddrs& ip, const std::string& ipstr, const std::string& host) { std::string chost; irc::sockets::sockaddrs hostip(false); bool host_is_ip = hostip.from_ip_port(host, ip.port()) && hostip == ip; - switch (info.mode) + switch (mode) { case MODE_HALF_CLOAK: { if (!host_is_ip) - chost = info.prefix + SegmentCloak(info, host, 1, 6) + VisibleDomainParts(host, info.domainparts); + chost = prefix + SegmentCloak(host, 1, 6) + "." + Cloak::VisiblePart(host, domainparts, '.'); if (chost.empty() || chost.length() > 50) - chost = SegmentIP(info, ip, false); + chost = SegmentIP(ip, false); break; } case MODE_OPAQUE: - chost = SegmentIP(info, ip, true); + chost = SegmentIP(ip, true); break; } return chost; } - void OnChangeRemoteAddress(LocalUser* user) override + std::string Generate(LocalUser* user) override ATTR_NOT_NULL(2) { - // Connecting users are handled in OnUserConnect not here. - if (!user->IsFullyConnected() || user->quitting) - return; + if (!Hash) + return {}; - // Remove the cloaks and generate new ones. - cu.ext.Unset(user); - OnUserConnect(user); + if (user->client_sa.family() != AF_INET && user->client_sa.family() != AF_INET6) + return {}; - // If a user is using a cloak then update it. - if (user->IsModeSet(cu)) - { - CloakList* cloaklist = cu.ext.Get(user); - user->ChangeDisplayedHost(cloaklist->front()); - } + return GenCloak(user->client_sa, user->GetIPString(), user->GetRealHost()); } - void OnUserConnect(LocalUser* dest) override + std::string Generate(const std::string& hostip) override { - if (cu.ext.Get(dest)) - return; + if (!Hash) + return {}; - // TODO: decide how we are going to cloak AF_UNIX hostnames. - if (dest->client_sa.family() != AF_INET && dest->client_sa.family() != AF_INET6) - return; - - CloakList cloaklist; - for (const auto& cloak : cloaks) - cloaklist.push_back(GenCloak(cloak, dest->client_sa, dest->GetIPString(), dest->GetRealHost())); - cu.ext.Set(dest, cloaklist); + irc::sockets::sockaddrs sa; + const char* ipaddr = sa.from_ip(hostip) ? hostip.c_str() : ""; + return GenCloak(sa, ipaddr, hostip); } }; -CmdResult CommandCloak::Handle(User* user, const Params& parameters) +class MD5Engine final + : public Cloak::Engine { - ModuleCloaking* mod = (ModuleCloaking*)(Module*)creator; +private: + bool halfcloak; - // If we're cloaking an IP address we pass it in the IP field too. - irc::sockets::sockaddrs sa; - const char* ipaddr = sa.from_ip(parameters[0]) ? parameters[0].c_str() : ""; - - unsigned int id = 0; - for (const auto& info : mod->cloaks) +public: + MD5Engine(Module* Creator, const std::string& Name, bool hc) + : Cloak::Engine(Creator, Name) + , halfcloak(hc) { - const std::string cloak = mod->GenCloak(info, sa, ipaddr, parameters[0]); - user->WriteNotice(InspIRCd::Format("*** Cloak #%u for %s is %s", ++id, parameters[0].c_str(), cloak.c_str())); } - return CmdResult::SUCCESS; -} -MODULE_INIT(ModuleCloaking) + Cloak::MethodPtr Create(const std::shared_ptr& tag, bool primary) override + { + // Ensure that we have the parameter. + const std::string key = tag->getString("key"); + if (key.empty()) + throw ModuleException(creator, "You have not defined a cloaking key. Define as a " + ConvToStr(minkeylen) + "+ character network-wide secret, at " + tag->source.str()); + + // If we are the first cloak method then mandate a strong key. + if (primary && key.length() < minkeylen) + throw ModuleException(creator, "Your cloaking key is not secure. It should be at least " + ConvToStr(minkeylen) + " characters long, at " + tag->source.str()); + + const bool ignorecase = tag->getBool("ignorecase"); + const std::string mode = tag->getString("mode"); + const std::string prefix = tag->getString("prefix"); + const std::string suffix = tag->getString("suffix", ".IP"); + + if (halfcloak) + { + unsigned int domainparts = static_cast(tag->getUInt("domainparts", 3, 1, 10)); + return std::make_shared(this, MODE_HALF_CLOAK, key, prefix, suffix, ignorecase, domainparts); + } + else + { + return std::make_shared(this, MODE_OPAQUE, key, prefix, suffix, ignorecase); + } + } +}; + +class ModuleCloakMD5 final + : public Module +{ +private: + MD5Engine halfcloakengine; + MD5Engine fullcloakengine; + +public: + ModuleCloakMD5() + : Module(VF_VENDOR, "Provides the half and full cloak engines.") + , halfcloakengine(this, "half", true) + , fullcloakengine(this, "full", false) + { + } +}; + +MODULE_INIT(ModuleCloakMD5) diff --git a/src/modules/m_spanningtree/capab.cpp b/src/modules/m_spanningtree/capab.cpp index dee87c4e5..14e90f987 100644 --- a/src/modules/m_spanningtree/capab.cpp +++ b/src/modules/m_spanningtree/capab.cpp @@ -57,7 +57,9 @@ namespace modname.append(name.substr(0, endpos)).append(".so"); // Handle renamed modules. - if (stdalgo::string::equalsci(modname, "m_realnameban.so")) + if (stdalgo::string::equalsci(modname, "m_cloak.so")) + modname = "m_cloaking.so"; + else if (stdalgo::string::equalsci(modname, "m_realnameban.so")) modname = "m_gecosban.so"; else if (stdalgo::string::equalsci(modname, "m_account.so") && ServerInstance->Modules.Find("services")) modname = "m_services_account.so"; diff --git a/src/modules/m_spanningtree/main.cpp b/src/modules/m_spanningtree/main.cpp index 365994715..6f517afa2 100644 --- a/src/modules/m_spanningtree/main.cpp +++ b/src/modules/m_spanningtree/main.cpp @@ -878,7 +878,7 @@ ModuleSpanningTree::~ModuleSpanningTree() /* It is IMPORTANT that m_spanningtree is the last module in the chain * so that any activity it sees is FINAL, e.g. we arent going to send out - * a NICK message before m_cloaking has finished putting the +x on the user, + * a NICK message before the cloak module has finished putting the +x on the user, * etc etc. * Therefore, we set our priority to PRIORITY_LAST to make sure we end up at the END of * the module call queue.