summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--conf/inspircd.conf.example27
-rw-r--r--include/modules.h11
-rw-r--r--include/users.h22
-rw-r--r--src/configreader.cpp20
-rw-r--r--src/modules.cpp1
-rw-r--r--src/modules/m_password_hash.cpp4
-rw-r--r--src/modules/m_safelist.cpp236
-rw-r--r--src/stats.cpp2
-rw-r--r--src/userprocess.cpp5
-rw-r--r--src/users.cpp30
10 files changed, 71 insertions, 287 deletions
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,"<performance:maxwho> size out of range, setting to default of 128.");
- data.Set(128);
+ ServerInstance->Logs->Log("CONFIG",DEFAULT,"<performance:maxwho> 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<std::string> &n) : Command(Creator, "MKPASSWD", 2), hashers(h), names(n)
{
syntax = "<hashtype> <any-text>";
+ 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<std::string>& 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> 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<std::string> &parameters, 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<std::string> &parameters, 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;