From: Attila Molnar Date: Wed, 17 Aug 2016 10:49:48 +0000 (+0200) Subject: Merge insp20 X-Git-Url: https://git.netwichtig.de/gitweb/?a=commitdiff_plain;h=b9e11915a976daaf790ebc763aff56e19fd49e0f;p=user%2Fhenk%2Fcode%2Finspircd.git Merge insp20 --- b9e11915a976daaf790ebc763aff56e19fd49e0f diff --cc make/template/inspircd index 138de29a9,b43ad60c9..cb2d2902d --- a/make/template/inspircd +++ b/make/template/inspircd @@@ -153,8 -135,8 +153,8 @@@ sub cmd_rehash( sub cmd_cron() { - if (getstatus() == 0) { goto &cmd_start(); } + if (getstatus() == 0) { goto &cmd_start(@_); } - exit(); + exit GENERIC_EXIT_UNSPECIFIED; } sub cmd_version() diff --cc make/template/main.mk index bff065736,23daa7efc..818b4139d --- a/make/template/main.mk +++ b/make/template/main.mk @@@ -154,10 -165,10 +154,10 @@@ all: $(FOOTER 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 "*************************************" @@@ -220,25 -231,14 +220,25 @@@ install: targe @-$(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 "" @@@ -270,23 -272,25 +270,24 @@@ clean 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' diff --cc src/configreader.cpp index e607c6e7d,301db14e8..8a432e82f --- a/src/configreader.cpp +++ b/src/configreader.cpp @@@ -409,27 -531,48 +409,28 @@@ void ServerConfig::Fill( 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(), ""); - if (ConfValue("performance")->getBool("limitsomaxconn", true)) - range(MaxConn, 0, SOMAXCONN, SOMAXCONN, ""); - range(MaxTargets, 1, 31, 20, ""); - range(NetBufferSize, 1024, 65534, 10240, ""); - range(WhoWasGroupSize, 0, 10000, 10, ""); - range(WhoWasMaxGroups, 0, 1000000, 10240, ""); - range(WhoWasMaxKeep, 3600, INT_MAX, 3600, ""); - ValidIP(DNSServer, ""); + 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") @@@ -567,13 -729,8 +568,13 @@@ void ServerConfig::Apply(ServerConfig* // 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 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 diff --cc src/coremods/core_list.cpp index 67829a55e,000000000..6a62d122f mode 100644,000000..100644 --- a/src/coremods/core_list.cpp +++ b/src/coremods/core_list.cpp @@@ -1,118 -1,0 +1,117 @@@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2009 Daniel De Graaf + * Copyright (C) 2007 Robin Burchell + * + * 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" + +/** 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& parameters, User *user); +}; + + +/** Handle /LIST + */ +CmdResult CommandList::Handle (const std::vector& parameters, User *user) +{ + int minusers = 0, maxusers = 0; + + user->WriteNumeric(RPL_LISTSTART, "Channel", "Users Name"); + - /* Work around mIRC suckyness. YOU SUCK, KHALED! */ - if (parameters.size() == 1) ++ 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) diff --cc src/coremods/core_oper/cmd_kill.cpp index 0d111ce02,000000000..e75566e2f mode 100644,000000..100644 --- a/src/coremods/core_oper/cmd_kill.cpp +++ b/src/coremods/core_oper/cmd_kill.cpp @@@ -1,156 -1,0 +1,156 @@@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2009 Daniel De Graaf + * Copyright (C) 2008 Craig Edwards + * Copyright (C) 2007 Robin Burchell + * + * 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 "core_oper.h" + +CommandKill::CommandKill(Module* parent) + : Command(parent, "KILL", 2, 2) +{ + flags_needed = 'o'; + syntax = " "; + TRANSLATE2(TR_CUSTOM, TR_CUSTOM); +} + + +/** Handle /KILL + */ +CmdResult CommandKill::Handle (const std::vector& 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 (!user->server->IsULine()) ++ 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& 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); +} diff --cc src/coremods/core_stats.cpp index d890d19ea,000000000..ee0c50db2 mode 100644,000000..100644 --- a/src/coremods/core_stats.cpp +++ b/src/coremods/core_stats.cpp @@@ -1,409 -1,0 +1,409 @@@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2009-2010 Daniel De Graaf + * Copyright (C) 2007-2008 Craig Edwards + * Copyright (C) 2007 Robin Burchell + * + * 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 "xline.h" + +#ifdef _WIN32 +#include +#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) { syntax = " []"; } ++ CommandStats ( Module* parent) : Command(parent,"STATS",1,2) { allow_empty_last_param = false; syntax = " []"; } + /** 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& parameters, User *user); + RouteDescriptor GetRouting(User* user, const std::vector& 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::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(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& parameters, User *user) +{ + if (parameters.size() > 1 && parameters[1] != ServerInstance->Config->ServerName) + { + // Give extra penalty if a non-oper does /STATS + 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& rows = stats.GetRows(); + for (std::vector::const_iterator i = rows.begin(); i != rows.end(); ++i) + { + const Stats::Row& row = *i; + user->WriteRemoteNumeric(row); + } + + return CMD_SUCCESS; +} + +COMMAND_INIT(CommandStats) diff --cc src/inspircd.cpp index eb24cdea0,0fa90fca5..5d4d8081f --- a/src/inspircd.cpp +++ b/src/inspircd.cpp @@@ -207,10 -307,12 +207,12 @@@ void InspIRCd::WritePID(const std::stri outfile.close(); } else - { - if (exitonfail) + { - 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." : "")); ++ this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to write PID-file '%s'%s", fname.c_str(), (exitonfail ? ", exiting." : "")); + if (exitonfail) - Exit(EXIT_STATUS_PID); ++ Exit(EXIT_STATUS_PID); } #endif } diff --cc src/mode.cpp index b29eda828,0f5d45783..3762dc52e --- a/src/mode.cpp +++ b/src/mode.cpp @@@ -791,24 -837,35 +791,33 @@@ std::string ModeParser::GiveModeList(Mo return type1 + "," + type2 + "," + type3 + "," + type4; } + struct PrefixModeSorter + { - bool operator()(ModeHandler* lhs, ModeHandler* rhs) ++ 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 > prefixes; - std::vector prefixes; ++ std::vector 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 >::reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); ++n) + std::sort(prefixes.begin(), prefixes.end(), PrefixModeSorter()); - for (std::vector::const_reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); ++n) ++ for (std::vector::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; diff --cc src/modules/extra/m_ssl_gnutls.cpp index bda4e6a48,2f4acf3f0..e5cb8ee90 --- a/src/modules/extra/m_ssl_gnutls.cpp +++ b/src/modules/extra/m_ssl_gnutls.cpp @@@ -866,487 -972,15 +866,490 @@@ info_done_dealloc 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(session_wrap); +#ifdef _WIN32 + GnuTLSIOHook* session = static_cast(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(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(transportptr); +#ifdef _WIN32 + GnuTLSIOHook* session = static_cast(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(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(session_wrap); +#ifdef _WIN32 + GnuTLSIOHook* session = static_cast(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(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& 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(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(gnutls_transport_get_ptr(sess)); + GnuTLS::X509Credentials& cred = static_cast(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 profile; + + public: + GnuTLSIOHookProvider(Module* mod, reference& 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 > 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 tags found, create a profile named "gnutls" from settings in the block + const std::string defname = "gnutls"; + ConfigTag* tag = ServerInstance->Config->ConfValue(defname); + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No tags found; using settings from the tag"); + + try + { + reference 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 tag without name at " + tag->getTagLocation()); + continue; + } + + reference 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(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(user->eh.GetModHook(this)); + if ((iohook) && (!iohook->IsHandshakeDone())) return MOD_RES_DENY; return MOD_RES_PASSTHRU; } diff --cc src/modules/extra/m_ssl_openssl.cpp index 4df0d8962,aee7a5e34..8467cc6d4 --- a/src/modules/extra/m_ssl_openssl.cpp +++ b/src/modules/extra/m_ssl_openssl.cpp @@@ -631,8 -598,15 +631,14 @@@ class OpenSSLIOHook : public SSLIOHoo 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) diff --cc src/modules/m_censor.cpp index 7d8c74024,65b965df2..c8b6b73a8 --- a/src/modules/m_censor.cpp +++ b/src/modules/m_censor.cpp @@@ -79,7 -101,7 +79,7 @@@ class ModuleCensor : public Modul { 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; } diff --cc src/modules/m_securelist.cpp index b6c6ce174,5d11d27f7..b925c3f37 --- a/src/modules/m_securelist.cpp +++ b/src/modules/m_securelist.cpp @@@ -62,12 -75,12 +62,12 @@@ class ModuleSecureList : public Modul 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; diff --cc src/modules/m_spanningtree/postcommand.cpp index 7b0478229,3f5d427e1..64ca72977 --- a/src/modules/m_spanningtree/postcommand.cpp +++ b/src/modules/m_spanningtree/postcommand.cpp @@@ -30,42 -37,49 +30,42 @@@ void ModuleSpanningTree::OnPostCommand( 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(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 { diff --cc src/usermanager.cpp index 7b4bbe281,2cb7ad511..5891b42f0 --- a/src/usermanager.cpp +++ b/src/usermanager.cpp @@@ -147,16 -139,21 +147,17 @@@ void UserManager::AddUser(int socket, L } } - 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; diff --cc tools/travis-ci.sh index 000000000,6dbc82300..8828532b0 mode 000000,100755..100755 --- a/tools/travis-ci.sh +++ b/tools/travis-ci.sh @@@ -1,0 -1,20 +1,19 @@@ + #!/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 -./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 ++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