sub cmd_cron()
{
- if (getstatus() == 0) { goto &cmd_start(); }
+ if (getstatus() == 0) { goto &cmd_start(@_); }
- exit();
+ exit GENERIC_EXIT_UNSPECIFIED;
}
sub cmd_version()
target: $(HEADER)
$(MAKEENV) perl make/calcdep.pl
- cd $(BUILDPATH); $(MAKEENV) $(MAKE) -f real.mk $(TARGET)
+ cd "$(BUILDPATH)"; $(MAKEENV) $(MAKE) -f real.mk $(TARGET)
debug:
- @${MAKE} D=1 all
+ @${MAKE} INSPIRCD_DEBUG=1 all
debug-header:
@echo "*************************************"
@-$(INSTALL) -d -m $(INSTMODE_DIR) $(BINPATH)
@-$(INSTALL) -d -m $(INSTMODE_DIR) $(CONPATH)/examples/aliases
@-$(INSTALL) -d -m $(INSTMODE_DIR) $(CONPATH)/examples/modules
+ @-$(INSTALL) -d -m $(INSTMODE_DIR) $(MANPATH)
@-$(INSTALL) -d -m $(INSTMODE_DIR) $(MODPATH)
- [ $(BUILDPATH)/bin/ -ef $(BINPATH) ] || $(INSTALL) -m $(INSTMODE_BIN) $(BUILDPATH)/bin/inspircd $(BINPATH)
+ [ "$(BUILDPATH)/bin/" -ef $(BINPATH) ] || $(INSTALL) -m $(INSTMODE_BIN) "$(BUILDPATH)/bin/inspircd" $(BINPATH)
-@IFNDEF PURE_STATIC
+@IFNDEF INSPIRCD_STATIC
- [ $(BUILDPATH)/modules/ -ef $(MODPATH) ] || $(INSTALL) -m $(INSTMODE_LIB) $(BUILDPATH)/modules/*.so $(MODPATH)
+ [ "$(BUILDPATH)/modules/" -ef $(MODPATH) ] || $(INSTALL) -m $(INSTMODE_LIB) "$(BUILDPATH)/modules/"*.so $(MODPATH)
@ENDIF
- -$(INSTALL) -m $(INSTMODE_BIN) @STARTSCRIPT@ $(BASE) 2>/dev/null
- -$(INSTALL) -m $(INSTMODE_LIB) tools/gdbargs $(BASE)/.gdbargs 2>/dev/null
+ -$(INSTALL) -m $(INSTMODE_BIN) @CONFIGURE_DIRECTORY@/inspircd $(BASE) 2>/dev/null
+ -$(INSTALL) -m $(INSTMODE_LIB) .gdbargs $(BASE)/.gdbargs 2>/dev/null
+@IFEQ $(SYSTEM) darwin
+ -$(INSTALL) -m $(INSTMODE_BIN) @CONFIGURE_DIRECTORY@/org.inspircd.plist $(BASE) 2>/dev/null
+@ENDIF
+@IFEQ $(SYSTEM) linux
+ -$(INSTALL) -m $(INSTMODE_LIB) @CONFIGURE_DIRECTORY@/inspircd.service $(BASE) 2>/dev/null
+@ENDIF
+ -$(INSTALL) -m $(INSTMODE_LIB) @CONFIGURE_DIRECTORY@/inspircd.1 $(MANPATH) 2>/dev/null
+ -$(INSTALL) -m $(INSTMODE_LIB) @CONFIGURE_DIRECTORY@/inspircd-genssl.1 $(MANPATH) 2>/dev/null
+ -$(INSTALL) -m $(INSTMODE_BIN) tools/genssl $(BINPATH)/inspircd-genssl 2>/dev/null
-$(INSTALL) -m $(INSTMODE_LIB) docs/conf/*.example $(CONPATH)/examples
+ -$(INSTALL) -m $(INSTMODE_LIB) *.pem $(CONPATH) 2>/dev/null
-$(INSTALL) -m $(INSTMODE_LIB) docs/conf/aliases/*.example $(CONPATH)/examples/aliases
-$(INSTALL) -m $(INSTMODE_LIB) docs/conf/modules/*.example $(CONPATH)/examples/modules
@echo ""
deinstall:
-rm -f $(BINPATH)/inspircd
-rm -rf $(CONPATH)/examples
- -rm -f $(MODPATH)/cmd_*.so
+ -rm -f $(MANPATH)/inspircd.1
+ -rm -f $(MANPATH)/inspircd-genssl.1
- -rm -f $(MODPATH)/*.so
+ -rm -f $(MODPATH)/m_*.so
++ -rm -f $(MODPATH)/core_*.so
-rm -f $(BASE)/.gdbargs
+ -rm -f $(BASE)/inspircd.service
-rm -f $(BASE)/org.inspircd.plist
-squeakyclean: distclean
-
configureclean:
- rm -f .config.cache
+ rm -f .gdbargs
rm -f BSDmakefile
rm -f GNUmakefile
- rm -f include/inspircd_config.h
- rm -f include/inspircd_version.h
- rm -f inspircd
- -rm -f org.inspircd.plist
+ rm -f include/config.h
+ rm -rf @CONFIGURE_DIRECTORY@
distclean: clean configureclean
- -rm -rf $(SOURCEPATH)/run
- find $(SOURCEPATH)/src/modules -type l | xargs rm -f
+ -rm -rf "$(SOURCEPATH)/run"
+ find "$(SOURCEPATH)/src/modules" -type l | xargs rm -f
help:
@echo 'InspIRCd Makefile'
HideBans = security->getBool("hidebans");
HideWhoisServer = security->getString("hidewhois");
HideKillsServer = security->getString("hidekills");
+ HideULineKills = security->getBool("hideulinekills");
RestrictBannedUsers = security->getBool("restrictbannedusers", true);
GenericOper = security->getBool("genericoper");
- NoUserDns = ConfValue("performance")->getBool("nouserdns");
SyntaxHints = options->getBool("syntaxhints");
- CycleHosts = options->getBool("cyclehosts");
CycleHostsFromUser = options->getBool("cyclehostsfromuser");
- UndernetMsgPrefix = options->getBool("ircumsgprefix");
FullHostInTopic = options->getBool("hostintopic");
- MaxTargets = security->getInt("maxtargets", 20);
- DefaultModes = options->getString("defaultmodes", "nt");
+ MaxTargets = security->getInt("maxtargets", 20, 1, 31);
+ DefaultModes = options->getString("defaultmodes", "not");
PID = ConfValue("pid")->getString("file");
- WhoWasGroupSize = ConfValue("whowas")->getInt("groupsize");
- WhoWasMaxGroups = ConfValue("whowas")->getInt("maxgroups");
- WhoWasMaxKeep = ServerInstance->Duration(ConfValue("whowas")->getString("maxkeep"));
MaxChans = ConfValue("channels")->getInt("users", 20);
- OperMaxChans = ConfValue("channels")->getInt("opers", 60);
+ OperMaxChans = ConfValue("channels")->getInt("opers");
c_ipv4_range = ConfValue("cidr")->getInt("ipv4clone", 32);
c_ipv6_range = ConfValue("cidr")->getInt("ipv6clone", 128);
- Limits.NickMax = ConfValue("limits")->getInt("maxnick", 32);
- Limits.ChanMax = ConfValue("limits")->getInt("maxchan", 64);
- Limits.MaxModes = ConfValue("limits")->getInt("maxmodes", 20);
- Limits.IdentMax = ConfValue("limits")->getInt("maxident", 11);
- Limits.MaxQuit = ConfValue("limits")->getInt("maxquit", 255);
- Limits.MaxTopic = ConfValue("limits")->getInt("maxtopic", 307);
- Limits.MaxKick = ConfValue("limits")->getInt("maxkick", 255);
- Limits.MaxGecos = ConfValue("limits")->getInt("maxgecos", 128);
- Limits.MaxAway = ConfValue("limits")->getInt("maxaway", 200);
- InvBypassModes = options->getBool("invitebypassmodes", true);
+ Limits = ServerLimits(ConfValue("limits"));
+ Paths.Config = ConfValue("path")->getString("configdir", INSPIRCD_CONFIG_PATH);
+ Paths.Data = ConfValue("path")->getString("datadir", INSPIRCD_DATA_PATH);
+ Paths.Log = ConfValue("path")->getString("logdir", INSPIRCD_LOG_PATH);
+ Paths.Module = ConfValue("path")->getString("moduledir", INSPIRCD_MODULE_PATH);
NoSnoticeStack = options->getBool("nosnoticestack", false);
- WelcomeNotice = options->getBool("welcomenotice", true);
-
- range(SoftLimit, 10, ServerInstance->SE->GetMaxFds(), ServerInstance->SE->GetMaxFds(), "<performance:softlimit>");
- if (ConfValue("performance")->getBool("limitsomaxconn", true))
- range(MaxConn, 0, SOMAXCONN, SOMAXCONN, "<performance:somaxconn>");
- range(MaxTargets, 1, 31, 20, "<security:maxtargets>");
- range(NetBufferSize, 1024, 65534, 10240, "<performance:netbuffersize>");
- range(WhoWasGroupSize, 0, 10000, 10, "<whowas:groupsize>");
- range(WhoWasMaxGroups, 0, 1000000, 10240, "<whowas:maxgroups>");
- range(WhoWasMaxKeep, 3600, INT_MAX, 3600, "<whowas:maxkeep>");
- ValidIP(DNSServer, "<dns:server>");
+ if (Network.find(' ') != std::string::npos)
+ throw CoreException(Network + " is not a valid network name. A network name must not contain spaces.");
std::string defbind = options->getString("defaultbind");
if (assign(defbind) == "ipv4")
// write once here, to try it out and make sure its ok
if (valid)
- ServerInstance->WritePID(this->PID);
+ ServerInstance->WritePID(this->PID, !old);
+ ConfigTagList binds = ConfTags("bind");
+ if (binds.first == binds.second)
+ errstr << "Possible configuration error: you have not defined any <bind> blocks." << std::endl
+ << "You will need to do this if you want clients to be able to connect!" << std::endl;
+
if (old && valid)
{
// On first run, ports are bound later on
--- /dev/null
- /* Work around mIRC suckyness. YOU SUCK, KHALED! */
- if (parameters.size() == 1)
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+/** Handle /LIST.
+ */
+class CommandList : public Command
+{
+ ChanModeReference secretmode;
+ ChanModeReference privatemode;
+
+ public:
+ /** Constructor for list.
+ */
+ CommandList(Module* parent)
+ : Command(parent,"LIST", 0, 0)
+ , secretmode(creator, "secret")
+ , privatemode(creator, "private")
+ {
+ Penalty = 5;
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+};
+
+
+/** Handle /LIST
+ */
+CmdResult CommandList::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ int minusers = 0, maxusers = 0;
+
+ user->WriteNumeric(RPL_LISTSTART, "Channel", "Users Name");
+
++ if ((parameters.size() == 1) && (!parameters[0].empty()))
+ {
+ if (parameters[0][0] == '<')
+ {
+ maxusers = atoi((parameters[0].c_str())+1);
+ }
+ else if (parameters[0][0] == '>')
+ {
+ minusers = atoi((parameters[0].c_str())+1);
+ }
+ }
+
+ const bool has_privs = user->HasPrivPermission("channels/auspex");
+ const bool match_name_topic = ((!parameters.empty()) && (!parameters[0].empty()) && (parameters[0][0] != '<') && (parameters[0][0] != '>'));
+
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
+ {
+ Channel* const chan = i->second;
+
+ // attempt to match a glob pattern
+ long users = chan->GetUserCounter();
+
+ bool too_few = (minusers && (users <= minusers));
+ bool too_many = (maxusers && (users >= maxusers));
+
+ if (too_many || too_few)
+ continue;
+
+ if (match_name_topic)
+ {
+ if (!InspIRCd::Match(chan->name, parameters[0]) && !InspIRCd::Match(chan->topic, parameters[0]))
+ continue;
+ }
+
+ // if the channel is not private/secret, OR the user is on the channel anyway
+ bool n = (has_privs || chan->HasUser(user));
+
+ // If we're not in the channel and +s is set on it, we want to ignore it
+ if ((n) || (!chan->IsModeSet(secretmode)))
+ {
+ if ((!n) && (chan->IsModeSet(privatemode)))
+ {
+ // Channel is private (+p) and user is outside/not privileged
+ user->WriteNumeric(RPL_LIST, '*', users, "");
+ }
+ else
+ {
+ /* User is in the channel/privileged, channel is not +s */
+ user->WriteNumeric(RPL_LIST, chan->name, users, InspIRCd::Format("[+%s] %s", chan->ChanModes(n), chan->topic.c_str()));
+ }
+ }
+ }
+ user->WriteNumeric(RPL_LISTEND, "End of channel list.");
+
+ return CMD_SUCCESS;
+}
+
+
+COMMAND_INIT(CommandList)
--- /dev/null
- if (!user->server->IsULine())
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_oper.h"
+
+CommandKill::CommandKill(Module* parent)
+ : Command(parent, "KILL", 2, 2)
+{
+ flags_needed = 'o';
+ syntax = "<nickname> <reason>";
+ TRANSLATE2(TR_CUSTOM, TR_CUSTOM);
+}
+
+
+/** Handle /KILL
+ */
+CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ /* Allow comma seperated lists of users for /KILL (thanks w00t) */
+ if (CommandParser::LoopCall(user, this, parameters, 0))
+ {
+ // If we got a colon delimited list of nicks then the handler ran for each nick,
+ // and KILL commands were broadcast for remote targets.
+ return CMD_FAILURE;
+ }
+
+ User *u = ServerInstance->FindNick(parameters[0]);
+ if (u)
+ {
+ /*
+ * Here, we need to decide how to munge kill messages. Whether to hide killer, what to show opers, etc.
+ * We only do this when the command is being issued LOCALLY, for remote KILL, we just copy the message we got.
+ *
+ * This conditional is so that we only append the "Killed (" prefix ONCE. If killer is remote, then the kill
+ * just gets processed and passed on, otherwise, if they are local, it gets prefixed. Makes sense :-) -- w00t
+ */
+
+ if (IS_LOCAL(user))
+ {
+ /*
+ * Moved this event inside the IS_LOCAL check also, we don't want half the network killing a user
+ * and the other half not. This would be a bad thing. ;p -- w00t
+ */
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnKill, MOD_RESULT, (user, u, parameters[1]));
+
+ if (MOD_RESULT == MOD_RES_DENY)
+ return CMD_FAILURE;
+
+ killreason = "Killed (";
+ if (!ServerInstance->Config->HideKillsServer.empty())
+ {
+ // hidekills is on, use it
+ killreason += ServerInstance->Config->HideKillsServer;
+ }
+ else
+ {
+ // hidekills is off, do nothing
+ killreason += user->nick;
+ }
+
+ killreason += " (" + parameters[1] + "))";
+ }
+ else
+ {
+ /* Leave it alone, remote server has already formatted it */
+ killreason.assign(parameters[1], 0, ServerInstance->Config->Limits.MaxQuit);
+ }
+
+ /*
+ * Now we need to decide whether or not to send a local or remote snotice. Currently this checking is a little flawed.
+ * No time to fix it right now, so left a note. -- w00t
+ */
+ if (!IS_LOCAL(u))
+ {
+ // remote kill
- if (!user->server->IsULine())
++ if ((!ServerInstance->Config->HideULineKills) || (!user->server->IsULine()))
+ ServerInstance->SNO->WriteToSnoMask('K', "Remote kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
+ this->lastuuid = u->uuid;
+ }
+ else
+ {
+ // local kill
+ /*
+ * XXX - this isn't entirely correct, servers A - B - C, oper on A, client on C. Oper kills client, A and B will get remote kill
+ * snotices, C will get a local kill snotice. this isn't accurate, and needs fixing at some stage. -- w00t
+ */
++ if ((!ServerInstance->Config->HideULineKills) || (!user->server->IsULine()))
+ {
+ if (IS_LOCAL(user))
+ ServerInstance->SNO->WriteGlobalSno('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
+ else
+ ServerInstance->SNO->WriteToSnoMask('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
+ }
+
+ ServerInstance->Logs->Log("KILL", LOG_DEFAULT, "LOCAL KILL: %s :%s!%s!%s (%s)", u->nick.c_str(), ServerInstance->Config->ServerName.c_str(), user->dhost.c_str(), user->nick.c_str(), parameters[1].c_str());
+
+ u->Write(":%s KILL %s :%s!%s!%s (%s)", ServerInstance->Config->HideKillsServer.empty() ? user->GetFullHost().c_str() : ServerInstance->Config->HideKillsServer.c_str(),
+ u->nick.c_str(),
+ ServerInstance->Config->ServerName.c_str(),
+ ServerInstance->Config->HideKillsServer.empty() ? user->dhost.c_str() : ServerInstance->Config->HideKillsServer.c_str(),
+ ServerInstance->Config->HideKillsServer.empty() ? user->nick.c_str() : ServerInstance->Config->HideKillsServer.c_str(),
+ parameters[1].c_str());
+
+ this->lastuuid.clear();
+ }
+
+ // send the quit out
+ ServerInstance->Users->QuitUser(u, killreason);
+ }
+ else
+ {
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
+ return CMD_FAILURE;
+ }
+
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandKill::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ // FindNick() doesn't work here because we quit the target user in Handle() which
+ // removes it from the nicklist, so we check lastuuid: if it's empty then this KILL
+ // was for a local user, otherwise it contains the uuid of the user who was killed.
+ if (lastuuid.empty())
+ return ROUTE_LOCALONLY;
+ return ROUTE_BROADCAST;
+}
+
+
+void CommandKill::EncodeParameter(std::string& param, int index)
+{
+ // Manually translate the nick -> uuid (see above), and also the reason (params[1])
+ // because we decorate it if the oper is local and want remote servers to see the
+ // decorated reason not the original.
+ param = ((index == 0) ? lastuuid : killreason);
+}
--- /dev/null
- CommandStats ( Module* parent) : Command(parent,"STATS",1,2) { syntax = "<stats-symbol> [<servername>]"; }
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "xline.h"
+
+#ifdef _WIN32
+#include <psapi.h>
+#pragma comment(lib, "psapi.lib") // For GetProcessMemoryInfo()
+#endif
+
+/** Handle /STATS.
+ */
+class CommandStats : public Command
+{
+ void DoStats(Stats::Context& stats);
+ public:
+ /** Constructor for stats.
+ */
++ CommandStats ( Module* parent) : Command(parent,"STATS",1,2) { allow_empty_last_param = false; syntax = "<stats-symbol> [<servername>]"; }
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ {
+ if ((parameters.size() > 1) && (parameters[1].find('.') != std::string::npos))
+ return ROUTE_UNICAST(parameters[1]);
+ return ROUTE_LOCALONLY;
+ }
+};
+
+static void GenerateStatsLl(Stats::Context& stats)
+{
+ stats.AddRow(211, InspIRCd::Format("nick[ident@%s] sendq cmds_out bytes_out cmds_in bytes_in time_open", (stats.GetSymbol() == 'l' ? "host" : "ip")));
+
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ LocalUser* u = *i;
+ stats.AddRow(211, u->nick+"["+u->ident+"@"+(stats.GetSymbol() == 'l' ? u->dhost : u->GetIPString())+"] "+ConvToStr(u->eh.getSendQSize())+" "+ConvToStr(u->cmds_out)+" "+ConvToStr(u->bytes_out)+" "+ConvToStr(u->cmds_in)+" "+ConvToStr(u->bytes_in)+" "+ConvToStr(ServerInstance->Time() - u->signon));
+ }
+}
+
+void CommandStats::DoStats(Stats::Context& stats)
+{
+ User* const user = stats.GetSource();
+ const char statschar = stats.GetSymbol();
+
+ bool isPublic = ServerInstance->Config->UserStats.find(statschar) != std::string::npos;
+ bool isRemoteOper = IS_REMOTE(user) && (user->IsOper());
+ bool isLocalOperWithPrivs = IS_LOCAL(user) && user->HasPrivPermission("servers/auspex");
+
+ if (!isPublic && !isRemoteOper && !isLocalOperWithPrivs)
+ {
+ ServerInstance->SNO->WriteToSnoMask('t',
+ "%s '%c' denied for %s (%s@%s)",
+ (IS_LOCAL(user) ? "Stats" : "Remote stats"),
+ statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
+ stats.AddRow(481, (std::string("Permission Denied - STATS ") + statschar + " requires the servers/auspex priv."));
+ return;
+ }
+
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnStats, MOD_RESULT, (stats));
+ if (MOD_RESULT == MOD_RES_DENY)
+ {
+ stats.AddRow(219, statschar, "End of /STATS report");
+ ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",
+ (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
+ return;
+ }
+
+ switch (statschar)
+ {
+ /* stats p (show listening ports) */
+ case 'p':
+ {
+ for (std::vector<ListenSocket*>::const_iterator i = ServerInstance->ports.begin(); i != ServerInstance->ports.end(); ++i)
+ {
+ ListenSocket* ls = *i;
+ std::string ip = ls->bind_addr;
+ if (ip.empty())
+ ip.assign("*");
+ else if (ip.find_first_of(':') != std::string::npos)
+ {
+ ip.insert(ip.begin(), '[');
+ ip.insert(ip.end(), ']');
+ }
+ std::string type = ls->bind_tag->getString("type", "clients");
+ std::string hook = ls->bind_tag->getString("ssl", "plaintext");
+
+ stats.AddRow(249, ip + ":"+ConvToStr(ls->bind_port) + " (" + type + ", " + hook + ")");
+ }
+ }
+ break;
+
+ /* These stats symbols must be handled by a linking module */
+ case 'n':
+ case 'c':
+ break;
+
+ case 'i':
+ {
+ for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i)
+ {
+ ConnectClass* c = *i;
+ Stats::Row row(215);
+ row.push("I").push(c->name);
+
+ std::string param;
+ if (c->type == CC_ALLOW)
+ param.push_back('+');
+ if (c->type == CC_DENY)
+ param.push_back('-');
+
+ if (c->type == CC_NAMED)
+ param.push_back('*');
+ else
+ param.append(c->host);
+
+ row.push(param).push(c->config->getString("port", "*"));
+ row.push(ConvToStr(c->GetRecvqMax())).push(ConvToStr(c->GetSendqSoftMax())).push(ConvToStr(c->GetSendqHardMax())).push(ConvToStr(c->GetCommandRate()));
+
+ param = ConvToStr(c->GetPenaltyThreshold());
+ if (c->fakelag)
+ param.push_back('*');
+ row.push(param);
+
+ stats.AddRow(row);
+ }
+ }
+ break;
+
+ case 'Y':
+ {
+ int idx = 0;
+ for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+ {
+ ConnectClass* c = *i;
+ stats.AddRow(215, 'i', "NOMATCH", '*', c->GetHost(), (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++;
+ }
+ }
+ break;
+
+ case 'P':
+ {
+ unsigned int idx = 0;
+ const UserManager::OperList& opers = ServerInstance->Users->all_opers;
+ for (UserManager::OperList::const_iterator i = opers.begin(); i != opers.end(); ++i)
+ {
+ User* oper = *i;
+ if (!oper->server->IsULine())
+ {
+ LocalUser* lu = IS_LOCAL(oper);
+ stats.AddRow(249, oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
+ (lu ? ConvToStr(ServerInstance->Time() - lu->idle_lastmsg) + " secs" : "unavailable"));
+ idx++;
+ }
+ }
+ stats.AddRow(249, ConvToStr(idx)+" OPER(s)");
+ }
+ break;
+
+ case 'k':
+ ServerInstance->XLines->InvokeStats("K",216,stats);
+ break;
+ case 'g':
+ ServerInstance->XLines->InvokeStats("G",223,stats);
+ break;
+ case 'q':
+ ServerInstance->XLines->InvokeStats("Q",217,stats);
+ break;
+ case 'Z':
+ ServerInstance->XLines->InvokeStats("Z",223,stats);
+ break;
+ case 'e':
+ ServerInstance->XLines->InvokeStats("E",223,stats);
+ break;
+ case 'E':
+ {
+ const SocketEngine::Statistics& sestats = SocketEngine::GetStats();
+ stats.AddRow(249, "Total events: "+ConvToStr(sestats.TotalEvents));
+ stats.AddRow(249, "Read events: "+ConvToStr(sestats.ReadEvents));
+ stats.AddRow(249, "Write events: "+ConvToStr(sestats.WriteEvents));
+ stats.AddRow(249, "Error events: "+ConvToStr(sestats.ErrorEvents));
+ break;
+ }
+
+ /* stats m (list number of times each command has been used, plus bytecount) */
+ case 'm':
+ {
+ const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
+ for (CommandParser::CommandMap::const_iterator i = commands.begin(); i != commands.end(); ++i)
+ {
+ if (i->second->use_count)
+ {
+ /* RPL_STATSCOMMANDS */
+ stats.AddRow(212, i->second->name, i->second->use_count);
+ }
+ }
+ }
+ break;
+
+ /* stats z (debug and memory info) */
+ case 'z':
+ {
+ stats.AddRow(249, "Users: "+ConvToStr(ServerInstance->Users->GetUsers().size()));
+ stats.AddRow(249, "Channels: "+ConvToStr(ServerInstance->GetChans().size()));
+ stats.AddRow(249, "Commands: "+ConvToStr(ServerInstance->Parser.GetCommands().size()));
+
+ float kbitpersec_in, kbitpersec_out, kbitpersec_total;
+ char kbitpersec_in_s[30], kbitpersec_out_s[30], kbitpersec_total_s[30];
+
+ SocketEngine::GetStats().GetBandwidth(kbitpersec_in, kbitpersec_out, kbitpersec_total);
+
+ snprintf(kbitpersec_total_s, 30, "%03.5f", kbitpersec_total);
+ snprintf(kbitpersec_out_s, 30, "%03.5f", kbitpersec_out);
+ snprintf(kbitpersec_in_s, 30, "%03.5f", kbitpersec_in);
+
+ stats.AddRow(249, "Bandwidth total: "+ConvToStr(kbitpersec_total_s)+" kilobits/sec");
+ stats.AddRow(249, "Bandwidth out: "+ConvToStr(kbitpersec_out_s)+" kilobits/sec");
+ stats.AddRow(249, "Bandwidth in: "+ConvToStr(kbitpersec_in_s)+" kilobits/sec");
+
+#ifndef _WIN32
+ /* Moved this down here so all the not-windows stuff (look w00tie, I didn't say win32!) is in one ifndef.
+ * Also cuts out some identical code in both branches of the ifndef. -- Om
+ */
+ rusage R;
+
+ /* Not sure why we were doing '0' with a RUSAGE_SELF comment rather than just using RUSAGE_SELF -- Om */
+ if (!getrusage(RUSAGE_SELF,&R)) /* RUSAGE_SELF */
+ {
+ stats.AddRow(249, "Total allocation: "+ConvToStr(R.ru_maxrss)+"K");
+ stats.AddRow(249, "Signals: "+ConvToStr(R.ru_nsignals));
+ stats.AddRow(249, "Page faults: "+ConvToStr(R.ru_majflt));
+ stats.AddRow(249, "Swaps: "+ConvToStr(R.ru_nswap));
+ stats.AddRow(249, "Context Switches: Voluntary; "+ConvToStr(R.ru_nvcsw)+" Involuntary; "+ConvToStr(R.ru_nivcsw));
+
+ char percent[30];
+
+ float n_elapsed = (ServerInstance->Time() - ServerInstance->stats.LastSampled.tv_sec) * 1000000
+ + (ServerInstance->Time_ns() - ServerInstance->stats.LastSampled.tv_nsec) / 1000;
+ float n_eaten = ((R.ru_utime.tv_sec - ServerInstance->stats.LastCPU.tv_sec) * 1000000 + R.ru_utime.tv_usec - ServerInstance->stats.LastCPU.tv_usec);
+ float per = (n_eaten / n_elapsed) * 100;
+
+ snprintf(percent, 30, "%03.5f%%", per);
+ stats.AddRow(249, std::string("CPU Use (now): ")+percent);
+
+ n_elapsed = ServerInstance->Time() - ServerInstance->startup_time;
+ n_eaten = (float)R.ru_utime.tv_sec + R.ru_utime.tv_usec / 100000.0;
+ per = (n_eaten / n_elapsed) * 100;
+ snprintf(percent, 30, "%03.5f%%", per);
+ stats.AddRow(249, std::string("CPU Use (total): ")+percent);
+ }
+#else
+ PROCESS_MEMORY_COUNTERS MemCounters;
+ if (GetProcessMemoryInfo(GetCurrentProcess(), &MemCounters, sizeof(MemCounters)))
+ {
+ stats.AddRow(249, "Total allocation: "+ConvToStr((MemCounters.WorkingSetSize + MemCounters.PagefileUsage) / 1024)+"K");
+ stats.AddRow(249, "Pagefile usage: "+ConvToStr(MemCounters.PagefileUsage / 1024)+"K");
+ stats.AddRow(249, "Page faults: "+ConvToStr(MemCounters.PageFaultCount));
+ }
+
+ FILETIME CreationTime;
+ FILETIME ExitTime;
+ FILETIME KernelTime;
+ FILETIME UserTime;
+ LARGE_INTEGER ThisSample;
+ if(GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime) &&
+ QueryPerformanceCounter(&ThisSample))
+ {
+ KernelTime.dwHighDateTime += UserTime.dwHighDateTime;
+ KernelTime.dwLowDateTime += UserTime.dwLowDateTime;
+ double n_eaten = (double)( ( (uint64_t)(KernelTime.dwHighDateTime - ServerInstance->stats.LastCPU.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime - ServerInstance->stats.LastCPU.dwLowDateTime) )/100000;
+ double n_elapsed = (double)(ThisSample.QuadPart - ServerInstance->stats.LastSampled.QuadPart) / ServerInstance->stats.QPFrequency.QuadPart;
+ double per = (n_eaten/n_elapsed);
+
+ char percent[30];
+
+ snprintf(percent, 30, "%03.5f%%", per);
+ stats.AddRow(249, std::string("CPU Use (now): ")+percent);
+
+ n_elapsed = ServerInstance->Time() - ServerInstance->startup_time;
+ n_eaten = (double)(( (uint64_t)(KernelTime.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime))/100000;
+ per = (n_eaten / n_elapsed);
+ snprintf(percent, 30, "%03.5f%%", per);
+ stats.AddRow(249, std::string("CPU Use (total): ")+percent));
+ }
+#endif
+ }
+ break;
+
+ case 'T':
+ {
+ stats.AddRow(249, "accepts "+ConvToStr(ServerInstance->stats.Accept)+" refused "+ConvToStr(ServerInstance->stats.Refused));
+ stats.AddRow(249, "unknown commands "+ConvToStr(ServerInstance->stats.Unknown));
+ stats.AddRow(249, "nick collisions "+ConvToStr(ServerInstance->stats.Collisions));
+ stats.AddRow(249, "dns requests "+ConvToStr(ServerInstance->stats.DnsGood+ServerInstance->stats.DnsBad)+" succeeded "+ConvToStr(ServerInstance->stats.DnsGood)+" failed "+ConvToStr(ServerInstance->stats.DnsBad));
+ stats.AddRow(249, "connection count "+ConvToStr(ServerInstance->stats.Connects));
+ stats.AddRow(249, InspIRCd::Format("bytes sent %5.2fK recv %5.2fK",
+ ServerInstance->stats.Sent / 1024.0, ServerInstance->stats.Recv / 1024.0));
+ }
+ break;
+
+ /* stats o */
+ case 'o':
+ {
+ ConfigTagList tags = ServerInstance->Config->ConfTags("oper");
+ for(ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ ConfigTag* tag = i->second;
+ stats.AddRow(243, 'O', tag->getString("host"), '*', tag->getString("name"), tag->getString("type"), '0');
+ }
+ }
+ break;
+ case 'O':
+ {
+ for (ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->OperTypes.begin(); i != ServerInstance->Config->OperTypes.end(); ++i)
+ {
+ OperInfo* tag = i->second;
+ tag->init();
+ std::string umodes;
+ std::string cmodes;
+ for(char c='A'; c <= 'z'; c++)
+ {
+ ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_USER);
+ if (mh && mh->NeedsOper() && tag->AllowedUserModes[c - 'A'])
+ umodes.push_back(c);
+ mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL);
+ if (mh && mh->NeedsOper() && tag->AllowedChanModes[c - 'A'])
+ cmodes.push_back(c);
+ }
+ stats.AddRow(243, 'O', tag->name, umodes, cmodes);
+ }
+ }
+ break;
+
+ /* stats l (show user I/O stats) */
+ case 'l':
+ /* stats L (show user I/O stats with IP addresses) */
+ case 'L':
+ GenerateStatsLl(stats);
+ break;
+
+ /* stats u (show server uptime) */
+ case 'u':
+ {
+ unsigned int up = static_cast<unsigned int>(ServerInstance->Time() - ServerInstance->startup_time);
+ stats.AddRow(242, InspIRCd::Format("Server up %u days, %.2u:%.2u:%.2u",
+ up / 86400, (up / 3600) % 24, (up / 60) % 60, up % 60));
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ stats.AddRow(219, statschar, "End of /STATS report");
+ ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",
+ (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
+ return;
+}
+
+CmdResult CommandStats::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ if (parameters.size() > 1 && parameters[1] != ServerInstance->Config->ServerName)
+ {
+ // Give extra penalty if a non-oper does /STATS <remoteserver>
+ LocalUser* localuser = IS_LOCAL(user);
+ if ((localuser) && (!user->IsOper()))
+ localuser->CommandFloodPenalty += 2000;
+ return CMD_SUCCESS;
+ }
+ Stats::Context stats(user, parameters[0][0]);
+ DoStats(stats);
+ const std::vector<Stats::Row>& rows = stats.GetRows();
+ for (std::vector<Stats::Row>::const_iterator i = rows.begin(); i != rows.end(); ++i)
+ {
+ const Stats::Row& row = *i;
+ user->WriteRemoteNumeric(row);
+ }
+
+ return CMD_SUCCESS;
+}
+
+COMMAND_INIT(CommandStats)
outfile.close();
}
else
- {\r
- if (exitonfail)\r
+ {
- std::cout << "Failed to write PID-file '" << fname << "', exiting." << std::endl;
- this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to write PID-file '%s', exiting.",fname.c_str());
- Exit(EXIT_STATUS_PID);
++ if (exitonfail)
+ std::cout << "Failed to write PID-file '" << fname << "', exiting." << std::endl;
- this->Logs->Log("STARTUP",DEFAULT,"Failed to write PID-file '%s'%s",fname.c_str(), (exitonfail ? ", exiting." : ""));\r
++ this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to write PID-file '%s'%s", fname.c_str(), (exitonfail ? ", exiting." : ""));
+ if (exitonfail)
- Exit(EXIT_STATUS_PID);\r
++ Exit(EXIT_STATUS_PID);
}
#endif
}
return type1 + "," + type2 + "," + type3 + "," + type4;
}
- bool operator()(ModeHandler* lhs, ModeHandler* rhs)
+ struct PrefixModeSorter
+ {
++ bool operator()(PrefixMode* lhs, PrefixMode* rhs)
+ {
+ return lhs->GetPrefixRank() < rhs->GetPrefixRank();
+ }
+ };
+
std::string ModeParser::BuildPrefixes(bool lettersAndModes)
{
std::string mletters;
std::string mprefixes;
- insp::flat_map<int, std::pair<char, char> > prefixes;
- std::vector<ModeHandler*> prefixes;
++ std::vector<PrefixMode*> prefixes;
- for (unsigned char mode = 'A'; mode <= 'z'; mode++)
+ const PrefixModeList& list = GetPrefixModes();
+ for (PrefixModeList::const_iterator i = list.begin(); i != list.end(); ++i)
{
- unsigned char pos = (mode-65) | MASK_CHANNEL;
-
- if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix()))
- {
- prefixes.push_back(modehandlers[pos]);
- }
+ PrefixMode* pm = *i;
+ if (pm->GetPrefix())
- prefixes[pm->GetPrefixRank()] = std::make_pair(pm->GetPrefix(), pm->GetModeChar());
++ prefixes.push_back(pm);
}
- for (insp::flat_map<int, std::pair<char, char> >::reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); ++n)
+ std::sort(prefixes.begin(), prefixes.end(), PrefixModeSorter());
- for (std::vector<ModeHandler*>::const_reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); ++n)
++ for (std::vector<PrefixMode*>::const_reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); ++n)
{
- mletters = mletters + n->second.first;
- mprefixes = mprefixes + n->second.second;
+ mletters += (*n)->GetPrefix();
+ mprefixes += (*n)->GetModeChar();
}
return lettersAndModes ? "(" + mprefixes + ")" + mletters : mletters;
gnutls_x509_crt_deinit(cert);
}
- void OnEvent(Event& ev)
+ // Returns 1 if application I/O should proceed, 0 if it must wait for the underlying protocol to progress, -1 on fatal error
+ int PrepareIO(StreamSocket* sock)
+ {
+ if (status == ISSL_HANDSHAKEN)
+ return 1;
+ else if (status == ISSL_HANDSHAKING)
+ {
+ // The handshake isn't finished, try to finish it
+ return Handshake(sock);
+ }
+
+ CloseSession();
+ sock->SetError("No SSL session");
+ return -1;
+ }
+
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+ int FlushBuffer(StreamSocket* sock)
+ {
+ // If GnuTLS has some data buffered, write it
+ if (gbuffersize)
+ return HandleWriteRet(sock, gnutls_record_uncork(this->sess, 0));
+ return 1;
+ }
+#endif
+
+ int HandleWriteRet(StreamSocket* sock, int ret)
+ {
+ if (ret > 0)
+ {
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+ gbuffersize -= ret;
+ if (gbuffersize)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WANT_SINGLE_WRITE);
+ return 0;
+ }
+#endif
+ return ret;
+ }
+ else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED || ret == 0)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WANT_SINGLE_WRITE);
+ return 0;
+ }
+ else // (ret < 0)
+ {
+ sock->SetError(gnutls_strerror(ret));
+ CloseSession();
+ return -1;
+ }
+ }
+
+ static const char* UnknownIfNULL(const char* str)
+ {
+ return str ? str : "UNKNOWN";
+ }
+
+ static ssize_t gnutls_pull_wrapper(gnutls_transport_ptr_t session_wrap, void* buffer, size_t size)
+ {
+ StreamSocket* sock = reinterpret_cast<StreamSocket*>(session_wrap);
+#ifdef _WIN32
+ GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetModHook(thismod));
+#endif
+
+ if (sock->GetEventMask() & FD_READ_WILL_BLOCK)
+ {
+#ifdef _WIN32
+ gnutls_transport_set_errno(session->sess, EAGAIN);
+#else
+ errno = EAGAIN;
+#endif
+ return -1;
+ }
+
+ int rv = SocketEngine::Recv(sock, reinterpret_cast<char *>(buffer), size, 0);
+
+#ifdef _WIN32
+ if (rv < 0)
+ {
+ /* Windows doesn't use errno, but gnutls does, so check SocketEngine::IgnoreError()
+ * and then set errno appropriately.
+ * The gnutls library may also have a different errno variable than us, see
+ * gnutls_transport_set_errno(3).
+ */
+ gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
+ }
+#endif
+
+ if (rv < (int)size)
+ SocketEngine::ChangeEventMask(sock, FD_READ_WILL_BLOCK);
+ return rv;
+ }
+
+#ifdef INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
+ static ssize_t VectorPush(gnutls_transport_ptr_t transportptr, const giovec_t* iov, int iovcnt)
+ {
+ StreamSocket* sock = reinterpret_cast<StreamSocket*>(transportptr);
+#ifdef _WIN32
+ GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetModHook(thismod));
+#endif
+
+ if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
+ {
+#ifdef _WIN32
+ gnutls_transport_set_errno(session->sess, EAGAIN);
+#else
+ errno = EAGAIN;
+#endif
+ return -1;
+ }
+
+ // Cast the giovec_t to iovec not to IOVector so the correct function is called on Windows
+ int ret = SocketEngine::WriteV(sock, reinterpret_cast<const iovec*>(iov), iovcnt);
+#ifdef _WIN32
+ // See the function above for more info about the usage of gnutls_transport_set_errno() on Windows
+ if (ret < 0)
+ gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
+#endif
+
+ int size = 0;
+ for (int i = 0; i < iovcnt; i++)
+ size += iov[i].iov_len;
+
+ if (ret < size)
+ SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
+ return ret;
+ }
+
+#else // INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
+ static ssize_t gnutls_push_wrapper(gnutls_transport_ptr_t session_wrap, const void* buffer, size_t size)
{
- if (starttls.enabled)
- capHandler.HandleEvent(ev);
+ StreamSocket* sock = reinterpret_cast<StreamSocket*>(session_wrap);
+#ifdef _WIN32
+ GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetModHook(thismod));
+#endif
+
+ if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
+ {
+#ifdef _WIN32
+ gnutls_transport_set_errno(session->sess, EAGAIN);
+#else
+ errno = EAGAIN;
+#endif
+ return -1;
+ }
+
+ int rv = SocketEngine::Send(sock, reinterpret_cast<const char *>(buffer), size, 0);
+
+#ifdef _WIN32
+ if (rv < 0)
+ {
+ /* Windows doesn't use errno, but gnutls does, so check SocketEngine::IgnoreError()
+ * and then set errno appropriately.
+ * The gnutls library may also have a different errno variable than us, see
+ * gnutls_transport_set_errno(3).
+ */
+ gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
+ }
+#endif
+
+ if (rv < (int)size)
+ SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
+ return rv;
+ }
+#endif // INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
+
+ public:
+ GnuTLSIOHook(IOHookProvider* hookprov, StreamSocket* sock, inspircd_gnutls_session_init_flags_t flags, const reference<GnuTLS::Profile>& sslprofile)
+ : SSLIOHook(hookprov)
+ , sess(NULL)
+ , status(ISSL_NONE)
+ , profile(sslprofile)
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+ , gbuffersize(0)
+#endif
+ {
+ gnutls_init(&sess, flags);
+ gnutls_transport_set_ptr(sess, reinterpret_cast<gnutls_transport_ptr_t>(sock));
+#ifdef INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
+ gnutls_transport_set_vec_push_function(sess, VectorPush);
+#else
+ gnutls_transport_set_push_function(sess, gnutls_push_wrapper);
+#endif
+ gnutls_transport_set_pull_function(sess, gnutls_pull_wrapper);
+ profile->SetupSession(sess);
+
+ sock->AddIOHook(this);
+ Handshake(sock);
+ }
+
+ void OnStreamSocketClose(StreamSocket* user) CXX11_OVERRIDE
+ {
+ CloseSession();
+ }
+
+ int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE
+ {
+ // Finish handshake if needed
+ int prepret = PrepareIO(user);
+ if (prepret <= 0)
+ return prepret;
+
+ // If we resumed the handshake then this->status will be ISSL_HANDSHAKEN.
+ {
+ GnuTLS::DataReader reader(sess);
+ int ret = reader.ret();
+ if (ret > 0)
+ {
+ reader.appendto(recvq);
++ // Schedule a read if there is still data in the GnuTLS buffer
++ if (gnutls_record_check_pending(sess) > 0)
++ SocketEngine::ChangeEventMask(user, FD_ADD_TRIAL_READ);
+ return 1;
+ }
+ else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
+ {
+ return 0;
+ }
+ else if (ret == 0)
+ {
+ user->SetError("Connection closed");
+ CloseSession();
+ return -1;
+ }
+ else
+ {
+ user->SetError(gnutls_strerror(ret));
+ CloseSession();
+ return -1;
+ }
+ }
+ }
+
+ int OnStreamSocketWrite(StreamSocket* user, StreamSocket::SendQueue& sendq) CXX11_OVERRIDE
+ {
+ // Finish handshake if needed
+ int prepret = PrepareIO(user);
+ if (prepret <= 0)
+ return prepret;
+
+ // Session is ready for transferring application data
+
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+ while (true)
+ {
+ // If there is something in the GnuTLS buffer try to send() it
+ int ret = FlushBuffer(user);
+ if (ret <= 0)
+ return ret; // Couldn't flush entire buffer, retry later (or close on error)
+
+ // GnuTLS buffer is empty, if the sendq is empty as well then break to set FD_WANT_NO_WRITE
+ if (sendq.empty())
+ break;
+
+ // GnuTLS buffer is empty but sendq is not, begin sending data from the sendq
+ gnutls_record_cork(this->sess);
+ while ((!sendq.empty()) && (gbuffersize < profile->GetOutgoingRecordSize()))
+ {
+ const StreamSocket::SendQueue::Element& elem = sendq.front();
+ gbuffersize += elem.length();
+ ret = gnutls_record_send(this->sess, elem.data(), elem.length());
+ if (ret < 0)
+ {
+ CloseSession();
+ return -1;
+ }
+ sendq.pop_front();
+ }
+ }
+#else
+ int ret = 0;
+
+ while (!sendq.empty())
+ {
+ FlattenSendQueue(sendq, profile->GetOutgoingRecordSize());
+ const StreamSocket::SendQueue::Element& buffer = sendq.front();
+ ret = HandleWriteRet(user, gnutls_record_send(this->sess, buffer.data(), buffer.length()));
+
+ if (ret <= 0)
+ return ret;
+ else if (ret < (int)buffer.length())
+ {
+ sendq.erase_front(ret);
+ SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
+ return 0;
+ }
+
+ // Wrote entire record, continue sending
+ sendq.pop_front();
+ }
+#endif
+
+ SocketEngine::ChangeEventMask(user, FD_WANT_NO_WRITE);
+ return 1;
+ }
+
+ void GetCiphersuite(std::string& out) const CXX11_OVERRIDE
+ {
+ if (!IsHandshakeDone())
+ return;
+ out.append(UnknownIfNULL(gnutls_protocol_get_name(gnutls_protocol_get_version(sess)))).push_back('-');
+ out.append(UnknownIfNULL(gnutls_kx_get_name(gnutls_kx_get(sess)))).push_back('-');
+ out.append(UnknownIfNULL(gnutls_cipher_get_name(gnutls_cipher_get(sess)))).push_back('-');
+ out.append(UnknownIfNULL(gnutls_mac_get_name(gnutls_mac_get(sess))));
+ }
+
+ GnuTLS::Profile* GetProfile() { return profile; }
+ bool IsHandshakeDone() const { return (status == ISSL_HANDSHAKEN); }
+};
+
+int GnuTLS::X509Credentials::cert_callback(gnutls_session_t sess, const gnutls_datum_t* req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t* sign_algos, int sign_algos_length, cert_cb_last_param_type* st)
+{
+#ifndef GNUTLS_NEW_CERT_CALLBACK_API
+ st->type = GNUTLS_CRT_X509;
+#else
+ st->cert_type = GNUTLS_CRT_X509;
+ st->key_type = GNUTLS_PRIVKEY_X509;
+#endif
+ StreamSocket* sock = reinterpret_cast<StreamSocket*>(gnutls_transport_get_ptr(sess));
+ GnuTLS::X509Credentials& cred = static_cast<GnuTLSIOHook*>(sock->GetModHook(thismod))->GetProfile()->GetX509Credentials();
+
+ st->ncerts = cred.certs.size();
+ st->cert.x509 = cred.certs.raw();
+ st->key.x509 = cred.key.get();
+ st->deinit_all = 0;
+
+ return 0;
+}
+
+class GnuTLSIOHookProvider : public refcountbase, public IOHookProvider
+{
+ reference<GnuTLS::Profile> profile;
+
+ public:
+ GnuTLSIOHookProvider(Module* mod, reference<GnuTLS::Profile>& prof)
+ : IOHookProvider(mod, "ssl/" + prof->GetName(), IOHookProvider::IOH_SSL)
+ , profile(prof)
+ {
+ ServerInstance->Modules->AddService(*this);
+ }
+
+ ~GnuTLSIOHookProvider()
+ {
+ ServerInstance->Modules->DelService(*this);
+ }
+
+ void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
+ {
+ new GnuTLSIOHook(this, sock, GNUTLS_SERVER, profile);
+ }
+
+ void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
+ {
+ new GnuTLSIOHook(this, sock, GNUTLS_CLIENT, profile);
+ }
+};
+
+class ModuleSSLGnuTLS : public Module
+{
+ typedef std::vector<reference<GnuTLSIOHookProvider> > ProfileList;
+
+ // First member of the class, gets constructed first and destructed last
+ GnuTLS::Init libinit;
+ RandGen randhandler;
+ ProfileList profiles;
+
+ void ReadProfiles()
+ {
+ // First, store all profiles in a new, temporary container. If no problems occur, swap the two
+ // containers; this way if something goes wrong we can go back and continue using the current profiles,
+ // avoiding unpleasant situations where no new SSL connections are possible.
+ ProfileList newprofiles;
+
+ ConfigTagList tags = ServerInstance->Config->ConfTags("sslprofile");
+ if (tags.first == tags.second)
+ {
+ // No <sslprofile> tags found, create a profile named "gnutls" from settings in the <gnutls> block
+ const std::string defname = "gnutls";
+ ConfigTag* tag = ServerInstance->Config->ConfValue(defname);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No <sslprofile> tags found; using settings from the <gnutls> tag");
+
+ try
+ {
+ reference<GnuTLS::Profile> profile(GnuTLS::Profile::Create(defname, tag));
+ newprofiles.push_back(new GnuTLSIOHookProvider(this, profile));
+ }
+ catch (CoreException& ex)
+ {
+ throw ModuleException("Error while initializing the default SSL profile - " + ex.GetReason());
+ }
+ }
+
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ ConfigTag* tag = i->second;
+ if (tag->getString("provider") != "gnutls")
+ continue;
+
+ std::string name = tag->getString("name");
+ if (name.empty())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring <sslprofile> tag without name at " + tag->getTagLocation());
+ continue;
+ }
+
+ reference<GnuTLS::Profile> profile;
+ try
+ {
+ profile = GnuTLS::Profile::Create(name, tag);
+ }
+ catch (CoreException& ex)
+ {
+ throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason());
+ }
+
+ newprofiles.push_back(new GnuTLSIOHookProvider(this, profile));
+ }
+
+ // New profiles are ok, begin using them
+ // Old profiles are deleted when their refcount drops to zero
+ profiles.swap(newprofiles);
+ }
+
+ public:
+ ModuleSSLGnuTLS()
+ {
+#ifndef GNUTLS_HAS_RND
+ gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif
+ thismod = this;
+ }
+
+ void init() CXX11_OVERRIDE
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "GnuTLS lib version %s module was compiled for " GNUTLS_VERSION, gnutls_check_version(NULL));
+ ReadProfiles();
+ ServerInstance->GenRandom = &randhandler;
+ }
+
+ void OnModuleRehash(User* user, const std::string ¶m) CXX11_OVERRIDE
+ {
+ if(param != "ssl")
+ return;
+
+ try
+ {
+ ReadProfiles();
+ }
+ catch (ModuleException& ex)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, ex.GetReason() + " Not applying settings.");
+ }
+ }
+
+ ~ModuleSSLGnuTLS()
+ {
+ ServerInstance->GenRandom = &ServerInstance->HandleGenRandom;
+ }
+
+ void OnCleanup(int target_type, void* item) CXX11_OVERRIDE
+ {
+ if(target_type == TYPE_USER)
+ {
+ LocalUser* user = IS_LOCAL(static_cast<User*>(item));
+
+ if ((user) && (user->eh.GetModHook(this)))
+ {
+ // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
+ // Potentially there could be multiple SSL modules loaded at once on different ports.
+ ServerInstance->Users->QuitUser(user, "SSL module unloading");
+ }
+ }
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides SSL support for clients", VF_VENDOR);
}
- ModResult OnCheckReady(LocalUser* user)
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
{
- if ((user->eh.GetIOHook() == this) && (sessions[user->eh.GetFd()].status != ISSL_HANDSHAKEN))
+ const GnuTLSIOHook* const iohook = static_cast<GnuTLSIOHook*>(user->eh.GetModHook(this));
+ if ((iohook) && (!iohook->IsHandshakeDone()))
return MOD_RES_DENY;
return MOD_RES_PASSTHRU;
}
if (ret > 0)
{
recvq.append(buffer, ret);
-
+ int mask = 0;
+ // Schedule a read if there is still data in the OpenSSL buffer
- if (SSL_pending(session->sess) > 0)
++ if (SSL_pending(sess) > 0)
+ mask |= FD_ADD_TRIAL_READ;
- if (session->data_to_write)
+ if (data_to_write)
- SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE);
+ mask |= FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE;
+ if (mask != 0)
- ServerInstance->SE->ChangeEventMask(user, mask);
++ SocketEngine::ChangeEventMask(user, mask);
return 1;
}
else if (ret == 0)
{
if (index->second.empty())
{
- user->WriteNumeric(ERR_WORDFILTERED, ((Channel*)dest)->name, index->first, "Your message contained a censored word, and was blocked");
- user->WriteNumeric(ERR_WORDFILTERED, "%s %s %s :Your message contained a censored word, and was blocked", user->nick.c_str(), ((target_type == TYPE_CHANNEL) ? ((Channel*)dest)->name.c_str() : ((User*)dest)->nick.c_str()), index->first.c_str());
++ user->WriteNumeric(ERR_WORDFILTERED, ((target_type == TYPE_CHANNEL) ? ((Channel*)dest)->name : ((User*)dest)->nick), index->first, "Your message contained a censored word, and was blocked");
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
/* Not exempt, BOOK EM DANNO! */
- user->WriteServ("NOTICE %s :*** You cannot list within the first %lu seconds of connecting. Please try again later.",user->nick.c_str(), (unsigned long) WaitTime);
+ user->WriteNotice("*** You cannot list within the first " + ConvToStr(WaitTime) + " seconds of connecting. Please try again later.");
- /* Some crap clients (read: mIRC, various java chat applets) muck up if they don't
+ /* Some clients (e.g. mIRC, various java chat applets) muck up if they don't
* receive these numerics whenever they send LIST, so give them an empty LIST to mull over.
*/
- user->WriteNumeric(321, "%s Channel :Users Name",user->nick.c_str());
- user->WriteNumeric(323, "%s :End of channel list.",user->nick.c_str());
+ user->WriteNumeric(RPL_LISTSTART, "Channel", "Users Name");
+ user->WriteNumeric(RPL_LISTEND, "End of channel list.");
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
Utils->RouteCommand(NULL, command, parameters, user);
}
-void SpanningTreeUtilities::RouteCommand(TreeServer* origin, const std::string &command, const parameterlist& parameters, User *user)
+void SpanningTreeUtilities::RouteCommand(TreeServer* origin, CommandBase* thiscmd, const parameterlist& parameters, User* user)
{
- if (!ServerInstance->Parser->IsValidCommand(command, parameters.size(), user))
- return;
-
- /* We know it's non-null because IsValidCommand returned true */
- Command* thiscmd = ServerInstance->Parser->GetHandler(command);
-
+ const std::string& command = thiscmd->name;
RouteDescriptor routing = thiscmd->GetRouting(user, parameters);
-
- std::string sent_cmd = command;
- parameterlist params;
-
if (routing.type == ROUTE_TYPE_LOCALONLY)
- {
- /* Broadcast when it's a core command with the default route descriptor and the source is a
- * remote user or a remote server
- */
+ return;
- Version ver = thiscmd->creator->GetVersion();
- if ((!(ver.Flags & VF_CORE)) || (IS_LOCAL(user)) || (IS_SERVER(user) == ServerInstance->FakeClient))
- return;
+ const bool encap = ((routing.type == ROUTE_TYPE_OPT_BCAST) || (routing.type == ROUTE_TYPE_OPT_UCAST));
+ CmdBuilder params(user, encap ? "ENCAP" : command.c_str());
+ TreeServer* sdest = NULL;
- routing = ROUTE_BROADCAST;
- }
- else if (routing.type == ROUTE_TYPE_OPT_BCAST)
+ if (routing.type == ROUTE_TYPE_OPT_BCAST)
{
- params.push_back("*");
+ params.push('*');
params.push_back(command);
- sent_cmd = "ENCAP";
}
- else if (routing.type == ROUTE_TYPE_OPT_UCAST)
+ else if (routing.type == ROUTE_TYPE_UNICAST || routing.type == ROUTE_TYPE_OPT_UCAST)
{
- TreeServer* sdest = FindServer(routing.serverdest);
+ sdest = static_cast<TreeServer*>(routing.server);
if (!sdest)
{
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Trying to route ENCAP to nonexistent server %s",
- routing.serverdest.c_str());
- return;
+ // Assume the command handler already validated routing.serverdest and have only returned success if the target is something that the
+ // user executing the command is allowed to look up e.g. target is not an uuid if user is local.
+ sdest = FindRouteTarget(routing.serverdest);
+ if (!sdest)
+ {
- ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Trying to route %s%s to nonexistant server %s", (encap ? "ENCAP " : ""), command.c_str(), routing.serverdest.c_str());
++ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Trying to route %s%s to nonexistent server %s", (encap ? "ENCAP " : ""), command.c_str(), routing.serverdest.c_str());
+ return;
+ }
+ }
+
+ if (encap)
+ {
+ params.push_back(sdest->GetID());
+ params.push_back(command);
}
- params.push_back(sdest->GetID());
- params.push_back(command);
- sent_cmd = "ENCAP";
}
else
{
}
}
- if (!ServerInstance->SE->AddFd(eh, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE))
+ if (!SocketEngine::AddFd(eh, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE))
{
- ServerInstance->Logs->Log("USERS", DEBUG,"Internal error on new connection");
+ ServerInstance->Logs->Log("USERS", LOG_DEBUG, "Internal error on new connection");
this->QuitUser(New, "Internal error handling connection");
+ return;
}
- /* NOTE: even if dns lookups are *off*, we still need to display this.
- * BOPM and other stuff requires it.
- */
- New->WriteServ("NOTICE Auth :*** Looking up your hostname...");
if (ServerInstance->Config->RawLog)
- New->WriteServ("NOTICE Auth :*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
+ New->WriteNotice("*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
- FOREACH_MOD(I_OnSetUserIP,OnSetUserIP(New));
+ FOREACH_MOD(OnSetUserIP, (New));
if (New->quitting)
return;
--- /dev/null
-./configure --enable-extras=m_geoip.cpp,m_ldapauth.cpp,m_ldapoper.cpp,m_mysql.cpp,m_pgsql.cpp,m_regex_pcre.cpp,m_regex_posix.cpp,m_regex_tre.cpp,m_sqlite3.cpp,m_ssl_gnutls.cpp,m_ssl_openssl.cpp
-./configure --with-cc=$CXX
-make -j4 install
+ #!/bin/bash
+ set -v
+ if [ "$TRAVIS_OS_NAME" = "linux" ]
+ then
+ sudo apt-get update --assume-yes
+ sudo apt-get install --assume-yes libgeoip-dev libgnutls-dev libldap2-dev libmysqlclient-dev libpcre3-dev libpq-dev libsqlite3-dev libssl-dev libtre-dev
+ elif [ "$TRAVIS_OS_NAME" = "osx" ]
+ then
+ brew update
+ brew install geoip gnutls mysql-connector-c openssl pcre postgresql sqlite3 tre
+ brew link sqlite3 --force
+ else
+ >&2 echo "'$TRAVIS_OS_NAME' is an unknown Travis CI environment!"
+ exit 1
+ fi
+ set -e
++export TEST_BUILD_MODULES="m_geoip.cpp,m_ldap.cpp,m_mysql.cpp,m_pgsql.cpp,m_regex_pcre.cpp,m_regex_posix.cpp,m_regex_tre.cpp,m_sqlite3.cpp,m_ssl_gnutls.cpp,m_ssl_openssl.cpp"
++./tools/test-build $CXX
+ ./run/bin/inspircd --version