diff options
| author | 2019-12-08 17:38:47 +0000 | |
|---|---|---|
| committer | 2019-12-08 17:47:07 +0000 | |
| commit | 034dad6ab0df48172a70de70a9d0de4a9092112e (patch) | |
| tree | 0e852f3554dbce37d8e2e360fa099148155e8cab /src/modules | |
| parent | Move the nationalchars locale files to the docs directory. (diff) | |
| parent | Clean up the initialisation of the InspIRCd class. (diff) | |
Merge branch 'insp3' into master.
Diffstat (limited to 'src/modules')
| -rw-r--r-- | src/modules/extra/m_mysql.cpp | 112 | ||||
| -rw-r--r-- | src/modules/extra/m_ssl_gnutls.cpp | 1 | ||||
| -rw-r--r-- | src/modules/extra/m_ssl_mbedtls.cpp | 1 | ||||
| -rw-r--r-- | src/modules/extra/m_ssl_openssl.cpp | 1 | ||||
| -rw-r--r-- | src/modules/m_blockcolor.cpp | 8 | ||||
| -rw-r--r-- | src/modules/m_conn_umodes.cpp | 22 | ||||
| -rw-r--r-- | src/modules/m_conn_waitpong.cpp | 2 | ||||
| -rw-r--r-- | src/modules/m_ldapoper.cpp | 6 | ||||
| -rw-r--r-- | src/modules/m_nationalchars.cpp | 8 | ||||
| -rw-r--r-- | src/modules/m_noctcp.cpp | 6 | ||||
| -rw-r--r-- | src/modules/m_nokicks.cpp | 6 | ||||
| -rw-r--r-- | src/modules/m_nonicks.cpp | 8 | ||||
| -rw-r--r-- | src/modules/m_nonotice.cpp | 20 | ||||
| -rw-r--r-- | src/modules/m_ojoin.cpp | 6 | ||||
| -rw-r--r-- | src/modules/m_spanningtree/fjoin.cpp | 9 | ||||
| -rw-r--r-- | src/modules/m_timedbans.cpp | 8 | ||||
| -rw-r--r-- | src/modules/m_uninvite.cpp | 5 | ||||
| -rw-r--r-- | src/modules/m_websocket.cpp | 77 |
18 files changed, 183 insertions, 123 deletions
diff --git a/src/modules/extra/m_mysql.cpp b/src/modules/extra/m_mysql.cpp index 85bccc173..db5bfd018 100644 --- a/src/modules/extra/m_mysql.cpp +++ b/src/modules/extra/m_mysql.cpp @@ -83,24 +83,43 @@ class SQLConnection; class MySQLresult; class DispatcherThread; -struct QQueueItem +struct QueryQueueItem { - SQL::Query* q; - std::string query; - SQLConnection* c; - QQueueItem(SQL::Query* Q, const std::string& S, SQLConnection* C) : q(Q), query(S), c(C) {} + // An SQL database which this query is executed on. + SQLConnection* connection; + + // An object which handles the result of the query. + SQL::Query* query; + + // The SQL query which is to be executed. + std::string querystr; + + QueryQueueItem(SQL::Query* q, const std::string& s, SQLConnection* c) + : connection(c) + , query(q) + , querystr(s) + { + } }; -struct RQueueItem +struct ResultQueueItem { - SQL::Query* q; - MySQLresult* r; - RQueueItem(SQL::Query* Q, MySQLresult* R) : q(Q), r(R) {} + // An object which handles the result of the query. + SQL::Query* query; + + // The result returned from executing the MySQL query. + MySQLresult* result; + + ResultQueueItem(SQL::Query* q, MySQLresult* r) + : query(q) + , result(r) + { + } }; typedef insp::flat_map<std::string, SQLConnection*> ConnMap; -typedef std::deque<QQueueItem> QueryQueue; -typedef std::deque<RQueueItem> ResultQueue; +typedef std::deque<QueryQueueItem> QueryQueue; +typedef std::deque<ResultQueueItem> ResultQueue; /** MySQL module * */ @@ -131,10 +150,6 @@ class DispatcherThread : public SocketThread void OnNotify() override; }; -#if !defined(MYSQL_VERSION_ID) || MYSQL_VERSION_ID<32224 -#define mysql_field_count mysql_num_fields -#endif - /** Represents a mysql result set */ class MySQLresult : public SQL::Result @@ -146,7 +161,10 @@ class MySQLresult : public SQL::Result std::vector<std::string> colnames; std::vector<SQL::Row> fieldlists; - MySQLresult(MYSQL_RES* res, int affected_rows) : err(SQL::SUCCESS), currentrow(0), rows(0) + MySQLresult(MYSQL_RES* res, int affected_rows) + : err(SQL::SUCCESS) + , currentrow(0) + , rows(0) { if (affected_rows >= 1) { @@ -189,7 +207,10 @@ class MySQLresult : public SQL::Result } } - MySQLresult(SQL::Error& e) : err(e) + MySQLresult(SQL::Error& e) + : err(e) + , currentrow(0) + , rows(0) { } @@ -286,7 +307,7 @@ class SQLConnection : public SQL::Provider ~SQLConnection() { - Close(); + mysql_close(connection); } // This method connects to the database using the credentials supplied to the constructor, and returns @@ -365,21 +386,11 @@ class SQLConnection : public SQL::Provider return true; } - std::string GetError() - { - return mysql_error(connection); - } - - void Close() - { - mysql_close(connection); - } - void Submit(SQL::Query* q, const std::string& qs) override { ServerInstance->Logs.Log(MODNAME, LOG_DEBUG, "Executing MySQL query: " + qs); Parent()->Dispatcher->LockQueue(); - Parent()->qq.push_back(QQueueItem(q, qs, this)); + Parent()->qq.push_back(QueryQueueItem(q, qs, this)); Parent()->Dispatcher->UnlockQueueWakeup(); } @@ -422,8 +433,8 @@ class SQLConnection : public SQL::Provider }; ModuleSQL::ModuleSQL() + : Dispatcher(NULL) { - Dispatcher = NULL; } void ModuleSQL::init() @@ -488,10 +499,10 @@ void ModuleSQL::ReadConfig(ConfigStatus& status) for (size_t j = qq.size(); j > 0; j--) { size_t k = j - 1; - if (qq[k].c == i->second) + if (qq[k].connection == i->second) { - qq[k].q->OnError(err); - delete qq[k].q; + qq[k].query->OnError(err); + delete qq[k].query; qq.erase(qq.begin() + k); } } @@ -510,17 +521,16 @@ void ModuleSQL::OnUnloadModule(Module* mod) while (i > 0) { i--; - if (qq[i].q->creator == mod) + if (qq[i].query->creator == mod) { if (i == 0) { // need to wait until the query is done // (the result will be discarded) - qq[i].c->lock.lock(); - qq[i].c->lock.unlock(); + qq[i].connection->lock.lock(); } - qq[i].q->OnError(err); - delete qq[i].q; + qq[i].query->OnError(err); + delete qq[i].query; qq.erase(qq.begin() + i); } } @@ -541,23 +551,23 @@ void DispatcherThread::OnStart() { if (!Parent->qq.empty()) { - QQueueItem i = Parent->qq.front(); - i.c->lock.lock(); + QueryQueueItem i = Parent->qq.front(); + i.connection->lock.lock(); this->UnlockQueue(); - MySQLresult* res = i.c->DoBlockingQuery(i.query); - i.c->lock.unlock(); + MySQLresult* res = i.connection->DoBlockingQuery(i.querystr); + i.connection->lock.unlock(); /* * At this point, the main thread could be working on: - * Rehash - delete i.c out from under us. We don't care about that. - * UnloadModule - delete i.q and the qq item. Need to avoid reporting results. + * Rehash - delete i.connection out from under us. We don't care about that. + * UnloadModule - delete i.query and the qq item. Need to avoid reporting results. */ this->LockQueue(); - if (!Parent->qq.empty() && Parent->qq.front().q == i.q) + if (!Parent->qq.empty() && Parent->qq.front().query == i.query) { Parent->qq.pop_front(); - Parent->rq.push_back(RQueueItem(i.q, res)); + Parent->rq.push_back(ResultQueueItem(i.query, res)); NotifyParent(); } else @@ -583,13 +593,13 @@ void DispatcherThread::OnNotify() this->LockQueue(); for(ResultQueue::iterator i = Parent->rq.begin(); i != Parent->rq.end(); i++) { - MySQLresult* res = i->r; + MySQLresult* res = i->result; if (res->err.code == SQL::SUCCESS) - i->q->OnResult(*res); + i->query->OnResult(*res); else - i->q->OnError(res->err); - delete i->q; - delete i->r; + i->query->OnError(res->err); + delete i->query; + delete i->result; } Parent->rq.clear(); this->UnlockQueue(); diff --git a/src/modules/extra/m_ssl_gnutls.cpp b/src/modules/extra/m_ssl_gnutls.cpp index 16bdb9a8d..29f1b414f 100644 --- a/src/modules/extra/m_ssl_gnutls.cpp +++ b/src/modules/extra/m_ssl_gnutls.cpp @@ -1199,6 +1199,7 @@ class ModuleSSLGnuTLS : public Module try { ReadProfiles(); + ServerInstance->SNO.WriteToSnoMask('a', "SSL module %s rehashed.", MODNAME); } catch (ModuleException& ex) { diff --git a/src/modules/extra/m_ssl_mbedtls.cpp b/src/modules/extra/m_ssl_mbedtls.cpp index 8b5c720f2..6151a799c 100644 --- a/src/modules/extra/m_ssl_mbedtls.cpp +++ b/src/modules/extra/m_ssl_mbedtls.cpp @@ -932,6 +932,7 @@ class ModuleSSLmbedTLS : public Module try { ReadProfiles(); + ServerInstance->SNO.WriteToSnoMask('a', "SSL module %s rehashed.", MODNAME); } catch (ModuleException& ex) { diff --git a/src/modules/extra/m_ssl_openssl.cpp b/src/modules/extra/m_ssl_openssl.cpp index 801b54b82..a54a0ef0e 100644 --- a/src/modules/extra/m_ssl_openssl.cpp +++ b/src/modules/extra/m_ssl_openssl.cpp @@ -986,6 +986,7 @@ class ModuleSSLOpenSSL : public Module try { ReadProfiles(); + ServerInstance->SNO.WriteToSnoMask('a', "SSL module %s rehashed.", MODNAME); } catch (ModuleException& ex) { diff --git a/src/modules/m_blockcolor.cpp b/src/modules/m_blockcolor.cpp index d66cbc748..62b932b0a 100644 --- a/src/modules/m_blockcolor.cpp +++ b/src/modules/m_blockcolor.cpp @@ -46,19 +46,21 @@ class ModuleBlockColor : public Module if ((target.type == MessageTarget::TYPE_CHANNEL) && (IS_LOCAL(user))) { Channel* c = target.Get<Channel>(); - ModResult res = CheckExemption::Call(exemptionprov, user, c, "blockcolor"); + ModResult res = CheckExemption::Call(exemptionprov, user, c, "blockcolor"); if (res == MOD_RES_ALLOW) return MOD_RES_PASSTHRU; - if (!c->GetExtBanStatus(user, 'c').check(!c->IsModeSet(bc))) + bool modeset = c->IsModeSet(bc); + if (!c->GetExtBanStatus(user, 'c').check(!modeset)) { for (std::string::iterator i = details.text.begin(); i != details.text.end(); i++) { // Block all control codes except \001 for CTCP if ((*i >= 0) && (*i < 32) && (*i != 1)) { - user->WriteNumeric(ERR_CANNOTSENDTOCHAN, c->name, "Can't send colors to channel (+c is set)"); + user->WriteNumeric(ERR_CANNOTSENDTOCHAN, c->name, InspIRCd::Format("Can't send colors to channel (%s)", + modeset ? "+c is set" : "you're extbanned")); return MOD_RES_DENY; } } diff --git a/src/modules/m_conn_umodes.cpp b/src/modules/m_conn_umodes.cpp index 49d56acc0..705fa732d 100644 --- a/src/modules/m_conn_umodes.cpp +++ b/src/modules/m_conn_umodes.cpp @@ -32,22 +32,18 @@ class ModuleModesOnConnect : public Module void OnUserConnect(LocalUser* user) override { - ConfigTag* tag = user->MyClass->config; - std::string ThisModes = tag->getString("modes"); - if (!ThisModes.empty()) - { - std::string buf; - irc::spacesepstream ss(ThisModes); + const std::string modestr = user->MyClass->config->getString("modes"); + if (modestr.empty()) + return; - CommandBase::Params modes; - modes.push_back(user->nick); + CommandBase::Params params; + params.push_back(user->nick); - // split ThisUserModes into modes and mode params - while (ss.GetToken(buf)) - modes.push_back(buf); + irc::spacesepstream modestream(modestr); + for (std::string modetoken; modestream.GetToken(modetoken); ) + params.push_back(modetoken); - ServerInstance->Parser.CallHandler("MODE", modes, user); - } + ServerInstance->Parser.CallHandler("MODE", params, user); } }; diff --git a/src/modules/m_conn_waitpong.cpp b/src/modules/m_conn_waitpong.cpp index fdf935ba3..6b22812aa 100644 --- a/src/modules/m_conn_waitpong.cpp +++ b/src/modules/m_conn_waitpong.cpp @@ -39,7 +39,7 @@ class ModuleWaitPong : public Module void ReadConfig(ConfigStatus& status) override { ConfigTag* tag = ServerInstance->Config->ConfValue("waitpong"); - sendsnotice = tag->getBool("sendsnotice", true); + sendsnotice = tag->getBool("sendsnotice", false); killonbadreply = tag->getBool("killonbadreply", true); } diff --git a/src/modules/m_ldapoper.cpp b/src/modules/m_ldapoper.cpp index 360c4b2f7..76d2af57c 100644 --- a/src/modules/m_ldapoper.cpp +++ b/src/modules/m_ldapoper.cpp @@ -180,14 +180,14 @@ class AdminBindInterface : public LDAPInterface } }; -class ModuleLDAPAuth : public Module +class ModuleLDAPOper : public Module { dynamic_reference<LDAPProvider> LDAP; std::string base; std::string attribute; public: - ModuleLDAPAuth() + ModuleLDAPOper() : LDAP(this, "LDAP") { me = this; @@ -246,4 +246,4 @@ class ModuleLDAPAuth : public Module } }; -MODULE_INIT(ModuleLDAPAuth) +MODULE_INIT(ModuleLDAPOper) diff --git a/src/modules/m_nationalchars.cpp b/src/modules/m_nationalchars.cpp index 884efed23..82cac8358 100644 --- a/src/modules/m_nationalchars.cpp +++ b/src/modules/m_nationalchars.cpp @@ -222,6 +222,7 @@ class ModuleNationalChars : public Module std::function<bool(const std::string&)> rememberer; bool forcequit; const unsigned char * lowermap_rememberer; + std::string casemapping_rememberer; unsigned char prev_map[256]; template <typename T> @@ -248,7 +249,9 @@ class ModuleNationalChars : public Module public: ModuleNationalChars() - : rememberer(ServerInstance->IsNick), lowermap_rememberer(national_case_insensitive_map) + : rememberer(ServerInstance->IsNick) + , lowermap_rememberer(national_case_insensitive_map) + , casemapping_rememberer(ServerInstance->Config->CaseMapping) { memcpy(prev_map, national_case_insensitive_map, sizeof(prev_map)); } @@ -305,6 +308,9 @@ class ModuleNationalChars : public Module { ServerInstance->IsNick = rememberer; national_case_insensitive_map = lowermap_rememberer; + ServerInstance->Config->CaseMapping = casemapping_rememberer; + // The core rebuilds ISupport on module unload, but before the dtor. + ServerInstance->ISupport.Build(); CheckForceQuit("National characters module unloaded"); CheckRehash(); } diff --git a/src/modules/m_noctcp.cpp b/src/modules/m_noctcp.cpp index ba9babdcf..cc9017f36 100644 --- a/src/modules/m_noctcp.cpp +++ b/src/modules/m_noctcp.cpp @@ -70,9 +70,11 @@ class ModuleNoCTCP : public Module if (res == MOD_RES_ALLOW) return MOD_RES_PASSTHRU; - if (!c->GetExtBanStatus(user, 'C').check(!c->IsModeSet(nc))) + bool modeset = c->IsModeSet(nc); + if (!c->GetExtBanStatus(user, 'C').check(!modeset)) { - user->WriteNumeric(ERR_CANNOTSENDTOCHAN, c->name, "Can't send CTCP to channel (+C is set)"); + user->WriteNumeric(ERR_CANNOTSENDTOCHAN, c->name, InspIRCd::Format("Can't send CTCP to channel (%s)", + modeset ? "+C is set" : "you're extbanned")); return MOD_RES_DENY; } break; diff --git a/src/modules/m_nokicks.cpp b/src/modules/m_nokicks.cpp index ff113db35..14402e6d7 100644 --- a/src/modules/m_nokicks.cpp +++ b/src/modules/m_nokicks.cpp @@ -39,10 +39,12 @@ class ModuleNoKicks : public Module ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason) override { - if (!memb->chan->GetExtBanStatus(source, 'Q').check(!memb->chan->IsModeSet(nk))) + bool modeset = memb->chan->IsModeSet(nk); + if (!memb->chan->GetExtBanStatus(source, 'Q').check(!modeset)) { // Can't kick with Q in place, not even opers with override, and founders - source->WriteNumeric(ERR_CHANOPRIVSNEEDED, memb->chan->name, InspIRCd::Format("Can't kick user %s from channel (+Q is set)", memb->user->nick.c_str())); + source->WriteNumeric(ERR_CHANOPRIVSNEEDED, memb->chan->name, InspIRCd::Format("Can't kick user %s from channel (%s)", + memb->user->nick.c_str(), modeset ? "+Q is set" : "you're extbanned")); return MOD_RES_DENY; } return MOD_RES_PASSTHRU; diff --git a/src/modules/m_nonicks.cpp b/src/modules/m_nonicks.cpp index 194c326d7..f0efd771a 100644 --- a/src/modules/m_nonicks.cpp +++ b/src/modules/m_nonicks.cpp @@ -50,17 +50,17 @@ class ModuleNoNickChange : public Module Channel* curr = (*i)->chan; ModResult res = CheckExemption::Call(exemptionprov, user, curr, "nonick"); - if (res == MOD_RES_ALLOW) continue; if (user->HasPrivPermission("channels/ignore-nonicks")) continue; - if (!curr->GetExtBanStatus(user, 'N').check(!curr->IsModeSet(nn))) + bool modeset = curr->IsModeSet(nn); + if (!curr->GetExtBanStatus(user, 'N').check(!modeset)) { - user->WriteNumeric(ERR_CANTCHANGENICK, InspIRCd::Format("Cannot change nickname while on %s (+N is set)", - curr->name.c_str())); + user->WriteNumeric(ERR_CANTCHANGENICK, InspIRCd::Format("Can't change nickname while on %s (%s)", + curr->name.c_str(), modeset ? "+N is set" : "you're extbanned")); return MOD_RES_DENY; } } diff --git a/src/modules/m_nonotice.cpp b/src/modules/m_nonotice.cpp index 92d0f4482..450e476bf 100644 --- a/src/modules/m_nonotice.cpp +++ b/src/modules/m_nonotice.cpp @@ -41,20 +41,20 @@ class ModuleNoNotice : public Module ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) override { - ModResult res; if ((details.type == MSG_NOTICE) && (target.type == MessageTarget::TYPE_CHANNEL) && (IS_LOCAL(user))) { Channel* c = target.Get<Channel>(); - if (!c->GetExtBanStatus(user, 'T').check(!c->IsModeSet(nt))) + + ModResult res = CheckExemption::Call(exemptionprov, user, c, "nonotice"); + if (res == MOD_RES_ALLOW) + return MOD_RES_PASSTHRU; + + bool modeset = c->IsModeSet(nt); + if (!c->GetExtBanStatus(user, 'T').check(!modeset)) { - res = CheckExemption::Call(exemptionprov, user, c, "nonotice"); - if (res == MOD_RES_ALLOW) - return MOD_RES_PASSTHRU; - else - { - user->WriteNumeric(ERR_CANNOTSENDTOCHAN, c->name, "Can't send NOTICE to channel (+T is set)"); - return MOD_RES_DENY; - } + user->WriteNumeric(ERR_CANNOTSENDTOCHAN, c->name, InspIRCd::Format("Can't send NOTICE to channel (%s)", + modeset ? "+T is set" : "you're extbanned")); + return MOD_RES_DENY; } } return MOD_RES_PASSTHRU; diff --git a/src/modules/m_ojoin.cpp b/src/modules/m_ojoin.cpp index 77faf5065..91431d642 100644 --- a/src/modules/m_ojoin.cpp +++ b/src/modules/m_ojoin.cpp @@ -57,11 +57,7 @@ class CommandOjoin : public SplitCommand ServerInstance->SNO.WriteGlobalSno('a', user->nick+" used OJOIN to join "+channel->name); if (notice) - { - const std::string msg = user->nick + " joined on official network business."; - channel->WriteNotice(msg); - ServerInstance->PI->SendChannelNotice(channel, 0, msg); - } + channel->WriteNotice(user->nick + " joined on official network business."); } else { diff --git a/src/modules/m_spanningtree/fjoin.cpp b/src/modules/m_spanningtree/fjoin.cpp index 62a51faba..258d8ee4c 100644 --- a/src/modules/m_spanningtree/fjoin.cpp +++ b/src/modules/m_spanningtree/fjoin.cpp @@ -276,8 +276,13 @@ void CommandFJoin::RemoveStatus(Channel* c) void CommandFJoin::LowerTS(Channel* chan, time_t TS, const std::string& newname) { if (Utils->AnnounceTSChange) - chan->WriteNotice(InspIRCd::Format("Creation time of %s changed from %s to %s", newname.c_str(), - InspIRCd::TimeString(chan->age).c_str(), InspIRCd::TimeString(TS).c_str())); + { + // WriteNotice is not used here because the message only needs to go to the local server. + const std::string tsmessage = InspIRCd::Format("Creation time of %s changed from %s to %s", newname.c_str(), + InspIRCd::TimeString(chan->age).c_str(), InspIRCd::TimeString(TS).c_str()); + ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, chan, tsmessage, MSG_NOTICE); + chan->Write(ServerInstance->GetRFCEvents().privmsg, privmsg); + } // While the name is equal in case-insensitive compare, it might differ in case; use the remote version chan->name = newname; diff --git a/src/modules/m_timedbans.cpp b/src/modules/m_timedbans.cpp index edc2e2946..a1b4ac3e7 100644 --- a/src/modules/m_timedbans.cpp +++ b/src/modules/m_timedbans.cpp @@ -128,9 +128,7 @@ class CommandTban : public Command PrefixMode* mh = ServerInstance->Modes.FindPrefixMode('h'); char pfxchar = (mh && mh->name == "halfop") ? mh->GetPrefix() : '@'; - ClientProtocol::Messages::Privmsg notice(ServerInstance->FakeClient, channel, message, MSG_NOTICE); - channel->Write(ServerInstance->GetRFCEvents().privmsg, notice, pfxchar); - ServerInstance->PI->SendChannelNotice(channel, pfxchar, message); + channel->WriteNotice(message, pfxchar); return CMD_SUCCESS; } @@ -221,9 +219,7 @@ class ModuleTimedBans : public Module PrefixMode* mh = ServerInstance->Modes.FindPrefixMode('h'); char pfxchar = (mh && mh->name == "halfop") ? mh->GetPrefix() : '@'; - ClientProtocol::Messages::Privmsg notice(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, cr, message, MSG_NOTICE); - cr->Write(ServerInstance->GetRFCEvents().privmsg, notice, pfxchar); - ServerInstance->PI->SendChannelNotice(cr, pfxchar, message); + cr->WriteNotice(message, pfxchar); Modes::ChangeList setban; setban.push_remove(ServerInstance->Modes.FindMode('b', MODETYPE_CHANNEL), mask); diff --git a/src/modules/m_uninvite.cpp b/src/modules/m_uninvite.cpp index 5d4fb07a5..d0751dc56 100644 --- a/src/modules/m_uninvite.cpp +++ b/src/modules/m_uninvite.cpp @@ -100,10 +100,7 @@ class CommandUninvite : public Command user->WriteRemoteNumeric(n); lu->WriteNumeric(RPL_UNINVITED, InspIRCd::Format("You were uninvited from %s by %s", c->name.c_str(), user->nick.c_str())); - - std::string msg = "*** " + user->nick + " uninvited " + u->nick + "."; - c->WriteNotice(msg); - ServerInstance->PI->SendChannelNotice(c, 0, msg); + c->WriteNotice(InspIRCd::Format("*** %s uninvited %s.", user->nick.c_str(), u->nick.c_str())); } return CMD_SUCCESS; diff --git a/src/modules/m_websocket.cpp b/src/modules/m_websocket.cpp index fece5fdb1..69e01914b 100644 --- a/src/modules/m_websocket.cpp +++ b/src/modules/m_websocket.cpp @@ -25,18 +25,29 @@ #include <utf8.h> -typedef std::vector<std::string> OriginList; - static const char MagicGUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; static const char whitespace[] = " \t\r\n"; static dynamic_reference_nocheck<HashProvider>* sha1; -class WebSocketHookProvider : public IOHookProvider +struct WebSocketConfig { - public: + typedef std::vector<std::string> OriginList; + typedef std::vector<std::string> ProxyRanges; + + // The HTTP origins that can connect to the server. OriginList allowedorigins; + + // The IP ranges which send trustworthy X-Real-IP or X-Forwarded-For headers. + ProxyRanges proxyranges; + + // Whether to send as UTF-8 text instead of binary data. bool sendastext; +}; +class WebSocketHookProvider : public IOHookProvider +{ + public: + WebSocketConfig config; WebSocketHookProvider(Module* mod) : IOHookProvider(mod, "websocket", IOHookProvider::IOH_UNKNOWN, true) { @@ -110,8 +121,7 @@ class WebSocketHook : public IOHookMiddle State state; time_t lastpingpong; - OriginList& allowedorigins; - bool& sendastext; + WebSocketConfig& config; static size_t FillHeader(unsigned char* outbuf, size_t sendlength, OpCode opcode) { @@ -318,7 +328,7 @@ class WebSocketHook : public IOHookMiddle if (originheader.Find(recvq, "Origin:", 7, reqend)) { const std::string origin = originheader.ExtractValue(recvq); - for (OriginList::const_iterator iter = allowedorigins.begin(); iter != allowedorigins.end(); ++iter) + for (WebSocketConfig::OriginList::const_iterator iter = config.allowedorigins.begin(); iter != config.allowedorigins.end(); ++iter) { if (InspIRCd::Match(origin, *iter, ascii_case_insensitive_map)) { @@ -334,6 +344,36 @@ class WebSocketHook : public IOHookMiddle return -1; } + if (!config.proxyranges.empty() && sock->type == StreamSocket::SS_USER) + { + LocalUser* luser = static_cast<UserIOHandler*>(sock)->user; + irc::sockets::sockaddrs realsa(luser->client_sa); + + HTTPHeaderFinder proxyheader; + if (proxyheader.Find(recvq, "X-Real-IP:", 10, reqend) + && irc::sockets::aptosa(proxyheader.ExtractValue(recvq), realsa.port(), realsa)) + { + // Nothing to do here. + } + else if (proxyheader.Find(recvq, "X-Forwarded-For:", 16, reqend) + && irc::sockets::aptosa(proxyheader.ExtractValue(recvq), realsa.port(), realsa)) + { + // Nothing to do here. + } + + for (WebSocketConfig::ProxyRanges::const_iterator iter = config.proxyranges.begin(); iter != config.proxyranges.end(); ++iter) + { + if (InspIRCd::MatchCIDR(luser->GetIPString(), *iter, ascii_case_insensitive_map)) + { + // Give the user their real IP address. + if (realsa != luser->client_sa) + luser->SetClientIP(realsa); + break; + } + } + } + + HTTPHeaderFinder keyheader; if (!keyheader.Find(recvq, "Sec-WebSocket-Key:", 18, reqend)) { @@ -364,12 +404,11 @@ class WebSocketHook : public IOHookMiddle } public: - WebSocketHook(IOHookProvider* Prov, StreamSocket* sock, OriginList& AllowedOrigins, bool& SendAsText) + WebSocketHook(IOHookProvider* Prov, StreamSocket* sock, WebSocketConfig& cfg) : IOHookMiddle(Prov) , state(STATE_HTTPREQ) , lastpingpong(0) - , allowedorigins(AllowedOrigins) - , sendastext(SendAsText) + , config(cfg) { sock->AddIOHook(this); } @@ -390,7 +429,7 @@ class WebSocketHook : public IOHookMiddle if (*chr == '\n') { // We have found an entire message. Send it in its own frame. - if (sendastext) + if (config.sendastext) { // If we send messages as text then we need to ensure they are valid UTF-8. std::string encoded; @@ -451,7 +490,7 @@ class WebSocketHook : public IOHookMiddle void WebSocketHookProvider::OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) { - new WebSocketHook(this, sock, allowedorigins, sendastext); + new WebSocketHook(this, sock, config); } class ModuleWebSocket : public Module @@ -473,7 +512,7 @@ class ModuleWebSocket : public Module if (tags.first == tags.second) throw ModuleException("You have loaded the websocket module but not configured any allowed origins!"); - OriginList allowedorigins; + WebSocketConfig config; for (ConfigIter i = tags.first; i != tags.second; ++i) { ConfigTag* tag = i->second; @@ -483,12 +522,18 @@ class ModuleWebSocket : public Module if (allow.empty()) throw ModuleException("<wsorigin:allow> is a mandatory field, at " + tag->getTagLocation()); - allowedorigins.push_back(allow); + config.allowedorigins.push_back(allow); } ConfigTag* tag = ServerInstance->Config->ConfValue("websocket"); - hookprov->sendastext = tag->getBool("sendastext", true); - hookprov->allowedorigins.swap(allowedorigins); + config.sendastext = tag->getBool("sendastext", true); + + irc::spacesepstream proxyranges(tag->getString("proxyranges")); + for (std::string proxyrange; proxyranges.GetToken(proxyrange); ) + config.proxyranges.push_back(proxyrange); + + // Everything is okay; apply the new config. + hookprov->config = config; } void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item) override |
