From 7107ec12d8640d35cfe3d5002db1bc1deb33625d Mon Sep 17 00:00:00 2001 From: danieldg Date: Sat, 26 Sep 2009 16:41:07 +0000 Subject: [PATCH] Flexible SendQ git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@11766 e03df62e-2008-0410-955e-edbf42e46eb7 --- conf/inspircd.conf.example | 27 ++-- include/modules.h | 11 +- include/users.h | 22 ++- src/configreader.cpp | 20 ++- src/modules.cpp | 1 - src/modules/m_password_hash.cpp | 4 +- src/modules/m_safelist.cpp | 236 -------------------------------- src/stats.cpp | 2 +- src/userprocess.cpp | 5 - src/users.cpp | 30 +++- 10 files changed, 71 insertions(+), 287 deletions(-) delete mode 100644 src/modules/m_safelist.cpp diff --git a/conf/inspircd.conf.example b/conf/inspircd.conf.example index 1247a5dc3..0b860bb81 100644 --- a/conf/inspircd.conf.example +++ b/conf/inspircd.conf.example @@ -241,16 +241,6 @@ # send /nick, /user or /pass) timeout="10" - # pingfreq: How often (in seconds) the server tries to ping connecting clients. - pingfreq="120" - - # sendq: Amount of data (in bytes) that the server is allowed to send to the user - # before they are dropped. - sendq="262144" - - # recvq: Amount of data (in bytes) allowed in a clients queue before they are dropped. - recvq="8192" - # localmax: Maximum local connections per IP (or CIDR mask, see below). localmax="3" @@ -295,11 +285,17 @@ # pingfreq: How often (in seconds) the server tries to ping connecting clients. pingfreq="120" - # sendq: Amount of data that the server is allowed to send to the user - # before they are dropped. - sendq="262144" + # hardsendq: maximum amount of data allowed in a client's send queue + # before they are dropped. Keep this value higher than the length of + # your network's /LIST or /WHO output, or you will have lots of + # disconnects from sendq overruns! + hardsendq="1048576" + + # softsendq: amount of data in a client's send queue before the server + # begins delaying their commands + softsendq="8192" - # recvq: amount of data allowed in a clients queue before they are dropped. + # recvq: amount of data allowed in a client's queue before they are dropped. recvq="8192" # localmax: Maximum local connections per IP. @@ -544,8 +540,7 @@ netbuffersize="10240" # maxwho: Maximum number of results to show in a /who query. - # It is not recommended to set this above 1024. - maxwho="128" + maxwho="4096" # somaxconn: The maximum number of connections that may be waiting # in the accept queue. This is *NOT* the total maximum number of diff --git a/include/modules.h b/include/modules.h index 63492b885..ff52cd600 100644 --- a/include/modules.h +++ b/include/modules.h @@ -388,7 +388,7 @@ enum Implementation I_OnPostTopicChange, I_OnEvent, I_OnRequest, I_OnGlobalOper, I_OnPostConnect, I_OnAddBan, I_OnDelBan, I_OnChangeLocalUserGECOS, I_OnUserRegister, I_OnChannelPreDelete, I_OnChannelDelete, I_OnPostOper, I_OnSyncNetwork, I_OnSetAway, I_OnUserList, I_OnPostCommand, I_OnPostJoin, - I_OnWhoisLine, I_OnBuildExemptList, I_OnGarbageCollect, I_OnBufferFlushed, + I_OnWhoisLine, I_OnBuildExemptList, I_OnGarbageCollect, I_OnText, I_OnPassCompare, I_OnRunTestSuite, I_OnNamesListItem, I_OnNumeric, I_OnHookIO, I_OnHostCycle, I_OnPreRehash, I_OnModuleRehash, I_OnSendWhoLine, I_OnChangeIdent, I_END @@ -1305,15 +1305,6 @@ class CoreExport Module : public Extensible */ virtual void OnGarbageCollect(); - /** Called whenever a user's write buffer has been completely sent. - * This is called when the user's write buffer is completely empty, and - * there are no more pending bytes to be written and no pending write events - * in the socket engine's queue. This may be used to refill the buffer with - * data which is being spooled in a controlled manner, e.g. LIST lines. - * @param user The user who's buffer is now empty. - */ - virtual void OnBufferFlushed(User* user); - /** Add test suite hooks here. These are used for testing functionality of a module * via the --testsuite debugging parameter. */ diff --git a/include/users.h b/include/users.h index 763d45c57..76337b2c3 100644 --- a/include/users.h +++ b/include/users.h @@ -93,8 +93,14 @@ struct CoreExport ConnectClass : public classbase std::string hash; /** Maximum size of sendq for users in this class (bytes) + * Users cannot send commands if they go over this limit */ - unsigned long sendqmax; + unsigned long softsendqmax; + + /** Maximum size of sendq for users in this class (bytes) + * Users are killed if they go over this limit + */ + unsigned long hardsendqmax; /** Maximum size of recvq for users in this class (bytes) */ @@ -157,11 +163,19 @@ struct CoreExport ConnectClass : public classbase return (pingtime ? pingtime : 120); } - /** Returns the maximum sendq value + /** Returns the maximum sendq value (soft limit) + * Note that this is in addition to internal OS buffers + */ + unsigned long GetSendqSoftMax() + { + return (softsendqmax ? softsendqmax : 4096); + } + + /** Returns the maximum sendq value (hard limit) */ - unsigned long GetSendqMax() + unsigned long GetSendqHardMax() { - return (sendqmax ? sendqmax : 262114); + return (hardsendqmax ? hardsendqmax : 0x100000); } /** Returns the maximum recvq value diff --git a/src/configreader.cpp b/src/configreader.cpp index 3fb60034d..21bfce3db 100644 --- a/src/configreader.cpp +++ b/src/configreader.cpp @@ -338,8 +338,8 @@ static bool ValidateMaxWho(ServerConfig* conf, const char*, const char*, ValueIt { if ((data.GetInteger() > 65535) || (data.GetInteger() < 1)) { - ServerInstance->Logs->Log("CONFIG",DEFAULT," size out of range, setting to default of 128."); - data.Set(128); + ServerInstance->Logs->Log("CONFIG",DEFAULT," size out of range, setting to default of 1024."); + data.Set(1024); } return true; } @@ -688,7 +688,19 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current) if (ConfValue("connect", "pingfreq", i, tmpv, false)) me->pingtime = atol(tmpv.c_str()); if (ConfValue("connect", "sendq", i, tmpv, false)) - me->sendqmax = atol(tmpv.c_str()); + { + // attempt to guess a good hard/soft sendq from a single value + long value = atol(tmpv.c_str()); + if (value > 16384) + me->softsendqmax = value / 16; + else + me->softsendqmax = value; + me->hardsendqmax = value * 8; + } + if (ConfValue("connect", "softsendq", i, tmpv, false)) + me->softsendqmax = atol(tmpv.c_str()); + if (ConfValue("connect", "hardsendq", i, tmpv, false)) + me->hardsendqmax = atol(tmpv.c_str()); if (ConfValue("connect", "recvq", i, tmpv, false)) me->recvqmax = atol(tmpv.c_str()); if (ConfValue("connect", "localmax", i, tmpv, false)) @@ -770,7 +782,7 @@ static const InitialConfig Values[] = { {"options", "suffixpart", "", new ValueContainerChar (&ServerConfig::SuffixPart), DT_CHARPTR, NULL}, {"options", "fixedpart", "", new ValueContainerChar (&ServerConfig::FixedPart), DT_CHARPTR, NULL}, {"performance", "netbuffersize","10240", new ValueContainerInt (&ServerConfig::NetBufferSize), DT_INTEGER, ValidateNetBufferSize}, - {"performance", "maxwho", "128", new ValueContainerInt (&ServerConfig::MaxWhoResults), DT_INTEGER, ValidateMaxWho}, + {"performance", "maxwho", "1024", new ValueContainerInt (&ServerConfig::MaxWhoResults), DT_INTEGER, ValidateMaxWho}, {"options", "allowhalfop", "0", new ValueContainerBool (&ServerConfig::AllowHalfop), DT_BOOLEAN, ValidateHalfOp}, {"dns", "server", "", new ValueContainerChar (&ServerConfig::DNSServer), DT_IPADDRESS,ValidateDnsServer}, {"dns", "timeout", "5", new ValueContainerInt (&ServerConfig::dns_timeout), DT_INTEGER, NULL}, diff --git a/src/modules.cpp b/src/modules.cpp index 9e775ed1e..67c90b128 100644 --- a/src/modules.cpp +++ b/src/modules.cpp @@ -190,7 +190,6 @@ ModResult Module::OnUserList(User*, Channel*) { return MOD_RES_PASSTHRU; } ModResult Module::OnWhoisLine(User*, User*, int&, std::string&) { return MOD_RES_PASSTHRU; } void Module::OnBuildExemptList(MessageType, Channel*, User*, char, CUList&, const std::string&) { } void Module::OnGarbageCollect() { } -void Module::OnBufferFlushed(User*) { } void Module::OnText(User*, void*, int, const std::string&, char, CUList&) { } void Module::OnRunTestSuite() { } void Module::OnNamesListItem(User*, Membership*, std::string&, std::string&) { } diff --git a/src/modules/m_password_hash.cpp b/src/modules/m_password_hash.cpp index 011a592e7..12769acd4 100644 --- a/src/modules/m_password_hash.cpp +++ b/src/modules/m_password_hash.cpp @@ -29,6 +29,7 @@ class CommandMkpasswd : public Command CommandMkpasswd(Module* Creator, hashymodules &h, std::deque &n) : Command(Creator, "MKPASSWD", 2), hashers(h), names(n) { syntax = " "; + Penalty = 5; } void MakeHash(User* user, const char* algo, const char* stuff) @@ -57,9 +58,6 @@ class CommandMkpasswd : public Command CmdResult Handle (const std::vector& parameters, User *user) { MakeHash(user, parameters[0].c_str(), parameters[1].c_str()); - // this hashing could take some time, increasing server load. - // Slow down the user if they are trying to flood mkpasswd requests - user->IncreasePenalty(5); return CMD_SUCCESS; } diff --git a/src/modules/m_safelist.cpp b/src/modules/m_safelist.cpp deleted file mode 100644 index 0160de7c2..000000000 --- a/src/modules/m_safelist.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2009 InspIRCd Development Team - * See: http://wiki.inspircd.org/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" - -/** Holds a users m_safelist state - */ -class ListData : public classbase -{ - public: - long list_start; - long list_position; - bool list_ended; - const std::string glob; - int minusers; - int maxusers; - - ListData() : list_start(0), list_position(0), list_ended(false) {}; - ListData(long pos, time_t t, const std::string &pattern, int mi, int ma) : list_start(t), list_position(pos), list_ended(false), glob(pattern), minusers(mi), maxusers(ma) {}; -}; - -/* $ModDesc: A module overriding /list, and making it safe - stop those sendq problems. */ - -class ModuleSafeList : public Module -{ - time_t ThrottleSecs; - size_t ServerNameSize; - int global_listing; - int LimitList; - SimpleExtItem listData; - LocalIntExt listTime; - public: - ModuleSafeList() : listData("safelist_data", this), listTime("safelist_last", this) - { - OnRehash(NULL); - Extensible::Register(&listData); - Extensible::Register(&listTime); - Implementation eventlist[] = { I_OnBufferFlushed, I_OnPreCommand, I_On005Numeric, I_OnRehash }; - ServerInstance->Modules->Attach(eventlist, this, 4); - } - - ~ModuleSafeList() - { - } - - void OnRehash(User* user) - { - ConfigReader MyConf; - ThrottleSecs = MyConf.ReadInteger("safelist", "throttle", "60", 0, true); - LimitList = MyConf.ReadInteger("safelist", "maxlisters", "50", 0, true); - ServerNameSize = strlen(ServerInstance->Config->ServerName) + 4; - global_listing = 0; - } - - Version GetVersion() - { - return Version("A module overriding /list, and making it safe - stop those sendq problems.",VF_VENDOR,API_VERSION); - } - - - /* - * OnPreCommand() - * Intercept the LIST command. - */ - ModResult OnPreCommand(std::string &command, std::vector ¶meters, User *user, bool validated, const std::string &original_line) - { - /* If the command doesnt appear to be valid, we dont want to mess with it. */ - if (!validated) - return MOD_RES_PASSTHRU; - - if (command == "LIST") - { - return this->HandleList(parameters, user); - } - return MOD_RES_PASSTHRU; - } - - /* - * HandleList() - * Handle (override) the LIST command. - */ - ModResult HandleList(const std::vector ¶meters, User* user) - { - int pcnt = parameters.size(); - int minusers = 0, maxusers = 0; - - if (global_listing >= LimitList && !IS_OPER(user)) - { - user->WriteServ("NOTICE %s :*** Server load is currently too heavy. Please try again later.", user->nick.c_str()); - user->WriteNumeric(321, "%s Channel :Users Name",user->nick.c_str()); - user->WriteNumeric(323, "%s :End of channel list.",user->nick.c_str()); - return MOD_RES_DENY; - } - - /* First, let's check if the user is currently /list'ing */ - ListData *ld = listData.get(user); - - if (ld) - { - /* user is already /list'ing, we don't want to do shit. */ - return MOD_RES_DENY; - } - - /* Work around mIRC suckyness. YOU SUCK, KHALED! */ - if (pcnt == 1) - { - if (parameters[0][0] == '<') - { - maxusers = atoi(parameters[0].c_str()+1); - ServerInstance->Logs->Log("m_safelist",DEBUG,"Max users: %d", maxusers); - pcnt = 0; - } - else if (parameters[0][0] == '>') - { - minusers = atoi(parameters[0].c_str()+1); - ServerInstance->Logs->Log("m_safelist",DEBUG,"Min users: %d", minusers); - pcnt = 0; - } - } - - time_t last_list_time = listTime.get(user); - if (last_list_time && ServerInstance->Time() < last_list_time + ThrottleSecs) - { - user->WriteServ("NOTICE %s :*** Woah there, slow down a little, you can't /LIST so often!",user->nick.c_str()); - user->WriteNumeric(321, "%s Channel :Users Name",user->nick.c_str()); - user->WriteNumeric(323, "%s :End of channel list.",user->nick.c_str()); - return MOD_RES_DENY; - } - - /* - * start at channel 0! ;) - */ - ld = new ListData(0,ServerInstance->Time(), (pcnt && (parameters[0][0] != '<' && parameters[0][0] != '>')) ? parameters[0] : "*", minusers, maxusers); - listData.set(user, ld); - listTime.set(user, ServerInstance->Time()); - - user->WriteNumeric(321, "%s Channel :Users Name",user->nick.c_str()); - - global_listing++; - - return MOD_RES_DENY; - } - - void OnBufferFlushed(User* user) - { - char buffer[MAXBUF]; - ListData* ld = listData.get(user); - if (ld) - { - Channel* chan = NULL; - unsigned long amount_sent = 0; - do - { - chan = ServerInstance->GetChannelIndex(ld->list_position); - bool is_special = (chan && (chan->HasUser(user) || user->HasPrivPermission("channels/auspex"))); - long users = chan ? chan->GetUserCounter() : 0; - - bool too_few = (ld->minusers && (users <= ld->minusers)); - bool too_many = (ld->maxusers && (users >= ld->maxusers)); - - if (chan && (too_many || too_few)) - { - ld->list_position++; - continue; - } - - if (chan) - { - bool display = (InspIRCd::Match(chan->name, ld->glob) || (!chan->topic.empty() && InspIRCd::Match(chan->topic, ld->glob))); - - if (!users || !display) - { - ld->list_position++; - continue; - } - - /* +s, not in chan / not got channels/auspex */ - if (chan->IsModeSet('s') && !is_special) - { - ld->list_position++; - continue; - } - - if (chan->IsModeSet('p') && !is_special) - { - /* Channel is +p and user is outside/not privileged */ - int counter = snprintf(buffer, MAXBUF, "322 %s * %ld :", user->nick.c_str(), users); - amount_sent += counter + ServerNameSize; - user->WriteServ(std::string(buffer)); - } - else - { - /* User is in the channel/privileged, channel is not +s */ - int counter = snprintf(buffer, MAXBUF, "322 %s %s %ld :[+%s] %s", user->nick.c_str(), chan->name.c_str(), users, chan->ChanModes(is_special), chan->topic.c_str()); - amount_sent += counter + ServerNameSize; - user->WriteServ(std::string(buffer)); - } - } - else - { - if (!ld->list_ended) - { - ld->list_ended = true; - user->WriteNumeric(323, "%s :End of channel list.",user->nick.c_str()); - } - } - - ld->list_position++; - } - while ((chan != NULL) && (amount_sent < (user->MyClass->GetSendqMax() / 4))); - if (ld->list_ended) - { - listData.unset(user); - global_listing--; - } - } - } - - void On005Numeric(std::string &output) - { - output.append(" SAFELIST"); - } - -}; - -MODULE_INIT(ModuleSafeList) diff --git a/src/stats.cpp b/src/stats.cpp index ba1b19e9a..b7b92d9a6 100644 --- a/src/stats.cpp +++ b/src/stats.cpp @@ -79,7 +79,7 @@ void InspIRCd::DoStats(char statschar, User* user, string_list &results) for (ClassVector::iterator i = this->Config->Classes.begin(); i != this->Config->Classes.end(); i++) { ConnectClass* c = *i; - results.push_back(sn+" 218 "+user->nick+" Y "+ConvToStr(idx)+" "+ConvToStr(c->GetPingTime())+" 0 "+ConvToStr(c->GetSendqMax())+" :"+ + results.push_back(sn+" 218 "+user->nick+" Y "+ConvToStr(idx)+" "+ConvToStr(c->GetPingTime())+" 0 "+ConvToStr(c->GetSendqHardMax())+" :"+ ConvToStr(c->GetRecvqMax())+" "+ConvToStr(c->GetRegTimeout())); idx++; } diff --git a/src/userprocess.cpp b/src/userprocess.cpp index bc354fa6d..4da623590 100644 --- a/src/userprocess.cpp +++ b/src/userprocess.cpp @@ -61,11 +61,6 @@ void InspIRCd::DoBackgroundUserStuff() curr->OnDataReady(); } - if (curr->getSendQSize() == 0) - { - FOREACH_MOD(I_OnBufferFlushed,OnBufferFlushed(curr)); - } - switch (curr->registered) { case REG_ALL: diff --git a/src/users.cpp b/src/users.cpp index f5165edfa..1f38dfada 100644 --- a/src/users.cpp +++ b/src/users.cpp @@ -518,14 +518,17 @@ void User::OnDataReady() if (quitting) return; - if (MyClass && !HasPrivPermission("users/flood/increased-buffers") && recvq.length() > MyClass->GetRecvqMax()) + if (MyClass && recvq.length() > MyClass->GetRecvqMax() && !HasPrivPermission("users/flood/increased-buffers")) { ServerInstance->Users->QuitUser(this, "RecvQ exceeded"); ServerInstance->SNO->WriteToSnoMask('a', "User %s RecvQ of %lu exceeds connect class maximum of %lu", nick.c_str(), (unsigned long)recvq.length(), MyClass->GetRecvqMax()); } + unsigned long sendqmax = ULONG_MAX; + if (MyClass && !HasPrivPermission("users/flood/increased-buffers")) + sendqmax = MyClass->GetSendqSoftMax(); - while (this->Penalty < 10) + while (Penalty < 10 && getSendQSize() < sendqmax) { std::string line; line.reserve(MAXBUF); @@ -559,6 +562,9 @@ eol_found: ServerInstance->Parser->ProcessBuffer(line, this); } + // Add pseudo-penalty so that we continue processing after sendq recedes + if (Penalty == 0 && getSendQSize() >= sendqmax) + Penalty++; } void User::AddWriteBuf(const std::string &data) @@ -566,7 +572,7 @@ void User::AddWriteBuf(const std::string &data) // Don't bother sending text to remote users! if (IS_REMOTE(this)) return; - if (!quitting && MyClass && getSendQSize() + data.length() > MyClass->GetSendqMax() && !HasPrivPermission("users/flood/increased-buffers")) + if (!quitting && MyClass && getSendQSize() + data.length() > MyClass->GetSendqHardMax() && !HasPrivPermission("users/flood/increased-buffers")) { /* * Fix by brain - Set the error text BEFORE calling, because @@ -575,7 +581,7 @@ void User::AddWriteBuf(const std::string &data) */ ServerInstance->Users->QuitUser(this, "SendQ exceeded"); ServerInstance->SNO->WriteToSnoMask('a', "User %s SendQ of %lu exceeds connect class maximum of %lu", - nick.c_str(), (unsigned long)getSendQSize() + data.length(), MyClass->GetSendqMax()); + nick.c_str(), (unsigned long)getSendQSize() + data.length(), MyClass->GetSendqHardMax()); return; } @@ -1809,12 +1815,21 @@ const std::string FakeUser::GetFullRealHost() } ConnectClass::ConnectClass(char t, const std::string& mask) - : type(t), name("unnamed"), registration_timeout(0), host(mask), pingtime(0), pass(""), hash(""), sendqmax(0), recvqmax(0), maxlocal(0), maxglobal(0), maxchans(0), port(0), limit(0), RefCount(1) + : type(t), name("unnamed"), registration_timeout(0), host(mask), + pingtime(0), pass(""), hash(""), softsendqmax(0), hardsendqmax(0), + recvqmax(0), maxlocal(0), maxglobal(0), maxchans(0), port(0), limit(0), + RefCount(1) { } ConnectClass::ConnectClass(char t, const std::string& mask, const ConnectClass& parent) - : type(t), name("unnamed"), registration_timeout(parent.registration_timeout), host(mask), pingtime(parent.pingtime), pass(parent.pass), hash(parent.hash), sendqmax(parent.sendqmax), recvqmax(parent.recvqmax), maxlocal(parent.maxlocal), maxglobal(parent.maxglobal), maxchans(parent.maxchans), port(parent.port), limit(parent.limit), RefCount(1) + : type(t), name("unnamed"), + registration_timeout(parent.registration_timeout), host(mask), + pingtime(parent.pingtime), pass(parent.pass), hash(parent.hash), + softsendqmax(parent.softsendqmax), hardsendqmax(parent.hardsendqmax), + recvqmax(parent.recvqmax), maxlocal(parent.maxlocal), + maxglobal(parent.maxglobal), maxchans(parent.maxchans), + port(parent.port), limit(parent.limit), RefCount(1) { } @@ -1826,7 +1841,8 @@ void ConnectClass::Update(const ConnectClass* src) pingtime = src->pingtime; pass = src->pass; hash = src->hash; - sendqmax = src->sendqmax; + softsendqmax = src->softsendqmax; + hardsendqmax = src->hardsendqmax; recvqmax = src->recvqmax; maxlocal = src->maxlocal; maxglobal = src->maxglobal; -- 2.39.5