/* * InspIRCd -- Internet Relay Chat Daemon * * Copyright (C) 2013, 2018-2021 Sadie Powell * Copyright (C) 2012-2014 Attila Molnar * Copyright (C) 2012 Robby * Copyright (C) 2009 Robin Burchell * Copyright (C) 2009 Daniel De Graaf * Copyright (C) 2007 Dennis Friis * Copyright (C) 2005-2007, 2010 Craig Edwards * * This file is part of InspIRCd. InspIRCd is free software: you can * redistribute it and/or modify it under the terms of the GNU General Public * License as published by the Free Software Foundation, version 2. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "inspircd.h" #include "modules/account.h" // Holds information about a rule. class HostRule { public: enum HostChangeAction { // 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 host; std::string mask; insp::flat_set ports; std::string prefix; std::string suffix; public: HostRule(const std::string& Mask, const std::string& Host, const insp::flat_set& Ports) : action(HCA_SET) , host(Host) , mask(Mask) , ports(Ports) { } HostRule(HostChangeAction Action, const std::string& Mask, const insp::flat_set& 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->server_sa.port())) return false; if (InspIRCd::MatchCIDR(user->MakeHost(), mask)) return true; return InspIRCd::MatchCIDR(user->MakeHostIP(), mask); } 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 HostRules; class ModuleHostChange : public Module { private: std::bitset 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(*iter))) { buffer.push_back(*iter); } } return buffer; } public: void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { HostRules rules; ConfigTagList tags = ServerInstance->Config->ConfTags("hostchange"); for (ConfigIter i = tags.first; i != tags.second; ++i) { ConfigTag* tag = i->second; // Ensure that we have the parameter. const std::string mask = tag->getString("mask"); if (mask.empty()) throw ModuleException(" is a mandatory field, at " + tag->getTagLocation()); insp::flat_set ports; const std::string portlist = tag->getString("ports"); if (!portlist.empty()) { irc::portparser portrange(portlist, false); while (int port = portrange.GetToken()) ports.insert(port); } // 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][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][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 parameter. const std::string value = tag->getString("value"); if (value.empty()) throw ModuleException(" is a mandatory field when using the 'set' action, at " + tag->getTagLocation()); // The hostname is in the format . rules.push_back(HostRule(mask, value, ports)); continue; } else { throw ModuleException(action + " is an invalid type, at " + tag->getTagLocation()); } } ConfigTag* tag = ServerInstance->Config->ConfValue("hostname"); const std::string hmap = tag->getString("charmap", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789", 1); hostmap.reset(); for (std::string::const_iterator iter = hmap.begin(); iter != hmap.end(); ++iter) hostmap.set(static_cast(*iter)); hostrules.swap(rules); } Version GetVersion() CXX11_OVERRIDE { return Version("Allows the server administrator to define custom rules for applying hostnames to users.", VF_VENDOR); } void OnUserConnect(LocalUser* user) CXX11_OVERRIDE { for (HostRules::const_iterator iter = hostrules.begin(); iter != hostrules.end(); ++iter) { 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()) { user->WriteNotice("Setting your virtual host: " + newhost); if (!user->ChangeDisplayedHost(newhost)) user->WriteNotice("Could not set your virtual host: " + newhost); return; } } } }; MODULE_INIT(ModuleHostChange)