Merge branch 'insp3' into master.

This commit is contained in:
Sadie Powell 2021-03-30 10:12:41 +01:00
commit f602febac5
11 changed files with 140 additions and 77 deletions

View File

@ -43,6 +43,6 @@ InspIRCd is licensed under [version 2 of the GNU General Public License](https:/
* [Website](https://www.inspircd.org)
* [Documentation](https://docs.inspircd.org)
* [GitHub](https://github.com/inspircd)
* [Support IRC channel](https://kiwiirc.com/nextclient/irc.inspircd.org:+6697/inspircd?nick=Your%20nick%20here) — \#inspircd on irc.inspircd.org
* [Development IRC channel](https://kiwiirc.com/nextclient/irc.inspircd.org:+6697/inspircd.dev?nick=Your%20nick%20here) — \#inspircd.dev on irc.inspircd.org
* [InspIRCd test network](https://kiwiirc.com/nextclient/testnet.inspircd.org:+6697/?nick=Your%20nick%20here) — testnet.inspircd.org
* Support IRC channel — \#inspircd on irc.inspircd.org
* Development IRC channel — \#inspircd.dev on irc.inspircd.org
* InspIRCd test network — testnet.inspircd.org

2
configure vendored
View File

@ -210,7 +210,7 @@ if (defined $opt_portable) {
$config{LOG_DIR} = $opt_log_dir // catdir $config{BASE_DIR}, 'var/log/inspircd';
$config{MANUAL_DIR} = $opt_manual_dir // catdir $config{BASE_DIR}, 'usr/share/man/man1';
$config{MODULE_DIR} = $opt_module_dir // catdir $config{BASE_DIR}, 'usr/lib/inspircd';
$config{RUNTIME_DIR} = $opt_runtime_dir // catdir $config{BASE_DIR}, 'var/run';
$config{RUNTIME_DIR} = $opt_runtime_dir // catdir $config{BASE_DIR}, 'var/run/inspircd';
$config{SCRIPT_DIR} = $opt_script_dir // catdir $config{BASE_DIR}, 'usr/share/inspircd';
} else {
$config{BASE_DIR} = rel2abs $opt_prefix // $config{BASE_DIR} // catdir $RealDir, 'run';

View File

@ -1706,7 +1706,7 @@
#<permchanneldb filename="permchannels.conf"
# listmodes="yes"
# saveperiod="5s">
#<include file="permchannels.conf">
#<include file="permchannels.conf" missingokay="yes">
#
# You may also create channels on startup by using the <permchannels> block.
#<permchannels channel="#opers" modes="isP" topic="Opers only.">

View File

@ -85,9 +85,8 @@ struct CoreExport ConnectClass
*/
unsigned int registration_timeout = 0;
/** Host mask for this line
*/
std::string host;
/** Hosts that this connect class can be used by. */
std::vector<std::string> hosts;
/** Number of seconds between pings for this line
*/
@ -152,16 +151,20 @@ struct CoreExport ConnectClass
/** Create a new connect class with no settings.
*/
ConnectClass(std::shared_ptr<ConfigTag> tag, char type, const std::string& mask);
ConnectClass(std::shared_ptr<ConfigTag> tag, char type, const std::vector<std::string>& masks);
/** Create a new connect class with inherited settings.
*/
ConnectClass(std::shared_ptr<ConfigTag> tag, char type, const std::string& mask, std::shared_ptr<ConnectClass> parent);
ConnectClass(std::shared_ptr<ConfigTag> tag, char type, const std::vector<std::string>& masks, std::shared_ptr<ConnectClass> parent);
/** Update the settings in this block to match the given block */
void Update(const std::shared_ptr<ConnectClass> newSettings);
const std::string& GetName() { return name; }
const std::string& GetHost() { return host; }
/** Retrieves the name of this connect class. */
const std::string& GetName() const { return name; }
/** Retrieves the hosts for this connect class. */
const std::vector<std::string>& GetHosts() const { return hosts; }
/** Returns the registration timeout
*/

View File

@ -38,7 +38,10 @@ enum ParseFlags
FLAG_NO_INC = 4,
// &env.FOO; is disabled.
FLAG_NO_ENV = 8
FLAG_NO_ENV = 8,
// It's okay if an include doesn't exist.
FLAG_MISSING_OKAY = 16
};
// RAII wrapper for FILE* which closes the file when it goes out of scope.
@ -370,11 +373,18 @@ void ParseStack::DoInclude(std::shared_ptr<ConfigTag> tag, int flags)
{
if (tag->getBool("noinclude", false))
flags |= FLAG_NO_INC;
if (tag->getBool("noexec", false))
flags |= FLAG_NO_EXEC;
if (tag->getBool("noenv", false))
flags |= FLAG_NO_ENV;
if (tag->getBool("missingokay", false))
flags |= FLAG_MISSING_OKAY;
else
flags &= ~FLAG_MISSING_OKAY;
if (!ParseFile(ServerInstance->Config->Paths.PrependConfig(name), flags, mandatorytag))
throw CoreException("Included");
}
@ -459,7 +469,12 @@ bool ParseStack::ParseFile(const std::string& path, int flags, const std::string
FileWrapper file((isexec ? popen(path.c_str(), "r") : fopen(path.c_str(), "r")), isexec);
if (!file)
{
if (flags & FLAG_MISSING_OKAY)
return true;
throw CoreException("Could not read \"" + path + "\" for include");
}
reading.push_back(path);
Parser p(*this, flags, file, path, mandatory_tag);

View File

@ -164,11 +164,11 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
{
case CC_ALLOW:
case CC_DENY:
oldBlocksByMask[std::make_pair(c->host, c->type)] = c;
oldBlocksByMask[std::make_pair(stdalgo::string::join(c->GetHosts()), c->type)] = c;
break;
case CC_NAMED:
oldBlocksByMask[std::make_pair(c->name, c->type)] = c;
oldBlocksByMask[std::make_pair(c->GetName(), c->type)] = c;
break;
}
}
@ -222,19 +222,14 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
std::string mask;
char type;
if (tag->readString("allow", mask, false))
if (tag->readString("allow", mask, false) && !mask.empty())
type = CC_ALLOW;
else if (tag->readString("deny", mask, false))
else if (tag->readString("deny", mask, false) && !mask.empty())
type = CC_DENY;
else if (!name.empty())
{
type = CC_NAMED;
mask = name;
}
else
{
throw CoreException("Connect class must have allow, deny, or name specified at " + tag->source.str());
}
if (name.empty())
name = "unnamed-" + ConvToStr(i);
@ -243,9 +238,14 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
throw CoreException("Two connect classes with name \"" + name + "\" defined!");
names[name] = i;
std::vector<std::string> masks;
irc::spacesepstream maskstream(mask);
for (std::string maskentry; maskstream.GetToken(maskentry); )
masks.push_back(maskentry);
auto me = parent
? std::make_shared<ConnectClass>(tag, type, mask, parent)
: std::make_shared<ConnectClass>(tag, type, mask);
? std::make_shared<ConnectClass>(tag, type, masks, parent)
: std::make_shared<ConnectClass>(tag, type, masks);
me->name = name;

View File

@ -156,7 +156,7 @@ void CommandStats::DoStats(Stats::Context& stats)
if (c->type == CC_NAMED)
param.push_back('*');
else
param.append(c->host);
param.append(stdalgo::string::join(c->GetHosts(), ','));
row.push(param).push(c->config->getString("port", "*", 1));
row.push(ConvToStr(c->GetRecvqMax())).push(ConvToStr(c->GetSendqSoftMax())).push(ConvToStr(c->GetSendqHardMax())).push(ConvToStr(c->GetCommandRate()));
@ -176,7 +176,8 @@ void CommandStats::DoStats(Stats::Context& stats)
int idx = 0;
for (const auto& c : ServerInstance->Config->Classes)
{
stats.AddRow(215, 'i', "NOMATCH", '*', c->GetHost(), (c->limit ? c->limit : SocketEngine::GetMaxFds()), idx, ServerInstance->Config->ServerName, '*');
for (const auto& host : c->GetHosts())
stats.AddRow(215, 'i', "NOMATCH", '*', host, (c->limit ? c->limit : SocketEngine::GetMaxFds()), idx, ServerInstance->Config->ServerName, '*');
stats.AddRow(218, 'Y', idx, c->GetPingTime(), '0', c->GetSendqHardMax(), ConvToStr(c->GetRecvqMax())+" "+ConvToStr(c->GetRegTimeout()));
idx++;
}

View File

@ -26,6 +26,7 @@
#include "inspircd.h"
#include "modules/ctctags.h"
// User mode +d - filter out channel messages and channel notices
class DeafMode : public ModeHandler
@ -67,7 +68,9 @@ class PrivDeafMode : public ModeHandler
}
};
class ModuleDeaf : public Module
class ModuleDeaf
: public Module
, public CTCTags::EventListener
{
private:
DeafMode deafmode;
@ -76,9 +79,51 @@ class ModuleDeaf : public Module
std::string deaf_bypasschars_service;
bool privdeafservice;
ModResult HandleChannel(User* source, Channel* target, CUList& exemptions, bool is_bypasschar, bool is_bypasschar_service)
{
for (const auto& [member, _] : target->GetUsers())
{
// Allow if the user doesn't have the mode set.
if (!member->IsModeSet(deafmode))
continue;
// Allow if the message begins with a service char and the
// user is on a services server.
if (is_bypasschar_service && member->server->IsService())
continue;
// Allow if the prefix begins with a normal char and the
// user is not on a services server.
if (is_bypasschar && !member->server->IsService())
continue;
exemptions.insert(member);
}
return MOD_RES_PASSTHRU;
}
ModResult HandleUser(User* source, User* target)
{
// Allow if the mode is not set.
if (!target->IsModeSet(privdeafmode))
return MOD_RES_PASSTHRU;
// Reject if the source is a service and privdeafservice is disabled.
if (!privdeafservice && source->server->IsService())
return MOD_RES_DENY;
// Reject if the source doesn't have the right priv.
if (!source->HasPrivPermission("users/ignore-privdeaf"))
return MOD_RES_DENY;
return MOD_RES_ALLOW;
}
public:
ModuleDeaf()
: Module(VF_VENDOR, "Adds user mode d (deaf) which prevents users from receiving channel messages.")
, CTCTags::EventListener(this)
, deafmode(this)
, privdeafmode(this)
{
@ -88,8 +133,25 @@ class ModuleDeaf : public Module
{
auto tag = ServerInstance->Config->ConfValue("deaf");
deaf_bypasschars = tag->getString("bypasschars");
deaf_bypasschars_service = tag->getString("servicebypasschars", tag->getString("bypasscharsuline"));
privdeafservice = tag->getBool("privdeafservice", tag->getBool("privdeafuline", true));
deaf_bypasschars_service = tag->getString("servicebypasschars", tag->getString("bypasscharsservice"));
privdeafservice = tag->getBool("privdeafservice", tag->getBool("privdeafservice", true));
}
ModResult OnUserPreTagMessage(User* user, const MessageTarget& target, CTCTags::TagMessageDetails& details) override
{
switch (target.type)
{
case MessageTarget::TYPE_CHANNEL:
return HandleChannel(user, target.Get<Channel>(), details.exemptions, false, false);
case MessageTarget::TYPE_USER:
return HandleUser(user, target.Get<User>());
case MessageTarget::TYPE_SERVER:
break;
}
return MOD_RES_PASSTHRU;
}
ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) override
@ -98,52 +160,23 @@ class ModuleDeaf : public Module
{
case MessageTarget::TYPE_CHANNEL:
{
Channel* chan = target.Get<Channel>();
bool is_bypasschar = (deaf_bypasschars.find(details.text[0]) != std::string::npos);
bool is_bypasschar_service = (deaf_bypasschars_service.find(details.text[0]) != std::string::npos);
// If we have no bypasschars_service in config, and this is a bypasschar (regular)
// Then it is obviously going to get through +d, no exemption list required
bool is_bypasschar = (deaf_bypasschars.find(details.text[0]) != std::string::npos);
if (deaf_bypasschars_service.empty() && is_bypasschar)
return MOD_RES_PASSTHRU;
// If it matches both bypasschar and bypasschar_service, it will get through.
bool is_bypasschar_service = (deaf_bypasschars_service.find(details.text[0]) != std::string::npos);
if (is_bypasschar && is_bypasschar_service)
return MOD_RES_PASSTHRU;
const Channel::MemberMap& ulist = chan->GetUsers();
for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
{
// not +d
if (!i->first->IsModeSet(deafmode))
continue;
bool is_a_service = i->first->server->IsService();
// matched a U-line only bypass
if (is_bypasschar_service && is_a_service)
continue;
// matched a regular bypass
if (is_bypasschar && !is_a_service)
continue;
// don't deliver message!
details.exemptions.insert(i->first);
}
break;
return HandleChannel(user, target.Get<Channel>(), details.exemptions, is_bypasschar, is_bypasschar_service);
}
case MessageTarget::TYPE_USER:
{
User* targ = target.Get<User>();
if (!targ->IsModeSet(privdeafmode))
return MOD_RES_PASSTHRU;
return HandleUser(user, target.Get<User>());
if (!privdeafservice && user->server->IsService())
return MOD_RES_DENY;
if (!user->HasPrivPermission("users/ignore-privdeaf"))
return MOD_RES_DENY;
break;
}
case MessageTarget::TYPE_SERVER:
break;
}

View File

@ -945,10 +945,11 @@ bool ModuleFilter::Tick(time_t)
return true;
}
stream << "# This file is automatically generated by the filter module. Any changes will be overwritten." << std::endl
stream
<< "# This file was automatically generated by the " << INSPIRCD_VERSION << " filter module on " << InspIRCd::TimeString(ServerInstance->Time()) << "." << std::endl
<< "# Any changes to this file will be automatically overwritten." << std::endl
<< "# If you want to convert this to a normal config file you *MUST* remove the generated=\"yes\" keys!" << std::endl
<< std::endl
<< "<config format=\"xml\">" << std::endl;
<< std::endl;
for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); ++i)
{

View File

@ -77,7 +77,9 @@ static bool WriteDatabase(PermChannel& permchanmode, Module* mod, bool save_list
return false;
}
stream << "# This file is automatically generated by m_permchannels. Any changes will be overwritten." << std::endl
stream
<< "# This file was automatically generated by the " << INSPIRCD_VERSION << " permchannels module on " << InspIRCd::TimeString(ServerInstance->Time()) << "." << std::endl
<< "# Any changes to this file will be automatically overwritten." << std::endl
<< std::endl;
const chan_hash& chans = ServerInstance->GetChans();

View File

@ -1133,12 +1133,20 @@ void LocalUser::SetClass(const std::string &explicit_name)
continue;
}
/* check if host matches.. */
if (!InspIRCd::MatchCIDR(this->GetIPString(), c->GetHost(), NULL) &&
!InspIRCd::MatchCIDR(this->GetRealHost(), c->GetHost(), NULL))
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.Log("CONNECTCLASS", LOG_DEBUG, "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(), c->GetHost().c_str());
c->GetName().c_str(), this->GetRealHost().c_str(), this->GetIPString().c_str(), hosts.c_str());
continue;
}
@ -1221,20 +1229,20 @@ const std::string& FakeUser::GetFullRealHost()
return server->GetName();
}
ConnectClass::ConnectClass(std::shared_ptr<ConfigTag> tag, char t, const std::string& mask)
ConnectClass::ConnectClass(std::shared_ptr<ConfigTag> tag, char t, const std::vector<std::string>& masks)
: config(tag)
, type(t)
, name("unnamed")
, host(mask)
, hosts(masks)
{
}
ConnectClass::ConnectClass(std::shared_ptr<ConfigTag> tag, char t, const std::string& mask, std::shared_ptr<ConnectClass> parent)
ConnectClass::ConnectClass(std::shared_ptr<ConfigTag> tag, char t, const std::vector<std::string>& masks, std::shared_ptr<ConnectClass> parent)
{
Update(parent);
name = "unnamed";
type = t;
host = mask;
hosts = masks;
// Connect classes can inherit from each other but this is problematic for modules which can't use
// ConnectClass::Update so we build a hybrid tag containing all of the values set on this class as
@ -1265,7 +1273,7 @@ void ConnectClass::Update(const std::shared_ptr<ConnectClass> src)
fakelag = src->fakelag;
name = src->name;
registration_timeout = src->registration_timeout;
host = src->host;
hosts = src->hosts;
pingtime = src->pingtime;
softsendqmax = src->softsendqmax;
hardsendqmax = src->hardsendqmax;