From 850b7a3ace862101a944a9332d72b6bd597c17cc Mon Sep 17 00:00:00 2001 From: Peter Powell Date: Sun, 21 Jul 2019 13:44:32 +0100 Subject: [PATCH] Allow modules to prevent a failed connection from being closed. --- include/modules.h | 10 +++++++++- src/modules.cpp | 1 + src/usermanager.cpp | 10 ++++++++++ src/users.cpp | 7 +++++-- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/include/modules.h b/include/modules.h index 81664d364..f5af969de 100644 --- a/include/modules.h +++ b/include/modules.h @@ -223,7 +223,7 @@ enum Implementation I_OnUserPostNick, I_OnPreMode, I_On005Numeric, I_OnKill, I_OnLoadModule, I_OnUnloadModule, I_OnBackgroundTimer, I_OnPreCommand, I_OnCheckReady, I_OnCheckInvite, I_OnRawMode, I_OnCheckKey, I_OnCheckLimit, I_OnCheckBan, I_OnCheckChannelBan, I_OnExtBanCheck, - I_OnPreChangeHost, I_OnPreTopicChange, + I_OnPreChangeHost, I_OnPreTopicChange, I_OnConnectionFail, I_OnPostTopicChange, I_OnPostConnect, I_OnPostDeoper, I_OnPreChangeRealName, I_OnUserRegister, I_OnChannelPreDelete, I_OnChannelDelete, I_OnPostOper, I_OnPostCommand, I_OnPostJoin, @@ -942,6 +942,14 @@ class CoreExport Module : public classbase, public usecountbase * deny the message from being sent, or MOD_RES_PASSTHRU to let another module handle the event. */ virtual ModResult OnUserWrite(LocalUser* user, ClientProtocol::Message& msg); + + /** Called when a user connection has been unexpectedly disconnected. + * @param user The user who has been unexpectedly disconnected. + * @param error The type of error which caused this connection failure. + * @return MOD_RES_ALLOW to explicitly retain the user as a zombie, MOD_RES_DENY to explicitly + * disconnect the user, or MOD_RES_PASSTHRU to let another module handle the event. + */ + virtual ModResult OnConnectionFail(LocalUser* user, BufferedSocketError error); }; /** ModuleManager takes care of all things module-related diff --git a/src/modules.cpp b/src/modules.cpp index 3e268dae6..6f7ca2694 100644 --- a/src/modules.cpp +++ b/src/modules.cpp @@ -140,6 +140,7 @@ void Module::OnSetUserIP(LocalUser*) { DetachEvent(I_OnSetUserIP); } void Module::OnServiceAdd(ServiceProvider&) { DetachEvent(I_OnServiceAdd); } void Module::OnServiceDel(ServiceProvider&) { DetachEvent(I_OnServiceDel); } ModResult Module::OnUserWrite(LocalUser*, ClientProtocol::Message&) { DetachEvent(I_OnUserWrite); return MOD_RES_PASSTHRU; } +ModResult Module::OnConnectionFail(LocalUser*, BufferedSocketError) { DetachEvent(I_OnConnectionFail); return MOD_RES_PASSTHRU; } ServiceProvider::ServiceProvider(Module* Creator, const std::string& Name, ServiceType Type) : creator(Creator), name(Name), service(Type) diff --git a/src/usermanager.cpp b/src/usermanager.cpp index 6acd25ac5..4f65994aa 100644 --- a/src/usermanager.cpp +++ b/src/usermanager.cpp @@ -58,6 +58,16 @@ namespace // This user didn't answer the last ping, remove them. if (!user->lastping) { + ModResult res; + FIRST_MOD_RESULT(OnConnectionFail, res, (user, I_ERR_TIMEOUT)); + if (res == MOD_RES_ALLOW) + { + // A module is preventing this user from being timed out. + user->lastping = 1; + user->nextping = ServerInstance->Time() + user->MyClass->GetPingTime(); + return; + } + time_t secs = ServerInstance->Time() - (user->nextping - user->MyClass->GetPingTime()); const std::string message = "Ping timeout: " + ConvToStr(secs) + (secs != 1 ? " seconds" : " second"); ServerInstance->Users.QuitUser(user, message); diff --git a/src/users.cpp b/src/users.cpp index a3807bd75..c0dc69ff4 100644 --- a/src/users.cpp +++ b/src/users.cpp @@ -308,9 +308,12 @@ bool UserIOHandler::OnSetEndPoint(const irc::sockets::sockaddrs& server, const i return !user->quitting; } -void UserIOHandler::OnError(BufferedSocketError) +void UserIOHandler::OnError(BufferedSocketError error) { - ServerInstance->Users->QuitUser(user, getError()); + ModResult res; + FIRST_MOD_RESULT(OnConnectionFail, res, (user, error)); + if (res != MOD_RES_ALLOW) + ServerInstance->Users->QuitUser(user, getError()); } CullResult User::cull() -- 2.39.2