1 /*
2  * Copyright (C) 2004-2020 ZNC, see the NOTICE file for details.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <znc/IRCNetwork.h>
18 #include <znc/IRCSock.h>
19 
20 struct reply {
21     const char* szReply;
22     bool bLastResponse;
23 };
24 
25 // TODO this list is far from complete, no errors are handled
26 static const struct {
27     const char* szRequest;
28     struct reply vReplies[20];
29 } vRouteReplies[] = {
30       {"WHO",
31        {{"402", true},   /* rfc1459 ERR_NOSUCHSERVER */
32         {"352", false},  /* rfc1459 RPL_WHOREPLY */
33         {"315", true},   /* rfc1459 RPL_ENDOFWHO */
34         {"354", false},  // e.g. Quaknet uses this for WHO #chan %n
35         {"403", true},   // No such chan
36         {nullptr, true}}},
37       {"LIST",
38        {{"402", true},  /* rfc1459 ERR_NOSUCHSERVER */
39         {"321", false}, /* rfc1459 RPL_LISTSTART */
40         {"322", false}, /* rfc1459 RPL_LIST */
41         {"323", true},  /* rfc1459 RPL_LISTEND */
42         {nullptr, true}}},
43       {"NAMES",
44        {
45         {"353", false}, /* rfc1459 RPL_NAMREPLY */
46         {"366", true},  /* rfc1459 RPL_ENDOFNAMES */
47         {"401", true},  /* rfc1459 ERR_NOSUCHNICK */
48         {"403", true},  /* rfc1459 ERR_NOSUCHCHANNEL */
49         {nullptr, true},
50        }},
51       {"LUSERS",
52        {{"251", false}, /* rfc1459 RPL_LUSERCLIENT */
53         {"252", false}, /* rfc1459 RPL_LUSEROP */
54         {"253", false}, /* rfc1459 RPL_LUSERUNKNOWN */
55         {"254", false}, /* rfc1459 RPL_LUSERCHANNELS */
56         {"255", false}, /* rfc1459 RPL_LUSERME */
57         {"265", false},
58         {"266", true},
59         // We don't handle 250 here since some IRCds don't sent it
60         //{"250", true},
61         {nullptr, true}}},
62       {"WHOIS",
63        {{"311", false}, /* rfc1459 RPL_WHOISUSER */
64         {"312", false}, /* rfc1459 RPL_WHOISSERVER */
65         {"313", false}, /* rfc1459 RPL_WHOISOPERATOR */
66         {"317", false}, /* rfc1459 RPL_WHOISIDLE */
67         {"319", false}, /* rfc1459 RPL_WHOISCHANNELS */
68         {"320", false}, /* unreal RPL_WHOISSPECIAL */
69         {"301", false}, /* rfc1459 RPL_AWAY */
70         {"276", false}, /* oftc-hybrid RPL_WHOISCERTFP */
71         {"330", false}, /* ratbox RPL_WHOISLOGGEDIN
72                            aka ircu RPL_WHOISACCOUNT */
73         {"338", false}, /* ircu RPL_WHOISACTUALLY -- "actually using host" */
74         {"378", false}, /* RPL_WHOISHOST -- real address of vhosts */
75         {"671", false}, /* RPL_WHOISSECURE */
76         {"307", false}, /* RPL_WHOISREGNICK */
77         {"379", false}, /* RPL_WHOISMODES */
78         {"760", false}, /* ircv3.2 RPL_WHOISKEYVALUE */
79         {"318", true},  /* rfc1459 RPL_ENDOFWHOIS */
80         {"401", true},  /* rfc1459 ERR_NOSUCHNICK */
81         {"402", true},  /* rfc1459 ERR_NOSUCHSERVER */
82         {"431", true},  /* rfc1459 ERR_NONICKNAMEGIVEN */
83         {nullptr, true}}},
84       {"PING",
85        {{"PONG", true},
86         {"402", true}, /* rfc1459 ERR_NOSUCHSERVER */
87         {"409", true}, /* rfc1459 ERR_NOORIGIN */
88         {nullptr, true}}},
89       {"USERHOST",
90        {{"302", true},
91         {"461", true}, /* rfc1459 ERR_NEEDMOREPARAMS */
92         {nullptr, true}}},
93       {"TIME",
94        {{"391", true}, /* rfc1459 RPL_TIME */
95         {"402", true}, /* rfc1459 ERR_NOSUCHSERVER */
96         {nullptr, true}}},
97       {"WHOWAS",
98        {{"406", false}, /* rfc1459 ERR_WASNOSUCHNICK */
99         {"312", false}, /* rfc1459 RPL_WHOISSERVER */
100         {"314", false}, /* rfc1459 RPL_WHOWASUSER */
101         {"330", false}, /* ratbox RPL_WHOISLOGGEDIN
102                            aka ircu RPL_WHOISACCOUNT */
103         {"338", false}, /* ircu RPL_WHOISACTUALLY -- "actually using host" */
104         {"369", true},  /* rfc1459 RPL_ENDOFWHOWAS */
105         {"431", true},  /* rfc1459 ERR_NONICKNAMEGIVEN */
106         {nullptr, true}}},
107       {"ISON",
108        {{"303", true}, /* rfc1459 RPL_ISON */
109         {"461", true}, /* rfc1459 ERR_NEEDMOREPARAMS */
110         {nullptr, true}}},
111       {"LINKS",
112        {{"364", false}, /* rfc1459 RPL_LINKS */
113         {"365", true},  /* rfc1459 RPL_ENDOFLINKS */
114         {"402", true},  /* rfc1459 ERR_NOSUCHSERVER */
115         {nullptr, true}}},
116       {"MAP",
117        {{"006", false},
118         // inspircd
119         {"270", false},
120         // SilverLeo wants this two added
121         {"015", false},
122         {"017", true},
123         {"007", true},
124         {"481", true}, /* rfc1459 ERR_NOPRIVILEGES */
125         {nullptr, true}}},
126       {"TRACE",
127        {{"200", false}, /* rfc1459 RPL_TRACELINK */
128         {"201", false}, /* rfc1459 RPL_TRACECONNECTING */
129         {"202", false}, /* rfc1459 RPL_TRACEHANDSHAKE */
130         {"203", false}, /* rfc1459 RPL_TRACEUNKNOWN */
131         {"204", false}, /* rfc1459 RPL_TRACEOPERATOR */
132         {"205", false}, /* rfc1459 RPL_TRACEUSER */
133         {"206", false}, /* rfc1459 RPL_TRACESERVER */
134         {"208", false}, /* rfc1459 RPL_TRACENEWTYPE */
135         {"261", false}, /* rfc1459 RPL_TRACELOG */
136         {"262", true},
137         {"402", true}, /* rfc1459 ERR_NOSUCHSERVER */
138         {nullptr, true}}},
139       {"USERS",
140        {
141         {"265", false},
142         {"266", true},
143         {"392", false}, /* rfc1459 RPL_USERSSTART */
144         {"393", false}, /* rfc1459 RPL_USERS */
145         {"394", true},  /* rfc1459 RPL_ENDOFUSERS */
146         {"395", false}, /* rfc1459 RPL_NOUSERS */
147         {"402", true},  /* rfc1459 ERR_NOSUCHSERVER */
148         {"424", true},  /* rfc1459 ERR_FILEERROR */
149         {"446", true},  /* rfc1459 ERR_USERSDISABLED */
150         {nullptr, true},
151        }},
152       {"METADATA",
153        {
154         {"761", false}, /* ircv3.2 RPL_KEYVALUE */
155         {"762", true},  /* ircv3.2 RPL_METADATAEND */
156         {"765", true},  /* ircv3.2 ERR_TARGETINVALID */
157         {"766", true},  /* ircv3.2 ERR_NOMATCHINGKEYS */
158         {"767", true},  /* ircv3.2 ERR_KEYINVALID */
159         {"768", true},  /* ircv3.2 ERR_KEYNOTSET */
160         {"769", true},  /* ircv3.2 ERR_KEYNOPERMISSION */
161         {nullptr, true},
162        }},
163       // This is just a list of all possible /mode replies stuffed together.
164       // Since there should never be more than one of these going on, this
165       // should work fine and makes the code simpler.
166       {"MODE",
167        {// "You're not a channel operator"
168         {"482", true},
169         // MODE I
170         {"346", false},
171         {"347", true},
172         // MODE b
173         {"367", false},
174         {"368", true},
175         // MODE e
176         {"348", false},
177         {"349", true},
178         {"403", true}, /* rfc1459 ERR_NOSUCHCHANNEL */
179         {"442", true}, /* rfc1459 ERR_NOTONCHANNEL */
180         {"467", true}, /* rfc1459 ERR_KEYSET */
181         {"472", true}, /* rfc1459 ERR_UNKNOWNMODE */
182         {"501", true}, /* rfc1459 ERR_UMODEUNKNOWNFLAG */
183         {"502", true}, /* rfc1459 ERR_USERSDONTMATCH */
184         {nullptr, true},
185        }},
186       // END (last item!)
187       {nullptr, {{nullptr, true}}}};
188 
189 class CRouteTimeout : public CTimer {
190   public:
CRouteTimeout(CModule * pModule,unsigned int uInterval,unsigned int uCycles,const CString & sLabel,const CString & sDescription)191     CRouteTimeout(CModule* pModule, unsigned int uInterval,
192                   unsigned int uCycles, const CString& sLabel,
193                   const CString& sDescription)
194         : CTimer(pModule, uInterval, uCycles, sLabel, sDescription) {}
~CRouteTimeout()195     ~CRouteTimeout() override {}
196 
197   protected:
198     void RunJob() override;
199 };
200 
201 struct queued_req {
202     CMessage msg;
203     const struct reply* reply;
204 };
205 
206 typedef std::map<CClient*, std::vector<struct queued_req>> requestQueue;
207 
208 class CRouteRepliesMod : public CModule {
209   public:
MODCONSTRUCTOR(CRouteRepliesMod)210     MODCONSTRUCTOR(CRouteRepliesMod) {
211         m_pDoing = nullptr;
212         m_pReplies = nullptr;
213 
214         AddHelpCommand();
215         AddCommand("Silent", t_d("[yes|no]"),
216                    t_d("Decides whether to show the timeout messages or not"),
217                    [=](const CString& sLine) { SilentCommand(sLine); });
218     }
219 
~CRouteRepliesMod()220     ~CRouteRepliesMod() override {
221         requestQueue::iterator it;
222 
223         while (!m_vsPending.empty()) {
224             it = m_vsPending.begin();
225 
226             while (!it->second.empty()) {
227                 PutIRC(it->second[0].msg);
228                 it->second.erase(it->second.begin());
229             }
230 
231             m_vsPending.erase(it);
232         }
233     }
234 
OnIRCConnected()235     void OnIRCConnected() override {
236         m_pDoing = nullptr;
237         m_pReplies = nullptr;
238         m_vsPending.clear();
239 
240         // No way we get a reply, so stop the timer (If it's running)
241         RemTimer("RouteTimeout");
242     }
243 
OnIRCDisconnected()244     void OnIRCDisconnected() override {
245         OnIRCConnected();  // Let's keep it in one place
246     }
247 
OnClientDisconnect()248     void OnClientDisconnect() override {
249         requestQueue::iterator it;
250 
251         if (GetClient() == m_pDoing) {
252             // The replies which aren't received yet will be
253             // broadcasted to everyone, but at least nothing breaks
254             RemTimer("RouteTimeout");
255             m_pDoing = nullptr;
256             m_pReplies = nullptr;
257         }
258 
259         it = m_vsPending.find(GetClient());
260 
261         if (it != m_vsPending.end()) m_vsPending.erase(it);
262 
263         SendRequest();
264     }
265 
OnRawMessage(CMessage & msg)266     EModRet OnRawMessage(CMessage& msg) override {
267         CString sCmd = msg.GetCommand().AsUpper();
268         size_t i = 0;
269 
270         if (!m_pReplies) return CONTINUE;
271 
272         // Is this a "not enough arguments" error?
273         if (sCmd == "461") {
274             // :server 461 nick WHO :Not enough parameters
275             CString sOrigCmd = msg.GetParam(1);
276 
277             if (m_LastRequest.GetCommand().Equals(sOrigCmd)) {
278                 // This is the reply to the last request
279                 if (RouteReply(msg, true)) return HALTCORE;
280                 return CONTINUE;
281             }
282         }
283 
284         while (m_pReplies[i].szReply != nullptr) {
285             if (m_pReplies[i].szReply == sCmd) {
286                 if (RouteReply(msg, m_pReplies[i].bLastResponse))
287                     return HALTCORE;
288                 return CONTINUE;
289             }
290             i++;
291         }
292 
293         // TODO HALTCORE is wrong, it should not be passed to
294         // the clients, but the core itself should still handle it!
295 
296         return CONTINUE;
297     }
298 
OnUserRawMessage(CMessage & Message)299     EModRet OnUserRawMessage(CMessage& Message) override {
300         const CString& sCmd = Message.GetCommand();
301 
302         if (!GetNetwork()->GetIRCSock() ||
303             !GetNetwork()->GetIRCSock()->IsConnected())
304             return CONTINUE;
305 
306         if (Message.GetType() == CMessage::Type::Mode) {
307             // Check if this is a mode request that needs to be handled
308 
309             // If there are arguments to a mode change,
310             // we must not route it.
311             if (!Message.GetParamsColon(2).empty()) return CONTINUE;
312 
313             // Grab the mode change parameter
314             CString sMode = Message.GetParam(1);
315 
316             // If this is a channel mode request, znc core replies to it
317             if (sMode.empty()) return CONTINUE;
318 
319             // Check if this is a mode change or a specific
320             // mode request (the later needs to be routed).
321             sMode.TrimPrefix("+");
322             if (sMode.length() != 1) return CONTINUE;
323 
324             // Now just check if it's one of the supported modes
325             switch (sMode[0]) {
326                 case 'I':
327                 case 'b':
328                 case 'e':
329                     break;
330                 default:
331                     return CONTINUE;
332             }
333 
334             // Ok, this looks like we should route it.
335             // Fall through to the next loop
336         }
337 
338         for (size_t i = 0; vRouteReplies[i].szRequest != nullptr; i++) {
339             if (vRouteReplies[i].szRequest == sCmd) {
340                 struct queued_req req = {Message, vRouteReplies[i].vReplies};
341                 m_vsPending[GetClient()].push_back(req);
342                 SendRequest();
343 
344                 return HALTCORE;
345             }
346         }
347 
348         return CONTINUE;
349     }
350 
Timeout()351     void Timeout() {
352         // The timer will be deleted after this by the event loop
353 
354         if (!GetNV("silent_timeouts").ToBool()) {
355             PutModule(
356                 t_s("This module hit a timeout which is probably a "
357                     "connectivity issue."));
358             PutModule(
359                 t_s("However, if you can provide steps to reproduce this "
360                     "issue, please do report a bug."));
361             PutModule(
362                 t_f("To disable this message, do \"/msg {1} silent yes\"")(
363                     GetModNick()));
364             PutModule(t_f("Last request: {1}")(m_LastRequest.ToString()));
365             PutModule(t_s("Expected replies:"));
366 
367             for (size_t i = 0; m_pReplies[i].szReply != nullptr; i++) {
368                 if (m_pReplies[i].bLastResponse)
369                     PutModule(t_f("{1} (last)")(m_pReplies[i].szReply));
370                 else
371                     PutModule(m_pReplies[i].szReply);
372             }
373         }
374 
375         m_pDoing = nullptr;
376         m_pReplies = nullptr;
377         SendRequest();
378     }
379 
380   private:
RouteReply(const CMessage & msg,bool bFinished=false)381     bool RouteReply(const CMessage& msg, bool bFinished = false) {
382         if (!m_pDoing) return false;
383 
384         // TODO: RouteReply(const CMessage& Message, bool bFinished = false)
385         m_pDoing->PutClient(msg);
386 
387         if (bFinished) {
388             // Stop the timeout
389             RemTimer("RouteTimeout");
390 
391             m_pDoing = nullptr;
392             m_pReplies = nullptr;
393             SendRequest();
394         }
395 
396         return true;
397     }
398 
SendRequest()399     void SendRequest() {
400         requestQueue::iterator it;
401 
402         if (m_pDoing || m_pReplies) return;
403 
404         if (m_vsPending.empty()) return;
405 
406         it = m_vsPending.begin();
407 
408         if (it->second.empty()) {
409             m_vsPending.erase(it);
410             SendRequest();
411             return;
412         }
413 
414         // When we are called from the timer, we need to remove it.
415         // We can't delete it (segfault on return), thus we
416         // just stop it. The main loop will delete it.
417         CTimer* pTimer = FindTimer("RouteTimeout");
418         if (pTimer) {
419             pTimer->Stop();
420             UnlinkTimer(pTimer);
421         }
422         AddTimer(
423             new CRouteTimeout(this, 60, 1, "RouteTimeout",
424                               "Recover from missing / wrong server replies"));
425 
426         m_pDoing = it->first;
427         m_pReplies = it->second[0].reply;
428         m_LastRequest = it->second[0].msg;
429         PutIRC(it->second[0].msg);
430         it->second.erase(it->second.begin());
431     }
432 
SilentCommand(const CString & sLine)433     void SilentCommand(const CString& sLine) {
434         const CString sValue = sLine.Token(1);
435 
436         if (!sValue.empty()) {
437             SetNV("silent_timeouts", sValue);
438         }
439 
440         PutModule(GetNV("silent_timeouts").ToBool()
441                       ? t_s("Timeout messages are disabled.")
442                       : t_s("Timeout messages are enabled."));
443     }
444 
445     CClient* m_pDoing;
446     const struct reply* m_pReplies;
447     requestQueue m_vsPending;
448     // This field is only used for display purpose.
449     CMessage m_LastRequest;
450 };
451 
RunJob()452 void CRouteTimeout::RunJob() {
453     CRouteRepliesMod* pMod = (CRouteRepliesMod*)GetModule();
454     pMod->Timeout();
455 }
456 
457 template <>
TModInfo(CModInfo & Info)458 void TModInfo<CRouteRepliesMod>(CModInfo& Info) {
459     Info.SetWikiPage("route_replies");
460 }
461 
462 NETWORKMODULEDEFS(CRouteRepliesMod,
463                   t_s("Send replies (e.g. to /who) to the right client only"))
464