summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/conf/modules.conf.example7
-rw-r--r--src/modules/m_hostchange.cpp269
2 files changed, 175 insertions, 101 deletions
diff --git a/docs/conf/modules.conf.example b/docs/conf/modules.conf.example
index bc90c7b14..5635e717a 100644
--- a/docs/conf/modules.conf.example
+++ b/docs/conf/modules.conf.example
@@ -927,11 +927,10 @@
# #
# See https://wiki.inspircd.org/Modules/3.0/hostchange for help. #
# #
-#<host suffix="invalid.org" separator="." prefix="">
-#<hostchange mask="*@42.theanswer.example.org" action="addnick">
-#<hostchange mask="*root@*" action="suffix">
+#<hostchange mask="*@42.theanswer.example.org" action="addaccount" suffix=".users.example.com">
+#<hostchange mask="*root@*" action="addnick" prefix="example/users/">
#<hostchange mask="a@example.com" action="set" value="foo.bar.baz">
-#<hostchange mask="localhost" ports="7000,7001,7005-7007" action="set" value="blahblah.foo">
+#<hostchange mask="*@localhost" ports="7000,7001,7005-7007" action="set" value="blahblah.foo">
# hostcycle: If loaded, when a user gets a host or ident set, it will
# cycle them in all their channels. If not loaded it will simply change
diff --git a/src/modules/m_hostchange.cpp b/src/modules/m_hostchange.cpp
index 6d5896ef5..201c4b59b 100644
--- a/src/modules/m_hostchange.cpp
+++ b/src/modules/m_hostchange.cpp
@@ -20,141 +20,216 @@
#include "inspircd.h"
+#include "modules/account.h"
-/** Holds information on a host set by m_hostchange
- */
-class Host
+// Holds information about a <hostchange> rule.
+class HostRule
{
public:
enum HostChangeAction
{
- HCA_SET,
- HCA_SUFFIX,
- HCA_ADDNICK
+ // Add the user's account name to their hostname.
+ HCA_ADDACCOUNT,
+
+ // Add the user's nickname to their hostname.
+ HCA_ADDNICK,
+
+ // Set the user's hostname to the specific value.
+ HCA_SET
};
+ private:
HostChangeAction action;
- std::string newhost;
- std::string ports;
+ std::string host;
+ std::string mask;
+ insp::flat_set<int> ports;
+ std::string prefix;
+ std::string suffix;
+
+ public:
+ HostRule(const std::string& Host, const std::string& Mask, const insp::flat_set<int>& Ports)
+ : action(HCA_SET)
+ , host(Host)
+ , mask(Mask)
+ , ports(Ports)
+ {
+ }
+
+ HostRule(HostChangeAction Action, const std::string& Mask, const insp::flat_set<int>& Ports, const std::string& Prefix, const std::string& Suffix)
+ : action(Action)
+ , mask(Mask)
+ , ports(Ports)
+ , prefix(Prefix)
+ , suffix(Suffix)
+ {
+ }
+
+ HostChangeAction GetAction() const
+ {
+ return action;
+ }
+
+ const std::string& GetHost() const
+ {
+ return host;
+ }
+
+ bool Matches(LocalUser* user) const
+ {
+ if (!ports.empty() && !ports.count(user->GetServerPort()))
+ return false;
+
+ if (InspIRCd::MatchCIDR(user->MakeHost(), mask))
+ return true;
+
+ return InspIRCd::MatchCIDR(user->MakeHostIP(), mask);
+ }
- Host(HostChangeAction Action, const std::string& Newhost, const std::string& Ports) :
- action(Action), newhost(Newhost), ports(Ports) {}
+ void Wrap(const std::string& value, std::string& out) const
+ {
+ if (!prefix.empty())
+ out.append(prefix);
+
+ out.append(value);
+
+ if (!suffix.empty())
+ out.append(suffix);
+ }
};
-typedef std::vector<std::pair<std::string, Host> > hostchanges_t;
+typedef std::vector<HostRule> HostRules;
class ModuleHostChange : public Module
{
- hostchanges_t hostchanges;
- std::string MySuffix;
- std::string MyPrefix;
- std::string MySeparator;
+private:
+ std::bitset<UCHAR_MAX> hostmap;
+ HostRules hostrules;
+
+ std::string CleanName(const std::string& name)
+ {
+ std::string buffer;
+ buffer.reserve(name.length());
+ for (std::string::const_iterator iter = name.begin(); iter != name.end(); ++iter)
+ {
+ if (hostmap.test(static_cast<unsigned char>(*iter)))
+ {
+ buffer.push_back(*iter);
+ }
+ }
+ return buffer;
+ }
public:
void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- ConfigTag* host = ServerInstance->Config->ConfValue("host");
- MySuffix = host->getString("suffix");
- MyPrefix = host->getString("prefix");
- MySeparator = host->getString("separator", ".");
- hostchanges.clear();
+ HostRules rules;
- std::set<std::string> dupecheck;
ConfigTagList tags = ServerInstance->Config->ConfTags("hostchange");
for (ConfigIter i = tags.first; i != tags.second; ++i)
{
ConfigTag* tag = i->second;
- std::string mask = tag->getString("mask");
- if (!dupecheck.insert(mask).second)
- throw ModuleException("Duplicate hostchange entry: " + mask);
- Host::HostChangeAction act;
- std::string newhost;
- std::string action = tag->getString("action");
- if (!strcasecmp(action.c_str(), "set"))
+ // Ensure that we have the <hostchange:mask> parameter.
+ const std::string mask = tag->getString("mask");
+ if (mask.empty())
+ throw ModuleException("<hostchange:mask> is a mandatory field, at " + tag->getTagLocation());
+
+ insp::flat_set<int> ports;
+ const std::string portlist = tag->getString("ports");
+ if (!ports.empty())
{
- act = Host::HCA_SET;
- newhost = tag->getString("value");
+ irc::portparser portrange(portlist, false);
+ while (int port = portrange.GetToken())
+ ports.insert(port);
}
- else if (!strcasecmp(action.c_str(), "suffix"))
- act = Host::HCA_SUFFIX;
- else if (!strcasecmp(action.c_str(), "addnick"))
- act = Host::HCA_ADDNICK;
- else
- throw ModuleException("Invalid hostchange action: " + action);
- hostchanges.push_back(std::make_pair(mask, Host(act, newhost, tag->getString("ports"))));
+ // Determine what type of host rule this is.
+ const std::string action = tag->getString("action");
+ if (stdalgo::string::equalsci(action, "addaccount"))
+ {
+ // The hostname is in the format [prefix]<account>[suffix].
+ rules.push_back(HostRule(HostRule::HCA_ADDACCOUNT, mask, ports, tag->getString("prefix"), tag->getString("suffix")));
+ }
+ else if (stdalgo::string::equalsci(action, "addnick"))
+ {
+ // The hostname is in the format [prefix]<nick>[suffix].
+ rules.push_back(HostRule(HostRule::HCA_ADDNICK, mask, ports, tag->getString("prefix"), tag->getString("suffix")));
+ }
+ else if (stdalgo::string::equalsci(action, "set"))
+ {
+ // Ensure that we have the <hostchange:value> parameter.
+ const std::string value = tag->getString("value");
+ if (value.empty())
+ throw ModuleException("<hostchange:value> is a mandatory field when using the 'set' action, at " + tag->getTagLocation());
+
+ // The hostname is in the format <value>.
+ rules.push_back(HostRule(mask, value, ports));
+ continue;
+ }
+ else
+ {
+ throw ModuleException(action + " is an invalid <hostchange:action> type, at " + tag->getTagLocation());
+ }
}
+
+ const std::string hmap = ServerInstance->Config->ConfValue("hostname")->getString("charmap", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789");
+ hostmap.reset();
+ for (std::string::const_iterator iter = hmap.begin(); iter != hmap.end(); ++iter)
+ hostmap.set(static_cast<unsigned char>(*iter));
+ hostrules.swap(rules);
}
Version GetVersion() CXX11_OVERRIDE
{
- // returns the version number of the module to be
- // listed in /MODULES
- return Version("Provides masking of user hostnames in a different way to m_cloaking", VF_VENDOR);
+ return Version("Provides rule-based masking of user hostnames", VF_VENDOR);
}
void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
{
- for (hostchanges_t::iterator i = hostchanges.begin(); i != hostchanges.end(); i++)
+ for (HostRules::const_iterator iter = hostrules.begin(); iter != hostrules.end(); ++iter)
{
- if (((InspIRCd::MatchCIDR(user->MakeHost(), i->first)) || (InspIRCd::MatchCIDR(user->MakeHostIP(), i->first))))
+ const HostRule& rule = *iter;
+ if (!rule.Matches(user))
+ continue;
+
+ std::string newhost;
+ if (rule.GetAction() == HostRule::HCA_ADDACCOUNT)
+ {
+ // Retrieve the account name.
+ const AccountExtItem* accountext = GetAccountExtItem();
+ const std::string* accountptr = accountext ? accountext->get(user) : NULL;
+ if (!accountptr)
+ continue;
+
+ // Remove invalid hostname characters.
+ std::string accountname = CleanName(*accountptr);
+ if (accountname.empty())
+ continue;
+
+ // Create the hostname.
+ rule.Wrap(accountname, newhost);
+ }
+ else if (rule.GetAction() == HostRule::HCA_ADDNICK)
+ {
+ // Remove invalid hostname characters.
+ const std::string nickname = CleanName(user->nick);
+ if (nickname.empty())
+ continue;
+
+ // Create the hostname.
+ rule.Wrap(nickname, newhost);
+ }
+ else if (rule.GetAction() == HostRule::HCA_SET)
+ {
+ newhost.assign(rule.GetHost());
+ }
+
+ if (!newhost.empty())
{
- const Host& h = i->second;
-
- if (!h.ports.empty())
- {
- irc::portparser portrange(h.ports, false);
- long portno = -1;
- bool foundany = false;
-
- while ((portno = portrange.GetToken()))
- if (portno == user->GetServerPort())
- foundany = true;
-
- if (!foundany)
- continue;
- }
-
- // host of new user matches a hostchange tag's mask
- std::string newhost;
- if (h.action == Host::HCA_SET)
- {
- newhost = h.newhost;
- }
- else if (h.action == Host::HCA_SUFFIX)
- {
- newhost = MySuffix;
- }
- else if (h.action == Host::HCA_ADDNICK)
- {
- // first take their nick and strip out non-dns, leaving just [A-Z0-9\-]
- std::string complete;
- for (std::string::const_iterator j = user->nick.begin(); j != user->nick.end(); ++j)
- {
- if (((*j >= 'A') && (*j <= 'Z')) ||
- ((*j >= 'a') && (*j <= 'z')) ||
- ((*j >= '0') && (*j <= '9')) ||
- (*j == '-'))
- {
- complete = complete + *j;
- }
- }
- if (complete.empty())
- complete = "i-have-a-lame-nick";
-
- if (!MyPrefix.empty())
- newhost = MyPrefix + MySeparator + complete;
- else
- newhost = complete + MySeparator + MySuffix;
- }
- if (!newhost.empty())
- {
- user->WriteNotice("Setting your virtual host: " + newhost);
- if (!user->ChangeDisplayedHost(newhost))
- user->WriteNotice("Could not set your virtual host: " + newhost);
- return;
- }
+ user->WriteNotice("Setting your virtual host: " + newhost);
+ if (!user->ChangeDisplayedHost(newhost))
+ user->WriteNotice("Could not set your virtual host: " + newhost);
+ return;
}
}
}