diff options
28 files changed, 378 insertions, 150 deletions
diff --git a/docs/conf/inspircd.conf.example b/docs/conf/inspircd.conf.example index e0ed5e67e..eeed82237 100644 --- a/docs/conf/inspircd.conf.example +++ b/docs/conf/inspircd.conf.example @@ -646,6 +646,12 @@ # to 5, while others (such as linux and *BSD) default to 128. somaxconn="128" + # limitsomaxconn: By default, somaxconn (see above) is limited to a + # safe maximum value in the 2.0 branch for compatibility reasons. + # This setting can be used to disable this limit, forcing InspIRCd + # to use the value specifed above. + limitsomaxconn="true" + # softlimit: This optional feature allows a defined softlimit for # connections. If defined, it sets a soft max connections value. softlimit="12800" diff --git a/include/modules.h b/include/modules.h index 0b91e6048..72ee2683d 100644 --- a/include/modules.h +++ b/include/modules.h @@ -391,6 +391,11 @@ class CoreExport Module : public classbase, public usecountbase */ DLLManager* ModuleDLLManager; + /** If true, this module will be unloaded soon, further unload attempts will fail + * Value is used by the ModuleManager internally, you should not modify it + */ + bool dying; + /** Default constructor. * Creates a module class. Don't do any type of hook registration or checks * for other modules here; do that in init(). diff --git a/include/socketengine.h b/include/socketengine.h index fd199c324..8e4c3dfc9 100644 --- a/include/socketengine.h +++ b/include/socketengine.h @@ -488,6 +488,24 @@ public: /** Get data transfer statistics, kilobits per second in and out and total. */ void GetStats(float &kbitpersec_in, float &kbitpersec_out, float &kbitpersec_total); + + /** Should we ignore the error in errno? + * Checks EAGAIN and WSAEWOULDBLOCK + */ + static bool IgnoreError(); }; +inline bool SocketEngine::IgnoreError() +{ + if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) + return true; + +#ifdef _WIN32 + if (WSAGetLastError() == WSAEWOULDBLOCK) + return true; +#endif + + return false; +} + SocketEngine* CreateSocketEngine(); diff --git a/modulemanager b/modulemanager index d1212faf5..7884654af 100755 --- a/modulemanager +++ b/modulemanager @@ -262,7 +262,7 @@ sub resolve_deps { } } -my $action = lc shift @ARGV; +my $action = $#ARGV > 0 ? lc shift @ARGV : 'help'; if ($action eq 'install') { for my $mod (@ARGV) { diff --git a/src/channels.cpp b/src/channels.cpp index edb4d056a..9e9ad670a 100644 --- a/src/channels.cpp +++ b/src/channels.cpp @@ -34,12 +34,10 @@ static ModeReference ban(NULL, "ban"); Channel::Channel(const std::string &cname, time_t ts) { - chan_hash::iterator findchan = ServerInstance->chanlist->find(cname); - if (findchan != ServerInstance->chanlist->end()) + if (!ServerInstance->chanlist->insert(std::make_pair(cname, this)).second) throw CoreException("Cannot create duplicate channel " + cname); - (*(ServerInstance->chanlist))[cname.c_str()] = this; - this->name.assign(cname, 0, ServerInstance->Config->Limits.ChanMax); + this->name = cname; this->age = ts ? ts : ServerInstance->Time(); topicset = 0; diff --git a/src/commands/cmd_invite.cpp b/src/commands/cmd_invite.cpp index 6e5aead12..7221521de 100644 --- a/src/commands/cmd_invite.cpp +++ b/src/commands/cmd_invite.cpp @@ -72,16 +72,16 @@ CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, Use return CMD_FAILURE; } - if (c->HasUser(u)) - { - user->WriteNumeric(ERR_USERONCHANNEL, "%s %s %s :is already on channel",user->nick.c_str(),u->nick.c_str(),c->name.c_str()); - return CMD_FAILURE; - } - if ((IS_LOCAL(user)) && (!c->HasUser(user))) - { + { user->WriteNumeric(ERR_NOTONCHANNEL, "%s %s :You're not on that channel!",user->nick.c_str(), c->name.c_str()); - return CMD_FAILURE; + return CMD_FAILURE; + } + + if (c->HasUser(u)) + { + user->WriteNumeric(ERR_USERONCHANNEL, "%s %s %s :is already on channel",user->nick.c_str(),u->nick.c_str(),c->name.c_str()); + return CMD_FAILURE; } FIRST_MOD_RESULT(OnUserPreInvite, MOD_RESULT, (user,u,c,timeout)); diff --git a/src/configreader.cpp b/src/configreader.cpp index 1b565c7f7..dcf4b7162 100644 --- a/src/configreader.cpp +++ b/src/configreader.cpp @@ -431,7 +431,8 @@ void ServerConfig::Fill() NoSnoticeStack = options->getBool("nosnoticestack", false); range(SoftLimit, 10, ServerInstance->SE->GetMaxFds(), ServerInstance->SE->GetMaxFds(), "<performance:softlimit>"); - range(MaxConn, 0, SOMAXCONN, SOMAXCONN, "<performance:somaxconn>"); + if (ConfValue("performance")->getBool("limitsomaxconn", true)) + range(MaxConn, 0, SOMAXCONN, SOMAXCONN, "<performance:somaxconn>"); range(MaxTargets, 1, 31, 20, "<security:maxtargets>"); range(NetBufferSize, 1024, 65534, 10240, "<performance:netbuffersize>"); diff --git a/src/inspsocket.cpp b/src/inspsocket.cpp index b1bfaa9fd..34d7d3f8c 100644 --- a/src/inspsocket.cpp +++ b/src/inspsocket.cpp @@ -200,7 +200,7 @@ void StreamSocket::DoRead() error = "Connection closed"; ServerInstance->SE->ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE); } - else if (errno == EAGAIN) + else if (SocketEngine::IgnoreError()) { ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_READ | FD_READ_WILL_BLOCK); } @@ -291,7 +291,7 @@ void StreamSocket::DoWrite() } else if (rv < 0) { - if (errno == EAGAIN || errno == EINTR) + if (errno == EINTR || SocketEngine::IgnoreError()) ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK); else SetError(strerror(errno)); @@ -388,7 +388,7 @@ void StreamSocket::DoWrite() { error = "Connection closed"; } - else if (errno == EAGAIN) + else if (SocketEngine::IgnoreError()) { eventChange = FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK; } diff --git a/src/listensocket.cpp b/src/listensocket.cpp index 2f25c5493..8ac8babf3 100644 --- a/src/listensocket.cpp +++ b/src/listensocket.cpp @@ -75,7 +75,8 @@ ListenSocket::~ListenSocket() { ServerInstance->SE->DelFd(this); ServerInstance->Logs->Log("SOCKET", LOG_DEBUG,"Shut down listener on fd %d", this->fd); - if (ServerInstance->SE->Shutdown(this, 2) || ServerInstance->SE->Close(this)) + ServerInstance->SE->Shutdown(this, 2); + if (ServerInstance->SE->Close(this) != 0) ServerInstance->Logs->Log("SOCKET", LOG_DEBUG,"Failed to cancel listener: %s", strerror(errno)); this->fd = -1; } diff --git a/src/modmanager_dynamic.cpp b/src/modmanager_dynamic.cpp index 9935fb61a..a153accbb 100644 --- a/src/modmanager_dynamic.cpp +++ b/src/modmanager_dynamic.cpp @@ -65,6 +65,7 @@ bool ModuleManager::Load(const std::string& filename, bool defer) { newmod->ModuleSourceFile = filename; newmod->ModuleDLLManager = newhandle; + newmod->dying = false; Modules[filename] = newmod; std::string version = newhandle->GetVersion(); if (defer) diff --git a/src/modmanager_static.cpp b/src/modmanager_static.cpp index 23f30e1df..4c4624195 100644 --- a/src/modmanager_static.cpp +++ b/src/modmanager_static.cpp @@ -93,6 +93,7 @@ bool ModuleManager::Load(const std::string& name, bool defer) mod = (*it->second->init)(); mod->ModuleSourceFile = name; mod->ModuleDLLManager = NULL; + mod->dying = false; Modules[name] = mod; if (defer) { @@ -183,6 +184,7 @@ void ModuleManager::LoadAll() { Load("cmd_all", true); Load("cmd_whowas.so", true); + Load("cmd_lusers.so", true); ConfigTagList tags = ServerInstance->Config->ConfTags("module"); for(ConfigIter i = tags.first; i != tags.second; ++i) diff --git a/src/modules.cpp b/src/modules.cpp index c9118d1a7..63e1118d9 100644 --- a/src/modules.cpp +++ b/src/modules.cpp @@ -327,7 +327,7 @@ bool ModuleManager::CanUnload(Module* mod) { std::map<std::string, Module*>::iterator modfind = Modules.find(mod->ModuleSourceFile); - if (modfind == Modules.end() || modfind->second != mod) + if ((modfind == Modules.end()) || (modfind->second != mod) || (mod->dying)) { LastModuleError = "Module " + mod->ModuleSourceFile + " is not loaded, cannot unload it!"; ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError); @@ -339,6 +339,8 @@ bool ModuleManager::CanUnload(Module* mod) ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError); return false; } + + mod->dying = true; return true; } diff --git a/src/modules/extra/m_ssl_gnutls.cpp b/src/modules/extra/m_ssl_gnutls.cpp index 1caacaa0f..00dff68e6 100644 --- a/src/modules/extra/m_ssl_gnutls.cpp +++ b/src/modules/extra/m_ssl_gnutls.cpp @@ -86,6 +86,12 @@ static ssize_t gnutls_pull_wrapper(gnutls_transport_ptr_t user_wrap, void* buffe return -1; } int rv = ServerInstance->SE->Recv(user, reinterpret_cast<char *>(buffer), size, 0); + if (rv < 0) + { + /* On Windows we need to set errno for gnutls */ + if (SocketEngine::IgnoreError()) + errno = EAGAIN; + } if (rv < (int)size) ServerInstance->SE->ChangeEventMask(user, FD_READ_WILL_BLOCK); return rv; @@ -100,6 +106,12 @@ static ssize_t gnutls_push_wrapper(gnutls_transport_ptr_t user_wrap, const void* return -1; } int rv = ServerInstance->SE->Send(user, reinterpret_cast<const char *>(buffer), size, 0); + if (rv < 0) + { + /* On Windows we need to set errno for gnutls */ + if (SocketEngine::IgnoreError()) + errno = EAGAIN; + } if (rv < (int)size) ServerInstance->SE->ChangeEventMask(user, FD_WRITE_WILL_BLOCK); return rv; @@ -322,6 +334,7 @@ class ModuleSSLGnuTLS : public Module { gnutls_dh_params_deinit(dh_params); dh_alloc = false; + dh_params = NULL; } if (cred_alloc) @@ -409,10 +422,30 @@ class ModuleSSLGnuTLS : public Module ret = gnutls_dh_params_init(&dh_params); dh_alloc = (ret >= 0); if (!dh_alloc) - ServerInstance->Logs->Log("m_ssl_gnutls",LOG_DEFAULT, "m_ssl_gnutls.so: Failed to initialise DH parameters: %s", gnutls_strerror(ret)); + { + ServerInstance->Logs->Log("m_ssl_gnutls", LOG_DEFAULT, "m_ssl_gnutls.so: Failed to initialise DH parameters: %s", gnutls_strerror(ret)); + return; + } - // This may be on a large (once a day or week) timer eventually. - GenerateDHParams(); + std::string dhfile = Conf->getString("dhfile"); + if (!dhfile.empty()) + { + // Try to load DH params from file + reader.LoadFile(dhfile); + std::string dhstring = reader.Contents(); + gnutls_datum_t dh_datum = { (unsigned char*)dhstring.data(), static_cast<unsigned int>(dhstring.length()) }; + + if ((ret = gnutls_dh_params_import_pkcs3(dh_params, &dh_datum, GNUTLS_X509_FMT_PEM)) < 0) + { + // File unreadable or GnuTLS was unhappy with the contents, generate the DH primes now + ServerInstance->Logs->Log("m_ssl_gnutls", LOG_DEFAULT, "m_ssl_gnutls.so: Generating DH parameters because I failed to load them from file '%s': %s", dhfile.c_str(), gnutls_strerror(ret)); + GenerateDHParams(); + } + } + else + { + GenerateDHParams(); + } } void GenerateDHParams() diff --git a/src/modules/m_callerid.cpp b/src/modules/m_callerid.cpp index e8448bc91..3bc9583cc 100644 --- a/src/modules/m_callerid.cpp +++ b/src/modules/m_callerid.cpp @@ -38,26 +38,6 @@ class callerid_data std::list<callerid_data *> wholistsme; callerid_data() : lastnotify(0) { } - callerid_data(const std::string& str) - { - irc::commasepstream s(str); - std::string tok; - if (s.GetToken(tok)) - { - lastnotify = ConvToInt(tok); - } - while (s.GetToken(tok)) - { - if (tok.empty()) - { - continue; - } - - User *u = ServerInstance->FindNick(tok); - if ((u) && (u->registered == REG_ALL) && (!u->quitting) && (!IS_SERVER(u))) - accepting.insert(u); - } - } std::string ToString(SerializeFormat format) const { @@ -88,8 +68,31 @@ struct CallerIDExtInfo : public ExtensionItem void unserialize(SerializeFormat format, Extensible* container, const std::string& value) { - callerid_data* dat = new callerid_data(value); - set_raw(container, dat); + callerid_data* dat = new callerid_data; + irc::commasepstream s(value); + std::string tok; + if (s.GetToken(tok)) + dat->lastnotify = ConvToInt(tok); + + while (s.GetToken(tok)) + { + if (tok.empty()) + continue; + + User *u = ServerInstance->FindNick(tok); + if ((u) && (u->registered == REG_ALL) && (!u->quitting) && (!IS_SERVER(u))) + { + if (dat->accepting.insert(u).second) + { + callerid_data* other = this->get(u, true); + other->wholistsme.push_back(dat); + } + } + } + + void* old = set_raw(container, dat); + if (old) + this->free(old); } callerid_data* get(User* user, bool create) @@ -113,11 +116,16 @@ struct CallerIDExtInfo : public ExtensionItem callerid_data *targ = this->get(*it, false); if (!targ) + { + ServerInstance->Logs->Log("m_callerid", LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (1)"); continue; // shouldn't happen, but oh well. + } std::list<callerid_data*>::iterator it2 = std::find(targ->wholistsme.begin(), targ->wholistsme.end(), dat); if (it2 != targ->wholistsme.end()) targ->wholistsme.erase(it2); + else + ServerInstance->Logs->Log("m_callerid", LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (2)"); } delete dat; } @@ -277,6 +285,7 @@ public: if (!dat2) { // How the fuck is this possible. + ServerInstance->Logs->Log("m_callerid", LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (3)"); return false; } @@ -284,6 +293,9 @@ public: if (it != dat2->wholistsme.end()) // Found me! dat2->wholistsme.erase(it); + else + ServerInstance->Logs->Log("m_callerid", LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (4)"); + user->WriteServ("NOTICE %s :%s is no longer on your accept list", user->nick.c_str(), whotoremove->nick.c_str()); return true; @@ -320,6 +332,8 @@ class ModuleCallerID : public Module if (it2 != dat->accepting.end()) dat->accepting.erase(it2); + else + ServerInstance->Logs->Log("m_callerid", LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (5)"); } userdata->wholistsme.clear(); @@ -354,7 +368,7 @@ public: ModResult PreText(User* user, User* dest, std::string& text) { - if (!dest->IsModeSet('g')) + if (!dest->IsModeSet('g') || (user == dest)) return MOD_RES_PASSTHRU; if (operoverride && user->IsOper()) diff --git a/src/modules/m_filter.cpp b/src/modules/m_filter.cpp index 027222b3a..8bfefd6eb 100644 --- a/src/modules/m_filter.cpp +++ b/src/modules/m_filter.cpp @@ -165,19 +165,22 @@ class ImplFilter : public FilterResult class ModuleFilter : public Module { + bool initing; + RegexFactory* factory; + void FreeFilters(); + public: CommandFilter filtcommand; dynamic_reference<RegexFactory> RegexEngine; std::vector<ImplFilter> filters; - const char *error; - int erroffset; int flags; std::set<std::string> exemptfromfilter; // List of channel names excluded from filtering. ModuleFilter(); void init(); + CullResult cull(); ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list); FilterResult* FilterMatch(User* user, const std::string &text, int flags); bool DeleteFilter(const std::string &freeform); @@ -191,6 +194,7 @@ class ModuleFilter : public Module void OnDecodeMetaData(Extensible* target, const std::string &extname, const std::string &extdata); ModResult OnStats(char symbol, User* user, string_list &results); ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line); + void OnUnloadModule(Module* mod); bool AppliesToMe(User* user, FilterResult* filter, int flags); void ReadFilters(); static bool StringToFilterAction(const std::string& str, FilterAction& fa); @@ -292,18 +296,33 @@ bool ModuleFilter::AppliesToMe(User* user, FilterResult* filter, int iflags) return true; } -ModuleFilter::ModuleFilter() : filtcommand(this), RegexEngine(this, "regex") +ModuleFilter::ModuleFilter() + : initing(true), filtcommand(this), RegexEngine(this, "regex") { } void ModuleFilter::init() { ServerInstance->Modules->AddService(filtcommand); - Implementation eventlist[] = { I_OnPreCommand, I_OnStats, I_OnSyncNetwork, I_OnDecodeMetaData, I_OnUserPreMessage, I_OnUserPreNotice, I_OnRehash }; + Implementation eventlist[] = { I_OnPreCommand, I_OnStats, I_OnSyncNetwork, I_OnDecodeMetaData, I_OnUserPreMessage, I_OnUserPreNotice, I_OnRehash, I_OnUnloadModule }; ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation)); OnRehash(NULL); } +CullResult ModuleFilter::cull() +{ + FreeFilters(); + return Module::cull(); +} + +void ModuleFilter::FreeFilters() +{ + for (std::vector<ImplFilter>::const_iterator i = filters.begin(); i != filters.end(); ++i) + delete i->regex; + + filters.clear(); +} + ModResult ModuleFilter::OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { if (!IS_LOCAL(user)) @@ -457,20 +476,35 @@ void ModuleFilter::OnRehash(User* user) if (!chan.empty()) exemptfromfilter.insert(chan); } - std::string newrxengine = "regex/" + ServerInstance->Config->ConfValue("filteropts")->getString("engine"); - if (newrxengine == "regex/") - newrxengine = "regex"; - if (RegexEngine.GetProvider() == newrxengine) - return; - //ServerInstance->SNO->WriteGlobalSno('a', "Dumping all filters due to regex engine change (was '%s', now '%s')", RegexEngine.GetProvider().c_str(), newrxengine.c_str()); - //ServerInstance->XLines->DelAll("R"); + std::string newrxengine = ServerInstance->Config->ConfValue("filteropts")->getString("engine"); + + factory = RegexEngine ? (RegexEngine.operator->()) : NULL; + + if (newrxengine.empty()) + RegexEngine.SetProvider("regex"); + else + RegexEngine.SetProvider("regex/" + newrxengine); - RegexEngine.SetProvider(newrxengine); if (!RegexEngine) { - ServerInstance->SNO->WriteGlobalSno('a', "WARNING: Regex engine '%s' is not loaded - Filter functionality disabled until this is corrected.", newrxengine.c_str()); + if (newrxengine.empty()) + ServerInstance->SNO->WriteGlobalSno('a', "WARNING: No regex engine loaded - Filter functionality disabled until this is corrected."); + else + ServerInstance->SNO->WriteGlobalSno('a', "WARNING: Regex engine '%s' is not loaded - Filter functionality disabled until this is corrected.", newrxengine.c_str()); + + initing = false; + FreeFilters(); + return; + } + + if ((!initing) && (RegexEngine.operator->() != factory)) + { + ServerInstance->SNO->WriteGlobalSno('a', "Dumping all filters due to regex engine change"); + FreeFilters(); } + + initing = false; ReadFilters(); } @@ -697,4 +731,18 @@ ModResult ModuleFilter::OnStats(char symbol, User* user, string_list &results) return MOD_RES_PASSTHRU; } +void ModuleFilter::OnUnloadModule(Module* mod) +{ + // If the regex engine became unavailable or has changed, remove all filters + if (!RegexEngine) + { + FreeFilters(); + } + else if (RegexEngine.operator->() != factory) + { + factory = RegexEngine.operator->(); + FreeFilters(); + } +} + MODULE_INIT(ModuleFilter) diff --git a/src/modules/m_ident.cpp b/src/modules/m_ident.cpp index f38ad2241..2b5a512c9 100644 --- a/src/modules/m_ident.cpp +++ b/src/modules/m_ident.cpp @@ -367,6 +367,7 @@ class ModuleIdent : public Module user->WriteServ("NOTICE Auth :*** Found your ident, '%s'", user->ident.c_str()); } + user->InvalidateCache(); isock->Close(); ext.unset(user); return MOD_RES_PASSTHRU; diff --git a/src/modules/m_kicknorejoin.cpp b/src/modules/m_kicknorejoin.cpp index 5f19e5d0c..2f0c15bf3 100644 --- a/src/modules/m_kicknorejoin.cpp +++ b/src/modules/m_kicknorejoin.cpp @@ -27,31 +27,46 @@ /* $ModDesc: Provides channel mode +J (delay rejoin after kick) */ -typedef std::map<User*, time_t> delaylist; +typedef std::map<std::string, time_t> delaylist; /** Handles channel mode +J */ -class KickRejoin : public ParamChannelModeHandler +class KickRejoin : public ModeHandler { + static const unsigned int max = 60; public: SimpleExtItem<delaylist> ext; - KickRejoin(Module* Creator) : ParamChannelModeHandler(Creator, "kicknorejoin", 'J'), ext("norejoinusers", Creator) { } - - bool ParamValidate(std::string& parameter) + KickRejoin(Module* Creator) + : ModeHandler(Creator, "kicknorejoin", 'J', PARAM_SETONLY, MODETYPE_CHANNEL) + , ext("norejoinusers", Creator) { - int v = atoi(parameter.c_str()); - if (v <= 0) - return false; - parameter = ConvToStr(v); - return true; } - ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding) + ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) { - ModeAction rv = ParamChannelModeHandler::OnModeChange(source, dest, channel, parameter, adding); - if (rv == MODEACTION_ALLOW && !adding) + if (adding) + { + int v = ConvToInt(parameter); + if (v <= 0) + return MODEACTION_DENY; + if (parameter == channel->GetModeParameter(this)) + return MODEACTION_DENY; + + if ((IS_LOCAL(source) && ((unsigned int)v > max))) + v = max; + + parameter = ConvToStr(v); + channel->SetModeParam(this, parameter); + } + else + { + if (!channel->IsModeSet(this)) + return MODEACTION_DENY; + ext.unset(channel); - return rv; + channel->SetModeParam(this, ""); + } + return MODEACTION_ALLOW; } }; @@ -80,30 +95,26 @@ public: delaylist* dl = kr.ext.get(chan); if (dl) { - std::vector<User*> itemstoremove; - - for (delaylist::iterator iter = dl->begin(); iter != dl->end(); iter++) + for (delaylist::iterator iter = dl->begin(); iter != dl->end(); ) { if (iter->second > ServerInstance->Time()) { - if (iter->first == user) + if (iter->first == user->uuid) { std::string modeparam = chan->GetModeParameter(&kr); user->WriteNumeric(ERR_DELAYREJOIN, "%s %s :You must wait %s seconds after being kicked to rejoin (+J)", user->nick.c_str(), chan->name.c_str(), modeparam.c_str()); return MOD_RES_DENY; } + ++iter; } else { // Expired record, remove. - itemstoremove.push_back(iter->first); + dl->erase(iter++); } } - for (unsigned int i = 0; i < itemstoremove.size(); i++) - dl->erase(itemstoremove[i]); - if (!dl->size()) kr.ext.unset(chan); } @@ -113,7 +124,7 @@ public: void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts) { - if (memb->chan->IsModeSet(&kr) && (source != memb->user)) + if (memb->chan->IsModeSet(&kr) && (IS_LOCAL(memb->user)) && (source != memb->user)) { delaylist* dl = kr.ext.get(memb->chan); if (!dl) @@ -121,7 +132,7 @@ public: dl = new delaylist; kr.ext.set(memb->chan, dl); } - (*dl)[memb->user] = ServerInstance->Time() + ConvToInt(memb->chan->GetModeParameter(&kr)); + (*dl)[memb->user->uuid] = ServerInstance->Time() + ConvToInt(memb->chan->GetModeParameter(&kr)); } } diff --git a/src/modules/m_nonotice.cpp b/src/modules/m_nonotice.cpp index 89e6be3b7..3a2f0814f 100644 --- a/src/modules/m_nonotice.cpp +++ b/src/modules/m_nonotice.cpp @@ -59,11 +59,6 @@ class ModuleNoNotice : public Module Channel* c = (Channel*)dest; if (!c->GetExtBanStatus(user, 'T').check(!c->IsModeSet('T'))) { - if (ServerInstance->ULine(user->server)) - { - // ulines are exempt. - return MOD_RES_PASSTHRU; - } res = ServerInstance->OnCheckExemption(user,c,"nonotice"); if (res == MOD_RES_ALLOW) return MOD_RES_PASSTHRU; diff --git a/src/modules/m_permchannels.cpp b/src/modules/m_permchannels.cpp index 27026bde9..6a8694a1c 100644 --- a/src/modules/m_permchannels.cpp +++ b/src/modules/m_permchannels.cpp @@ -172,14 +172,6 @@ public: ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation)); OnRehash(NULL); - - // Load only when there are no linked servers - we set the TS of the channels we - // create to the current time, this can lead to desync because spanningtree has - // no way of knowing what we do - ProtoServerList serverlist; - ServerInstance->PI->GetServerList(serverlist); - if (serverlist.size() < 2) - LoadDatabase(); } CullResult cull() @@ -300,6 +292,38 @@ public: dirty = false; } + void Prioritize() + { + // XXX: Load the DB here because the order in which modules are init()ed at boot is + // alphabetical, this means we must wait until all modules have done their init() + // to be able to set the modes they provide (e.g.: m_stripcolor is inited after us) + // Prioritize() is called after all module initialization is complete, consequently + // all modes are available now + + static bool loaded = false; + if (loaded) + return; + + loaded = true; + + // Load only when there are no linked servers - we set the TS of the channels we + // create to the current time, this can lead to desync because spanningtree has + // no way of knowing what we do + ProtoServerList serverlist; + ServerInstance->PI->GetServerList(serverlist); + if (serverlist.size() < 2) + { + try + { + LoadDatabase(); + } + catch (CoreException& e) + { + ServerInstance->Logs->Log("m_permchannels", LOG_DEFAULT, "Error loading permchannels database: " + std::string(e.GetReason())); + } + } + } + virtual Version GetVersion() { return Version("Provides support for channel mode +P to provide permanent channels",VF_VENDOR); diff --git a/src/modules/m_rline.cpp b/src/modules/m_rline.cpp index c4439cbfb..d164f5fef 100644 --- a/src/modules/m_rline.cpp +++ b/src/modules/m_rline.cpp @@ -214,9 +214,13 @@ class ModuleRLine : public Module RLineFactory f; CommandRLine r; bool MatchOnNickChange; + bool initing; + RegexFactory* factory; public: - ModuleRLine() : rxfactory(this, "regex"), f(rxfactory), r(this, f) + ModuleRLine() + : rxfactory(this, "regex"), f(rxfactory), r(this, f) + , initing(true) { } @@ -227,7 +231,7 @@ class ModuleRLine : public Module ServerInstance->Modules->AddService(r); ServerInstance->XLines->RegisterFactory(&f); - Implementation eventlist[] = { I_OnUserConnect, I_OnRehash, I_OnUserPostNick, I_OnStats, I_OnBackgroundTimer }; + Implementation eventlist[] = { I_OnUserRegister, I_OnRehash, I_OnUserPostNick, I_OnStats, I_OnBackgroundTimer, I_OnUnloadModule }; ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation)); } @@ -242,7 +246,7 @@ class ModuleRLine : public Module return Version("RLINE: Regexp user banning.", VF_COMMON | VF_VENDOR, rxfactory ? rxfactory->name : ""); } - virtual void OnUserConnect(LocalUser* user) + ModResult OnUserRegister(LocalUser* user) { // Apply lines on user connect XLine *rl = ServerInstance->XLines->MatchesLine("R", user); @@ -251,7 +255,9 @@ class ModuleRLine : public Module { // Bang. :P rl->Apply(user); + return MOD_RES_DENY; } + return MOD_RES_PASSTHRU; } virtual void OnRehash(User *user) @@ -262,14 +268,29 @@ class ModuleRLine : public Module ZlineOnMatch = tag->getBool("zlineonmatch"); std::string newrxengine = tag->getString("engine"); + factory = rxfactory ? (rxfactory.operator->()) : NULL; + if (newrxengine.empty()) rxfactory.SetProvider("regex"); else rxfactory.SetProvider("regex/" + newrxengine); + if (!rxfactory) { - ServerInstance->SNO->WriteToSnoMask('a', "WARNING: Regex engine '%s' is not loaded - R-Line functionality disabled until this is corrected.", newrxengine.c_str()); + if (newrxengine.empty()) + ServerInstance->SNO->WriteToSnoMask('a', "WARNING: No regex engine loaded - R-Line functionality disabled until this is corrected."); + else + ServerInstance->SNO->WriteToSnoMask('a', "WARNING: Regex engine '%s' is not loaded - R-Line functionality disabled until this is corrected.", newrxengine.c_str()); + + ServerInstance->XLines->DelAll(f.GetType()); } + else if ((!initing) && (rxfactory.operator->() != factory)) + { + ServerInstance->SNO->WriteToSnoMask('a', "Regex engine has changed, removing all R-Lines"); + ServerInstance->XLines->DelAll(f.GetType()); + } + + initing = false; } virtual ModResult OnStats(char symbol, User* user, string_list &results) @@ -307,6 +328,25 @@ class ModuleRLine : public Module } } + void OnUnloadModule(Module* mod) + { + // If the regex engine became unavailable or has changed, remove all rlines + if (!rxfactory) + { + ServerInstance->XLines->DelAll(f.GetType()); + } + else if (rxfactory.operator->() != factory) + { + factory = rxfactory.operator->(); + ServerInstance->XLines->DelAll(f.GetType()); + } + } + + void Prioritize() + { + Module* mod = ServerInstance->Modules->Find("m_cgiirc.so"); + ServerInstance->Modules->SetPriority(this, I_OnUserRegister, PRIORITY_AFTER, mod); + } }; MODULE_INIT(ModuleRLine) diff --git a/src/modules/m_services_account.cpp b/src/modules/m_services_account.cpp index 7c4eb5f0b..81ff3e4c3 100644 --- a/src/modules/m_services_account.cpp +++ b/src/modules/m_services_account.cpp @@ -37,7 +37,7 @@ class Channel_r : public ModeHandler ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding) { // only a u-lined server may add or remove the +r mode. - if (!IS_LOCAL(source) || ServerInstance->ULine(source->server)) + if (!IS_LOCAL(source)) { // Only change the mode if it's not redundant if ((adding != channel->IsModeSet('r'))) @@ -64,7 +64,7 @@ class User_r : public ModeHandler ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding) { - if (!IS_LOCAL(source) || ServerInstance->ULine(source->server)) + if (!IS_LOCAL(source)) { if ((adding != dest->IsModeSet('r'))) { @@ -171,12 +171,6 @@ class ModuleServicesAccount : public Module std::string *account = accountname.get(user); bool is_registered = account && !account->empty(); - if ((ServerInstance->ULine(user->nick.c_str())) || (ServerInstance->ULine(user->server))) - { - // user is ulined, can speak regardless - return MOD_RES_PASSTHRU; - } - if (target_type == TYPE_CHANNEL) { Channel* c = (Channel*)dest; @@ -252,12 +246,6 @@ class ModuleServicesAccount : public Module if (chan) { - if ((ServerInstance->ULine(user->nick.c_str())) || (ServerInstance->ULine(user->server))) - { - // user is ulined, won't be stopped from joining - return MOD_RES_PASSTHRU; - } - if (chan->IsModeSet('R')) { if (!is_registered) diff --git a/src/modules/m_spanningtree/main.cpp b/src/modules/m_spanningtree/main.cpp index 018fcece7..1fa7ef76b 100644 --- a/src/modules/m_spanningtree/main.cpp +++ b/src/modules/m_spanningtree/main.cpp @@ -731,7 +731,7 @@ void ModuleSpanningTree::OnRehash(User* user) std::string msg = "Error in configuration: "; msg.append(e.GetReason()); ServerInstance->SNO->WriteToSnoMask('l', msg); - if (!IS_LOCAL(user)) + if (user && !IS_LOCAL(user)) ServerInstance->PI->SendSNONotice("L", msg); } } diff --git a/src/modules/m_spanningtree/server.cpp b/src/modules/m_spanningtree/server.cpp index 19e2d53a6..85204ccaa 100644 --- a/src/modules/m_spanningtree/server.cpp +++ b/src/modules/m_spanningtree/server.cpp @@ -125,8 +125,9 @@ bool TreeSocket::Outbound_Reply_Server(parameterlist ¶ms) TreeServer* CheckDupe = Utils->FindServer(sname); if (CheckDupe) { - this->SendError("Server "+sname+" already exists on server "+CheckDupe->GetParent()->GetName()+"!"); - ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName()); + std::string pname = CheckDupe->GetParent() ? CheckDupe->GetParent()->GetName() : "<ourself>"; + SendError("Server "+sname+" already exists on server "+pname+"!"); + ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+pname); return false; } CheckDupe = Utils->FindServer(sid); @@ -168,6 +169,33 @@ bool TreeSocket::Outbound_Reply_Server(parameterlist ¶ms) return false; } +bool TreeSocket::CheckDuplicate(const std::string& sname, const std::string& sid) +{ + /* Check for fully initialized instances of the server by name */ + TreeServer* CheckDupe = Utils->FindServer(sname); + if (CheckDupe) + { + std::string pname = CheckDupe->GetParent() ? CheckDupe->GetParent()->GetName() : "<ourself>"; + SendError("Server "+sname+" already exists on server "+pname+"!"); + ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+pname); + return false; + } + + /* Check for fully initialized instances of the server by id */ + ServerInstance->Logs->Log("m_spanningtree", LOG_DEBUG, "Looking for dupe SID %s", sid.c_str()); + CheckDupe = Utils->FindServerID(sid); + + if (CheckDupe) + { + this->SendError("Server ID "+CheckDupe->GetID()+" already exists on server "+CheckDupe->GetName()+"! You may want to specify the server ID for the server manually with <server:id> so they do not conflict."); + ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server ID '"+CheckDupe->GetID()+ + "' already exists on server "+CheckDupe->GetName()); + return false; + } + + return true; +} + /* * Someone else is attempting to connect to us if this is called. Validate their credentials etc. * -- w @@ -206,39 +234,24 @@ bool TreeSocket::Inbound_Server(parameterlist ¶ms) continue; } - /* Now check for fully initialized ServerInstances of the server by name */ - TreeServer* CheckDupe = Utils->FindServer(sname); - if (CheckDupe) - { - std::string pname = CheckDupe->GetParent() ? CheckDupe->GetParent()->GetName() : "<ourself>"; - SendError("Server "+sname+" already exists on server "+pname+"!"); - ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+pname); + if (!CheckDuplicate(sname, sid)) return false; - } - /* Check for fully initialized instances of the server by id */ - ServerInstance->Logs->Log("m_spanningtree",LOG_DEBUG,"Looking for dupe SID %s", sid.c_str()); - CheckDupe = Utils->FindServerID(sid); + ServerInstance->SNO->WriteToSnoMask('l',"Verified incoming server connection " + linkID + " ("+description+")"); - if (CheckDupe) - { - this->SendError("Server ID "+CheckDupe->GetID()+" already exists on server "+CheckDupe->GetName()+"! You may want to specify the server ID for the server manually with <server:id> so they do not conflict."); - ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server ID '"+CheckDupe->GetID()+ - "' already exists on server "+CheckDupe->GetName()); - return false; - } + this->SendCapabilities(2); - ServerInstance->SNO->WriteToSnoMask('l',"Verified incoming server connection " + linkID + " ("+description+")"); - linkID = sname; + // Save these for later, so when they accept our credentials (indicated by BURST) we remember them + this->capab->hidden = x->Hidden; + this->capab->sid = sid; + this->capab->description = description; + this->capab->name = sname; - // this is good. Send our details: Our server name and description and hopcount of 0, + // Send our details: Our server name and description and hopcount of 0, // along with the sendpass from this block. - this->SendCapabilities(2); this->WriteLine("SERVER "+ServerInstance->Config->ServerName+" "+this->MakePass(x->SendPass, this->GetTheirChallenge())+" 0 "+ServerInstance->Config->GetSID()+" :"+ServerInstance->Config->ServerDesc); - // move to the next state, we are now waiting for THEM. - MyRoot = new TreeServer(Utils, sname, description, sid, Utils->TreeRoot, this, x->Hidden); - Utils->TreeRoot->AddChild(MyRoot); + // move to the next state, we are now waiting for THEM. this->LinkState = WAIT_AUTH_2; return true; } diff --git a/src/modules/m_spanningtree/treesocket.h b/src/modules/m_spanningtree/treesocket.h index b6230a6a5..0a519041c 100644 --- a/src/modules/m_spanningtree/treesocket.h +++ b/src/modules/m_spanningtree/treesocket.h @@ -75,6 +75,12 @@ struct CapabData int capab_phase; /* Have sent CAPAB already */ bool auth_fingerprint; /* Did we auth using SSL fingerprint */ bool auth_challenge; /* Did we auth using challenge/response */ + + // Data saved from incoming SERVER command, for later use when our credentials have been accepted by the other party + std::string description; + std::string sid; + std::string name; + bool hidden; }; /** Every SERVER connection inbound or outbound is represented by an object of @@ -92,6 +98,11 @@ class TreeSocket : public BufferedSocket bool LastPingWasGood; /* Responded to last ping we sent? */ int proto_version; /* Remote protocol version */ bool ConnectionFailureShown; /* Set to true if a connection failure message was shown */ + + /** Checks if the given servername and sid are both free + */ + bool CheckDuplicate(const std::string& servername, const std::string& sid); + public: time_t age; diff --git a/src/modules/m_spanningtree/treesocket2.cpp b/src/modules/m_spanningtree/treesocket2.cpp index 1504a8807..fdd28a734 100644 --- a/src/modules/m_spanningtree/treesocket2.cpp +++ b/src/modules/m_spanningtree/treesocket2.cpp @@ -160,9 +160,21 @@ void TreeSocket::ProcessLine(std::string &line) ServerInstance->SNO->WriteGlobalSno('l',"\2WARNING\2: Your clocks are out by %d seconds. Please consider synching your clocks.", abs((long)delta)); } } + + // Check for duplicate server name/sid again, it's possible that a new + // server was introduced while we were waiting for them to send BURST. + // (we do not reserve their server name/sid when they send SERVER, we do it now) + if (!CheckDuplicate(capab->name, capab->sid)) + return; + this->LinkState = CONNECTED; Utils->timeoutlist.erase(this); + linkID = capab->name; + + MyRoot = new TreeServer(Utils, capab->name, capab->description, capab->sid, Utils->TreeRoot, this, capab->hidden); + Utils->TreeRoot->AddChild(MyRoot); + MyRoot->bursting = true; this->DoBurst(MyRoot); diff --git a/src/socketengine.cpp b/src/socketengine.cpp index 44f65e1d5..80e9eaed9 100644 --- a/src/socketengine.cpp +++ b/src/socketengine.cpp @@ -166,12 +166,7 @@ int SocketEngine::NonBlocking(int fd) void SocketEngine::SetReuse(int fd) { int on = 1; - struct linger linger = { 0, 0 }; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)); - /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */ - linger.l_onoff = 1; - linger.l_linger = 1; - setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger)); } int SocketEngine::RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, sockaddr *from, socklen_t *fromlen) diff --git a/src/usermanager.cpp b/src/usermanager.cpp index f1a3c0183..78ec673b9 100644 --- a/src/usermanager.cpp +++ b/src/usermanager.cpp @@ -63,7 +63,7 @@ void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs this->unregistered_count++; /* The users default nick is their UUID */ - New->nick.assign(New->uuid, 0, ServerInstance->Config->Limits.NickMax); + New->nick = New->uuid; (*(this->clientlist))[New->nick] = New; New->registered = REG_NONE; @@ -153,13 +153,13 @@ void UserManager::QuitUser(User *user, const std::string &quitreason, const char { if (user->quitting) { - ServerInstance->Logs->Log("CULLLIST",LOG_DEBUG, "*** Warning *** - You tried to quit a user (%s) twice. Did your module call QuitUser twice?", user->nick.c_str()); + ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Tried to quit quitting user: " + user->nick); return; } if (IS_SERVER(user)) { - ServerInstance->Logs->Log("CULLLIST",LOG_DEBUG, "*** Warning *** - You tried to quit a fake user (%s)", user->nick.c_str()); + ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Tried to quit server user: " + user->nick); return; } @@ -224,7 +224,7 @@ void UserManager::QuitUser(User *user, const std::string &quitreason, const char if (iter != this->clientlist->end()) this->clientlist->erase(iter); else - ServerInstance->Logs->Log("USERS", LOG_DEBUG, "iter == clientlist->end, can't remove them from hash... problematic.."); + ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Nick not found in clientlist, cannot remove: " + user->nick); ServerInstance->Users->uuidlist->erase(user->uuid); } diff --git a/src/users.cpp b/src/users.cpp index 623af7fe3..cdc6beed2 100644 --- a/src/users.cpp +++ b/src/users.cpp @@ -416,6 +416,7 @@ void UserIOHandler::OnDataReady() ServerInstance->Users->QuitUser(user, "RecvQ exceeded"); ServerInstance->SNO->WriteToSnoMask('a', "User %s RecvQ of %lu exceeds connect class maximum of %lu", user->nick.c_str(), (unsigned long)recvq.length(), user->MyClass->GetRecvqMax()); + return; } unsigned long sendqmax = ULONG_MAX; if (!user->HasPrivPermission("users/flood/increased-buffers")) @@ -506,6 +507,8 @@ CullResult LocalUser::cull() // is only a precaution currently. if (localuseriter != ServerInstance->Users->local_users.end()) ServerInstance->Users->local_users.erase(localuseriter); + else + ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: LocalUserIter does not point to a valid entry for " + this->nick); ClearInvites(); eh.cull(); @@ -786,6 +789,12 @@ void User::InvalidateCache() bool User::ChangeNick(const std::string& newnick, bool force) { + if (quitting) + { + ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Attempted to change nick of a quitting user: " + this->nick); + return false; + } + ModResult MOD_RESULT; if (force) |