1 /*
2  *
3  * (C) 2003-2020 Anope Team
4  * Contact us at team@anope.org
5  *
6  * Please read COPYING and README for further details.
7  *
8  * Based on the original code of Epona by Lara.
9  * Based on the original code of Services by Andy Church.
10  */
11 
12 #include "services.h"
13 #include "modules.h"
14 #include "protocol.h"
15 #include "users.h"
16 #include "servers.h"
17 #include "config.h"
18 #include "uplink.h"
19 #include "bots.h"
20 #include "channels.h"
21 
22 IRCDProto *IRCD = NULL;
23 
IRCDProto(Module * creator,const Anope::string & p)24 IRCDProto::IRCDProto(Module *creator, const Anope::string &p) : Service(creator, "IRCDProto", creator->name), proto_name(p)
25 {
26 	DefaultPseudoclientModes = "+io";
27 	CanSVSNick = CanSVSJoin = CanSetVHost = CanSetVIdent = CanSNLine = CanSQLine = CanSQLineChannel
28 		= CanSZLine = CanSVSHold = CanSVSO = CanCertFP = RequiresID = AmbiguousID = false;
29 	MaxModes = 3;
30 	MaxLine = 512;
31 
32 	if (IRCD == NULL)
33 		IRCD = this;
34 }
35 
~IRCDProto()36 IRCDProto::~IRCDProto()
37 {
38 	if (IRCD == this)
39 		IRCD = NULL;
40 }
41 
GetProtocolName()42 const Anope::string &IRCDProto::GetProtocolName()
43 {
44 	return this->proto_name;
45 }
46 
nextID(int pos,Anope::string & buf)47 static inline char nextID(int pos, Anope::string &buf)
48 {
49 	char &c = buf[pos];
50 	if (c == 'Z')
51 		c = '0';
52 	else if (c != '9')
53 		++c;
54 	else if (pos)
55 		c = 'A';
56 	else
57 		c = '0';
58 	return c;
59 }
60 
UID_Retrieve()61 Anope::string IRCDProto::UID_Retrieve()
62 {
63 	if (!IRCD || !IRCD->RequiresID)
64 		return "";
65 
66 	static Anope::string current_uid = "AAAAAA";
67 
68 	do
69 	{
70 		int current_len = current_uid.length() - 1;
71 		while (current_len >= 0 && nextID(current_len--, current_uid) == 'A');
72 	}
73 	while (User::Find(Me->GetSID() + current_uid) != NULL);
74 
75 	return Me->GetSID() + current_uid;
76 }
77 
SID_Retrieve()78 Anope::string IRCDProto::SID_Retrieve()
79 {
80 	if (!IRCD || !IRCD->RequiresID)
81 		return "";
82 
83 	static Anope::string current_sid = Config->GetBlock("serverinfo")->Get<const Anope::string>("id");
84 	if (current_sid.empty())
85 		current_sid = "00A";
86 
87 	do
88 	{
89 		int current_len = current_sid.length() - 1;
90 		while (current_len >= 0 && nextID(current_len--, current_sid) == 'A');
91 	}
92 	while (Server::Find(current_sid) != NULL);
93 
94 	return current_sid;
95 }
96 
SendKill(const MessageSource & source,const Anope::string & target,const Anope::string & reason)97 void IRCDProto::SendKill(const MessageSource &source, const Anope::string &target, const Anope::string &reason)
98 {
99 	UplinkSocket::Message(source) << "KILL " << target << " :" << reason;
100 }
101 
SendSVSKillInternal(const MessageSource & source,User * user,const Anope::string & buf)102 void IRCDProto::SendSVSKillInternal(const MessageSource &source, User *user, const Anope::string &buf)
103 {
104 	UplinkSocket::Message(source) << "KILL " << user->GetUID() << " :" << buf;
105 }
106 
SendModeInternal(const MessageSource & source,const Channel * dest,const Anope::string & buf)107 void IRCDProto::SendModeInternal(const MessageSource &source, const Channel *dest, const Anope::string &buf)
108 {
109 	UplinkSocket::Message(source) << "MODE " << dest->name << " " << buf;
110 }
111 
SendModeInternal(const MessageSource & source,User * dest,const Anope::string & buf)112 void IRCDProto::SendModeInternal(const MessageSource &source, User *dest, const Anope::string &buf)
113 {
114 	UplinkSocket::Message(source) << "MODE " << dest->GetUID() << " " << buf;
115 }
116 
SendKickInternal(const MessageSource & source,const Channel * c,User * u,const Anope::string & r)117 void IRCDProto::SendKickInternal(const MessageSource &source, const Channel *c, User *u, const Anope::string &r)
118 {
119 	if (!r.empty())
120 		UplinkSocket::Message(source) << "KICK " << c->name << " " << u->GetUID() << " :" << r;
121 	else
122 		UplinkSocket::Message(source) << "KICK " << c->name << " " << u->GetUID();
123 }
124 
SendNoticeInternal(const MessageSource & source,const Anope::string & dest,const Anope::string & msg)125 void IRCDProto::SendNoticeInternal(const MessageSource &source, const Anope::string &dest, const Anope::string &msg)
126 {
127 	UplinkSocket::Message(source) << "NOTICE " << dest << " :" << msg;
128 }
129 
SendPrivmsgInternal(const MessageSource & source,const Anope::string & dest,const Anope::string & buf)130 void IRCDProto::SendPrivmsgInternal(const MessageSource &source, const Anope::string &dest, const Anope::string &buf)
131 {
132 	UplinkSocket::Message(source) << "PRIVMSG " << dest << " :" << buf;
133 }
134 
SendQuitInternal(User * u,const Anope::string & buf)135 void IRCDProto::SendQuitInternal(User *u, const Anope::string &buf)
136 {
137 	if (!buf.empty())
138 		UplinkSocket::Message(u) << "QUIT :" << buf;
139 	else
140 		UplinkSocket::Message(u) << "QUIT";
141 }
142 
SendPartInternal(User * u,const Channel * chan,const Anope::string & buf)143 void IRCDProto::SendPartInternal(User *u, const Channel *chan, const Anope::string &buf)
144 {
145 	if (!buf.empty())
146 		UplinkSocket::Message(u) << "PART " << chan->name << " :" << buf;
147 	else
148 		UplinkSocket::Message(u) << "PART " << chan->name;
149 }
150 
SendGlobopsInternal(const MessageSource & source,const Anope::string & buf)151 void IRCDProto::SendGlobopsInternal(const MessageSource &source, const Anope::string &buf)
152 {
153 	UplinkSocket::Message(source) << "GLOBOPS :" << buf;
154 }
155 
SendCTCPInternal(const MessageSource & source,const Anope::string & dest,const Anope::string & buf)156 void IRCDProto::SendCTCPInternal(const MessageSource &source, const Anope::string &dest, const Anope::string &buf)
157 {
158 	Anope::string s = Anope::NormalizeBuffer(buf);
159 	this->SendNoticeInternal(source, dest, "\1" + s + "\1");
160 }
161 
SendNumericInternal(int numeric,const Anope::string & dest,const Anope::string & buf)162 void IRCDProto::SendNumericInternal(int numeric, const Anope::string &dest, const Anope::string &buf)
163 {
164 	Anope::string n = stringify(numeric);
165 	if (numeric < 10)
166 		n = "0" + n;
167 	if (numeric < 100)
168 		n = "0" + n;
169 	UplinkSocket::Message(Me) << n << " " << dest << " " << buf;
170 }
171 
SendTopic(const MessageSource & source,Channel * c)172 void IRCDProto::SendTopic(const MessageSource &source, Channel *c)
173 {
174 	UplinkSocket::Message(source) << "TOPIC " << c->name << " :" << c->topic;
175 }
176 
SendSVSKill(const MessageSource & source,User * user,const char * fmt,...)177 void IRCDProto::SendSVSKill(const MessageSource &source, User *user, const char *fmt, ...)
178 {
179 	if (!user || !fmt)
180 		return;
181 
182 	va_list args;
183 	char buf[BUFSIZE] = "";
184 	va_start(args, fmt);
185 	vsnprintf(buf, BUFSIZE - 1, fmt, args);
186 	va_end(args);
187 	SendSVSKillInternal(source, user, buf);
188 }
189 
SendMode(const MessageSource & source,const Channel * dest,const char * fmt,...)190 void IRCDProto::SendMode(const MessageSource &source, const Channel *dest, const char *fmt, ...)
191 {
192 	va_list args;
193 	char buf[BUFSIZE] = "";
194 	va_start(args, fmt);
195 	vsnprintf(buf, BUFSIZE - 1, fmt, args);
196 	va_end(args);
197 	SendModeInternal(source, dest, buf);
198 }
199 
SendMode(const MessageSource & source,User * u,const char * fmt,...)200 void IRCDProto::SendMode(const MessageSource &source, User *u, const char *fmt, ...)
201 {
202 	va_list args;
203 	char buf[BUFSIZE] = "";
204 	va_start(args, fmt);
205 	vsnprintf(buf, BUFSIZE - 1, fmt, args);
206 	va_end(args);
207 	SendModeInternal(source, u, buf);
208 }
209 
SendKick(const MessageSource & source,const Channel * chan,User * user,const char * fmt,...)210 void IRCDProto::SendKick(const MessageSource &source, const Channel *chan, User *user, const char *fmt, ...)
211 {
212 	if (!chan || !user)
213 		return;
214 
215 	va_list args;
216 	char buf[BUFSIZE] = "";
217 	va_start(args, fmt);
218 	vsnprintf(buf, BUFSIZE - 1, fmt, args);
219 	va_end(args);
220 	SendKickInternal(source, chan, user, buf);
221 }
222 
SendNotice(const MessageSource & source,const Anope::string & dest,const char * fmt,...)223 void IRCDProto::SendNotice(const MessageSource &source, const Anope::string &dest, const char *fmt, ...)
224 {
225 	va_list args;
226 	char buf[BUFSIZE] = "";
227 	va_start(args, fmt);
228 	vsnprintf(buf, BUFSIZE - 1, fmt, args);
229 	va_end(args);
230 	SendNoticeInternal(source, dest, buf);
231 }
232 
SendAction(const MessageSource & source,const Anope::string & dest,const char * fmt,...)233 void IRCDProto::SendAction(const MessageSource &source, const Anope::string &dest, const char *fmt, ...)
234 {
235 	va_list args;
236 	char buf[BUFSIZE] = "";
237 	va_start(args, fmt);
238 	vsnprintf(buf, BUFSIZE - 1, fmt, args);
239 	va_end(args);
240 	Anope::string actionbuf = Anope::string("\1ACTION ") + buf + '\1';
241 	SendPrivmsgInternal(source, dest, actionbuf);
242 }
243 
SendPrivmsg(const MessageSource & source,const Anope::string & dest,const char * fmt,...)244 void IRCDProto::SendPrivmsg(const MessageSource &source, const Anope::string &dest, const char *fmt, ...)
245 {
246 	va_list args;
247 	char buf[BUFSIZE] = "";
248 	va_start(args, fmt);
249 	vsnprintf(buf, BUFSIZE - 1, fmt, args);
250 	va_end(args);
251 	SendPrivmsgInternal(source, dest, buf);
252 }
253 
SendQuit(User * u,const char * fmt,...)254 void IRCDProto::SendQuit(User *u, const char *fmt, ...)
255 {
256 	va_list args;
257 	char buf[BUFSIZE] = "";
258 	va_start(args, fmt);
259 	vsnprintf(buf, BUFSIZE - 1, fmt, args);
260 	va_end(args);
261 	SendQuitInternal(u, buf);
262 }
263 
SendPing(const Anope::string & servname,const Anope::string & who)264 void IRCDProto::SendPing(const Anope::string &servname, const Anope::string &who)
265 {
266 	if (servname.empty())
267 		UplinkSocket::Message(Me) << "PING " << who;
268 	else
269 		UplinkSocket::Message(Me) << "PING " << servname << " " << who;
270 }
271 
272 /**
273  * Send a PONG reply to a received PING.
274  * servname should be left NULL to send a one param reply.
275  * @param servname Daemon or client that is responding to the PING.
276  * @param who Origin of the PING and destination of the PONG message.
277  **/
SendPong(const Anope::string & servname,const Anope::string & who)278 void IRCDProto::SendPong(const Anope::string &servname, const Anope::string &who)
279 {
280 	if (servname.empty())
281 		UplinkSocket::Message(Me) << "PONG " << who;
282 	else
283 		UplinkSocket::Message(Me) << "PONG " << servname << " " << who;
284 }
285 
SendInvite(const MessageSource & source,const Channel * c,User * u)286 void IRCDProto::SendInvite(const MessageSource &source, const Channel *c, User *u)
287 {
288 	UplinkSocket::Message(source) << "INVITE " << u->GetUID() << " " << c->name;
289 }
290 
SendPart(User * user,const Channel * chan,const char * fmt,...)291 void IRCDProto::SendPart(User *user, const Channel *chan, const char *fmt, ...)
292 {
293 	if (fmt)
294 	{
295 		va_list args;
296 		char buf[BUFSIZE] = "";
297 		va_start(args, fmt);
298 		vsnprintf(buf, BUFSIZE - 1, fmt, args);
299 		va_end(args);
300 		SendPartInternal(user, chan, buf);
301 	}
302 	else
303 		SendPartInternal(user, chan, "");
304 }
305 
SendGlobops(const MessageSource & source,const char * fmt,...)306 void IRCDProto::SendGlobops(const MessageSource &source, const char *fmt, ...)
307 {
308 	va_list args;
309 	char buf[BUFSIZE] = "";
310 	va_start(args, fmt);
311 	vsnprintf(buf, BUFSIZE - 1, fmt, args);
312 	va_end(args);
313 	SendGlobopsInternal(source, buf);
314 }
315 
SendSquit(Server * s,const Anope::string & message)316 void IRCDProto::SendSquit(Server *s, const Anope::string &message)
317 {
318 	UplinkSocket::Message() << "SQUIT " << s->GetSID() << " :" << message;
319 }
320 
SendNickChange(User * u,const Anope::string & newnick)321 void IRCDProto::SendNickChange(User *u, const Anope::string &newnick)
322 {
323 	UplinkSocket::Message(u) << "NICK " << newnick << " " << Anope::CurTime;
324 }
325 
SendForceNickChange(User * u,const Anope::string & newnick,time_t when)326 void IRCDProto::SendForceNickChange(User *u, const Anope::string &newnick, time_t when)
327 {
328 	UplinkSocket::Message() << "SVSNICK " << u->GetUID() << " " << newnick << " " << when;
329 }
330 
SendCTCP(const MessageSource & source,const Anope::string & dest,const char * fmt,...)331 void IRCDProto::SendCTCP(const MessageSource &source, const Anope::string &dest, const char *fmt, ...)
332 {
333 	va_list args;
334 	char buf[BUFSIZE] = "";
335 	va_start(args, fmt);
336 	vsnprintf(buf, BUFSIZE - 1, fmt, args);
337 	va_end(args);
338 	SendCTCPInternal(source, dest, buf);
339 }
340 
SendNumeric(int numeric,const Anope::string & dest,const char * fmt,...)341 void IRCDProto::SendNumeric(int numeric, const Anope::string &dest, const char *fmt, ...)
342 {
343 	va_list args;
344 	char buf[BUFSIZE] = "";
345 	va_start(args, fmt);
346 	vsnprintf(buf, BUFSIZE - 1, fmt, args);
347 	va_end(args);
348 	SendNumericInternal(numeric, dest, buf);
349 }
350 
IsNickValid(const Anope::string & nick)351 bool IRCDProto::IsNickValid(const Anope::string &nick)
352 {
353 	/**
354 	 * RFC: defination of a valid nick
355 	 * nickname =  ( letter / special ) ( letter / digit / special / "-" )
356 	 * letter   =  A-Z / a-z
357 	 * digit    =  0-9
358 	 * special  =  [, ], \, `, _, ^, {, |, }
359 	 **/
360 
361 	 if (nick.empty())
362 		return false;
363 
364 	Anope::string special = "[]\\`_^{|}";
365 
366 	for (unsigned i = 0; i < nick.length(); ++i)
367 		if (!(nick[i] >= 'A' && nick[i] <= 'Z') && !(nick[i] >= 'a' && nick[i] <= 'z')
368 		  && special.find(nick[i]) == Anope::string::npos
369 		  && (Config && Config->NickChars.find(nick[i]) == Anope::string::npos)
370 		  && (!i || (!(nick[i] >= '0' && nick[i] <= '9') && nick[i] != '-')))
371 			return false;
372 
373 	return true;
374 }
375 
IsChannelValid(const Anope::string & chan)376 bool IRCDProto::IsChannelValid(const Anope::string &chan)
377 {
378 	if (chan.empty() || chan[0] != '#' || chan.length() > Config->GetBlock("networkinfo")->Get<unsigned>("chanlen"))
379 		return false;
380 
381 	if (chan.find_first_of(" ,") != Anope::string::npos)
382 		return false;
383 
384 	return true;
385 }
386 
IsIdentValid(const Anope::string & ident)387 bool IRCDProto::IsIdentValid(const Anope::string &ident)
388 {
389 	if (ident.empty() || ident.length() > Config->GetBlock("networkinfo")->Get<unsigned>("userlen"))
390 		return false;
391 
392 	for (unsigned i = 0; i < ident.length(); ++i)
393 	{
394 		const char &c = ident[i];
395 
396 		if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.' || c == '-')
397 			continue;
398 
399 		return false;
400 	}
401 
402 	return true;
403 }
404 
IsHostValid(const Anope::string & host)405 bool IRCDProto::IsHostValid(const Anope::string &host)
406 {
407 	if (host.empty() || host.length() > Config->GetBlock("networkinfo")->Get<unsigned>("hostlen"))
408 		return false;
409 
410 	const Anope::string &vhostdisablebe = Config->GetBlock("networkinfo")->Get<const Anope::string>("disallow_start_or_end"),
411 		vhostchars = Config->GetBlock("networkinfo")->Get<const Anope::string>("vhost_chars");
412 
413 	if (vhostdisablebe.find_first_of(host[0]) != Anope::string::npos)
414 		return false;
415 	else if (vhostdisablebe.find_first_of(host[host.length() - 1]) != Anope::string::npos)
416 		return false;
417 
418 	int dots = 0;
419 	for (unsigned i = 0; i < host.length(); ++i)
420 	{
421 		if (host[i] == '.')
422 			++dots;
423 		if (vhostchars.find_first_of(host[i]) == Anope::string::npos)
424 			return false;
425 	}
426 
427 	return dots > 0 || Config->GetBlock("networkinfo")->Get<bool>("allow_undotted_vhosts");
428 }
429 
SendOper(User * u)430 void IRCDProto::SendOper(User *u)
431 {
432 	SendNumericInternal(381, u->GetUID(), ":You are now an IRC operator (set by services)");
433 	u->SetMode(NULL, "OPER");
434 }
435 
GetMaxListFor(Channel * c)436 unsigned IRCDProto::GetMaxListFor(Channel *c)
437 {
438 	return c->HasMode("LBAN") ? 0 : Config->GetBlock("networkinfo")->Get<int>("modelistsize");
439 }
440 
NormalizeMask(const Anope::string & mask)441 Anope::string IRCDProto::NormalizeMask(const Anope::string &mask)
442 {
443 	if (IsExtbanValid(mask))
444 		return mask;
445 	return Entry("", mask).GetNUHMask();
446 }
447 
MessageSource(const Anope::string & src)448 MessageSource::MessageSource(const Anope::string &src) : source(src), u(NULL), s(NULL)
449 {
450 	/* no source for incoming message is our uplink */
451 	if (src.empty())
452 		this->s = Servers::GetUplink();
453 	else if (IRCD->RequiresID || src.find('.') != Anope::string::npos)
454 		this->s = Server::Find(src);
455 	if (this->s == NULL)
456 		this->u = User::Find(src);
457 }
458 
MessageSource(User * _u)459 MessageSource::MessageSource(User *_u) : source(_u ? _u->nick : ""), u(_u), s(NULL)
460 {
461 }
462 
MessageSource(Server * _s)463 MessageSource::MessageSource(Server *_s) : source(_s ? _s->GetName() : ""), u(NULL), s(_s)
464 {
465 }
466 
GetName() const467 const Anope::string &MessageSource::GetName() const
468 {
469 	if (this->s)
470 		return this->s->GetName();
471 	else if (this->u)
472 		return this->u->nick;
473 	else
474 		return this->source;
475 }
476 
GetSource() const477 const Anope::string &MessageSource::GetSource() const
478 {
479 	return this->source;
480 }
481 
GetUser() const482 User *MessageSource::GetUser() const
483 {
484 	return this->u;
485 }
486 
GetBot() const487 BotInfo *MessageSource::GetBot() const
488 {
489 	return BotInfo::Find(this->GetName(), true);
490 }
491 
GetServer() const492 Server *MessageSource::GetServer() const
493 {
494 	return this->s;
495 }
496 
IRCDMessage(Module * o,const Anope::string & n,unsigned p)497 IRCDMessage::IRCDMessage(Module *o, const Anope::string &n, unsigned p) : Service(o, "IRCDMessage", o->name + "/" + n.lower()), name(n), param_count(p)
498 {
499 }
500 
GetParamCount() const501 unsigned IRCDMessage::GetParamCount() const
502 {
503 	return this->param_count;
504 }
505 
Run(MessageSource & source,const std::vector<Anope::string> & params,const Anope::map<Anope::string> & tags)506 void IRCDMessage::Run(MessageSource &source, const std::vector<Anope::string> &params, const Anope::map<Anope::string> &tags)
507 {
508 	// Most IRCds don't support message tags yet so use the tagless variant.
509 	Run(source, params);
510 }
511 
512