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