1 /*
2 * InspIRCd -- Internet Relay Chat Daemon
3 *
4 * Copyright (C) 2019-2020 Matt Schatz <genius3000@g3k.solutions>
5 * Copyright (C) 2018 linuxdaemon <linuxdaemon.irc@gmail.com>
6 * Copyright (C) 2013 Daniel Vassdal <shutter@canternet.org>
7 * Copyright (C) 2013 Adam <Adam@anope.org>
8 * Copyright (C) 2012-2015 Attila Molnar <attilamolnar@hush.com>
9 * Copyright (C) 2012-2014, 2017-2018, 2020 Sadie Powell <sadie@witchery.services>
10 * Copyright (C) 2012, 2018 Robby <robby@chatbelgie.be>
11 * Copyright (C) 2012 ChrisTX <xpipe@hotmail.de>
12 * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
13 * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
14 * Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
15 * Copyright (C) 2005, 2007, 2010 Craig Edwards <brain@inspircd.org>
16 *
17 * This file is part of InspIRCd. InspIRCd is free software: you can
18 * redistribute it and/or modify it under the terms of the GNU General Public
19 * License as published by the Free Software Foundation, version 2.
20 *
21 * This program is distributed in the hope that it will be useful, but WITHOUT
22 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
23 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
24 * details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 */
29
30
31 #ifdef _WIN32
32 #define _CRT_RAND_S
33 #include <stdlib.h>
34 #endif
35
36 #include "inspircd.h"
37 #include "xline.h"
38
39 /* Find a user record by nickname and return a pointer to it */
FindNick(const std::string & nick)40 User* InspIRCd::FindNick(const std::string &nick)
41 {
42 if (!nick.empty() && isdigit(*nick.begin()))
43 return FindUUID(nick);
44 return FindNickOnly(nick);
45 }
46
FindNickOnly(const std::string & nick)47 User* InspIRCd::FindNickOnly(const std::string &nick)
48 {
49 user_hash::iterator iter = this->Users->clientlist.find(nick);
50
51 if (iter == this->Users->clientlist.end())
52 return NULL;
53
54 return iter->second;
55 }
56
FindUUID(const std::string & uid)57 User *InspIRCd::FindUUID(const std::string &uid)
58 {
59 user_hash::iterator finduuid = this->Users->uuidlist.find(uid);
60
61 if (finduuid == this->Users->uuidlist.end())
62 return NULL;
63
64 return finduuid->second;
65 }
66 /* find a channel record by channel name and return a pointer to it */
67
FindChan(const std::string & chan)68 Channel* InspIRCd::FindChan(const std::string &chan)
69 {
70 chan_hash::iterator iter = chanlist.find(chan);
71
72 if (iter == chanlist.end())
73 /* Couldn't find it */
74 return NULL;
75
76 return iter->second;
77 }
78
IsValidMask(const std::string & mask)79 bool InspIRCd::IsValidMask(const std::string &mask)
80 {
81 const char* dest = mask.c_str();
82 int exclamation = 0;
83 int atsign = 0;
84
85 for (const char* i = dest; *i; i++)
86 {
87 /* out of range character, bad mask */
88 if (*i < 32 || *i > 126)
89 {
90 return false;
91 }
92
93 switch (*i)
94 {
95 case '!':
96 exclamation++;
97 break;
98 case '@':
99 atsign++;
100 break;
101 }
102 }
103
104 /* valid masks only have 1 ! and @ */
105 if (exclamation != 1 || atsign != 1)
106 return false;
107
108 if (mask.length() > ServerInstance->Config->Limits.GetMaxMask())
109 return false;
110
111 return true;
112 }
113
StripColor(std::string & sentence)114 void InspIRCd::StripColor(std::string &sentence)
115 {
116 /* refactor this completely due to SQUIT bug since the old code would strip last char and replace with \0 --peavey */
117 int seq = 0;
118
119 for (std::string::iterator i = sentence.begin(); i != sentence.end();)
120 {
121 if (*i == 3)
122 seq = 1;
123 else if (seq && (( ((*i >= '0') && (*i <= '9')) || (*i == ',') ) ))
124 {
125 seq++;
126 if ( (seq <= 4) && (*i == ',') )
127 seq = 1;
128 else if (seq > 3)
129 seq = 0;
130 }
131 else
132 seq = 0;
133
134 // Strip all control codes too except \001 for CTCP
135 if (seq || ((*i >= 0) && (*i < 32) && (*i != 1)))
136 i = sentence.erase(i);
137 else
138 ++i;
139 }
140 }
141
ProcessColors(file_cache & input)142 void InspIRCd::ProcessColors(file_cache& input)
143 {
144 /*
145 * Replace all color codes from the special[] array to actual
146 * color code chars using C++ style escape sequences. You
147 * can append other chars to replace if you like -- Justasic
148 */
149 static struct special_chars
150 {
151 std::string character;
152 std::string replace;
153 special_chars(const std::string& c, const std::string& r)
154 : character(c)
155 , replace(r)
156 {
157 }
158 } special[] = {
159 special_chars("\\b", "\x02"), // Bold
160 special_chars("\\c", "\x03"), // Color
161 special_chars("\\i", "\x1D"), // Italic
162 special_chars("\\m", "\x11"), // Monospace
163 special_chars("\\r", "\x16"), // Reverse
164 special_chars("\\s", "\x1E"), // Strikethrough
165 special_chars("\\u", "\x1F"), // Underline
166 special_chars("\\x", "\x0F"), // Reset
167 special_chars("", "")
168 };
169
170 for(file_cache::iterator it = input.begin(), it_end = input.end(); it != it_end; it++)
171 {
172 std::string ret = *it;
173 for(int i = 0; special[i].character.empty() == false; ++i)
174 {
175 std::string::size_type pos = ret.find(special[i].character);
176 if(pos == std::string::npos) // Couldn't find the character, skip this line
177 continue;
178
179 if((pos > 0) && (ret[pos-1] == '\\') && (ret[pos] == '\\'))
180 continue; // Skip double slashes.
181
182 // Replace all our characters in the array
183 while(pos != std::string::npos)
184 {
185 ret = ret.substr(0, pos) + special[i].replace + ret.substr(pos + special[i].character.size());
186 pos = ret.find(special[i].character, pos + special[i].replace.size());
187 }
188 }
189
190 // Replace double slashes with a single slash before we return
191 std::string::size_type pos = ret.find("\\\\");
192 while(pos != std::string::npos)
193 {
194 ret = ret.substr(0, pos) + "\\" + ret.substr(pos + 2);
195 pos = ret.find("\\\\", pos + 1);
196 }
197 *it = ret;
198 }
199 }
200
201 /* true for valid channel name, false else */
DefaultIsChannel(const std::string & chname)202 bool InspIRCd::DefaultIsChannel(const std::string& chname)
203 {
204 if (chname.empty() || chname.length() > ServerInstance->Config->Limits.ChanMax)
205 return false;
206
207 if (chname[0] != '#')
208 return false;
209
210 for (std::string::const_iterator i = chname.begin()+1; i != chname.end(); ++i)
211 {
212 switch (*i)
213 {
214 case ' ':
215 case ',':
216 case 7:
217 return false;
218 }
219 }
220
221 return true;
222 }
223
224 /* true for valid nickname, false else */
DefaultIsNick(const std::string & n)225 bool InspIRCd::DefaultIsNick(const std::string& n)
226 {
227 if (n.empty() || n.length() > ServerInstance->Config->Limits.NickMax)
228 return false;
229
230 for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
231 {
232 if ((*i >= 'A') && (*i <= '}'))
233 {
234 /* "A"-"}" can occur anywhere in a nickname */
235 continue;
236 }
237
238 if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i != n.begin()))
239 {
240 /* "0"-"9", "-" can occur anywhere BUT the first char of a nickname */
241 continue;
242 }
243
244 /* invalid character! abort */
245 return false;
246 }
247
248 return true;
249 }
250
251 /* return true for good ident, false else */
DefaultIsIdent(const std::string & n)252 bool InspIRCd::DefaultIsIdent(const std::string& n)
253 {
254 if (n.empty())
255 return false;
256
257 for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
258 {
259 if ((*i >= 'A') && (*i <= '}'))
260 {
261 continue;
262 }
263
264 if (((*i >= '0') && (*i <= '9')) || (*i == '-') || (*i == '.'))
265 {
266 continue;
267 }
268
269 return false;
270 }
271
272 return true;
273 }
274
IsHost(const std::string & host)275 bool InspIRCd::IsHost(const std::string& host)
276 {
277 // Hostnames must be non-empty and shorter than the maximum hostname length.
278 if (host.empty() || host.length() > ServerInstance->Config->Limits.MaxHost)
279 return false;
280
281 unsigned int numdashes = 0;
282 unsigned int numdots = 0;
283 bool seendot = false;
284 const std::string::const_iterator hostend = host.end() - 1;
285 for (std::string::const_iterator iter = host.begin(); iter != host.end(); ++iter)
286 {
287 unsigned char chr = static_cast<unsigned char>(*iter);
288
289 // If the current character is a label separator.
290 if (chr == '.')
291 {
292 numdots++;
293
294 // Consecutive separators are not allowed and dashes can not exist at the start or end
295 // of labels and separators must only exist between labels.
296 if (seendot || numdashes || iter == host.begin() || iter == hostend)
297 return false;
298
299 seendot = true;
300 continue;
301 }
302
303 // If this point is reached then the character is not a dot.
304 seendot = false;
305
306 // If the current character is a dash.
307 if (chr == '-')
308 {
309 // Consecutive separators are not allowed and dashes can not exist at the start or end
310 // of labels and separators must only exist between labels.
311 if (seendot || numdashes >= 2 || iter == host.begin() || iter == hostend)
312 return false;
313
314 numdashes += 1;
315 continue;
316 }
317
318 // If this point is reached then the character is not a dash.
319 numdashes = 0;
320
321 // Alphanumeric characters are allowed at any position.
322 if ((chr >= '0' && chr <= '9') || (chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z'))
323 continue;
324
325 return false;
326 }
327
328 // Whilst simple hostnames (e.g. localhost) are valid we do not allow the server to use
329 // them to prevent issues with clients that differentiate between short client and server
330 // prefixes by checking whether the nickname contains a dot.
331 return numdots;
332 }
333
IsSID(const std::string & str)334 bool InspIRCd::IsSID(const std::string &str)
335 {
336 /* Returns true if the string given is exactly 3 characters long,
337 * starts with a digit, and the other two characters are A-Z or digits
338 */
339 return ((str.length() == 3) && isdigit(str[0]) &&
340 ((str[1] >= 'A' && str[1] <= 'Z') || isdigit(str[1])) &&
341 ((str[2] >= 'A' && str[2] <= 'Z') || isdigit(str[2])));
342 }
343
344 /** A lookup table of values for multiplier characters used by
345 * InspIRCd::Duration(). In this lookup table, the indexes for
346 * the ascii values 'm' and 'M' have the value '60', the indexes
347 * for the ascii values 'D' and 'd' have a value of '86400', etc.
348 */
349 static const unsigned int duration_multi[] =
350 {
351 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
352 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
353 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
354 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
355 0, 0, 0, 0, 86400, 0, 0, 0, 3600, 0, 0, 0, 0, 60, 0, 0,
356 0, 0, 0, 1, 0, 0, 0, 604800, 0, 31557600, 0, 0, 0, 0, 0, 0,
357 0, 0, 0, 0, 86400, 0, 0, 0, 3600, 0, 0, 0, 0, 60, 0, 0,
358 0, 0, 0, 1, 0, 0, 0, 604800, 0, 31557600, 0, 0, 0, 0, 0, 0,
359 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
360 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
361 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
362 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
363 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
364 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
365 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
366 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
367 };
368
Duration(const std::string & str,unsigned long & duration)369 bool InspIRCd::Duration(const std::string& str, unsigned long& duration)
370 {
371 unsigned long total = 0;
372 unsigned long subtotal = 0;
373
374 /* Iterate each item in the string, looking for number or multiplier */
375 for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
376 {
377 /* Found a number, queue it onto the current number */
378 if ((*i >= '0') && (*i <= '9'))
379 {
380 subtotal = (subtotal * 10) + (*i - '0');
381 }
382 else
383 {
384 /* Found something that's not a number, find out how much
385 * it multiplies the built up number by, multiply the total
386 * and reset the built up number.
387 */
388 unsigned int multiplier = duration_multi[static_cast<unsigned char>(*i)];
389 if (multiplier == 0)
390 return false;
391
392 total += subtotal * multiplier;
393
394 /* Next subtotal please */
395 subtotal = 0;
396 }
397 }
398 /* Any trailing values built up are treated as raw seconds */
399 duration = total + subtotal;
400 return true;
401 }
402
Duration(const std::string & str)403 unsigned long InspIRCd::Duration(const std::string& str)
404 {
405 unsigned long out = 0;
406 InspIRCd::Duration(str, out);
407 return out;
408 }
409
IsValidDuration(const std::string & duration)410 bool InspIRCd::IsValidDuration(const std::string& duration)
411 {
412 for (std::string::const_iterator i = duration.begin(); i != duration.end(); ++i)
413 {
414 unsigned char c = *i;
415 if (((c >= '0') && (c <= '9')))
416 continue;
417
418 if (!duration_multi[c])
419 return false;
420 }
421 return true;
422 }
423
DurationString(time_t duration)424 std::string InspIRCd::DurationString(time_t duration)
425 {
426 if (duration == 0)
427 return "0s";
428
429 time_t years = duration / 31449600;
430 time_t weeks = (duration / 604800) % 52;
431 time_t days = (duration / 86400) % 7;
432 time_t hours = (duration / 3600) % 24;
433 time_t minutes = (duration / 60) % 60;
434 time_t seconds = duration % 60;
435
436 std::string ret;
437
438 if (years)
439 ret = ConvToStr(years) + "y";
440 if (weeks)
441 ret += ConvToStr(weeks) + "w";
442 if (days)
443 ret += ConvToStr(days) + "d";
444 if (hours)
445 ret += ConvToStr(hours) + "h";
446 if (minutes)
447 ret += ConvToStr(minutes) + "m";
448 if (seconds)
449 ret += ConvToStr(seconds) + "s";
450
451 return ret;
452 }
453
Format(va_list & vaList,const char * formatString)454 std::string InspIRCd::Format(va_list& vaList, const char* formatString)
455 {
456 static std::vector<char> formatBuffer(1024);
457
458 while (true)
459 {
460 va_list dst;
461 va_copy(dst, vaList);
462
463 int vsnret = vsnprintf(&formatBuffer[0], formatBuffer.size(), formatString, dst);
464 va_end(dst);
465
466 if (vsnret > 0 && static_cast<unsigned>(vsnret) < formatBuffer.size())
467 {
468 break;
469 }
470
471 formatBuffer.resize(formatBuffer.size() * 2);
472 }
473
474 return std::string(&formatBuffer[0]);
475 }
476
Format(const char * formatString,...)477 std::string InspIRCd::Format(const char* formatString, ...)
478 {
479 std::string ret;
480 VAFORMAT(ret, formatString, formatString);
481 return ret;
482 }
483
TimeString(time_t curtime,const char * format,bool utc)484 std::string InspIRCd::TimeString(time_t curtime, const char* format, bool utc)
485 {
486 #ifdef _WIN32
487 if (curtime < 0)
488 curtime = 0;
489 #endif
490
491 struct tm* timeinfo = utc ? gmtime(&curtime) : localtime(&curtime);
492 if (!timeinfo)
493 {
494 curtime = 0;
495 timeinfo = localtime(&curtime);
496 }
497
498 // If the calculated year exceeds four digits or is less than the year 1000,
499 // the behavior of asctime() is undefined
500 if (timeinfo->tm_year + 1900 > 9999)
501 timeinfo->tm_year = 9999 - 1900;
502 else if (timeinfo->tm_year + 1900 < 1000)
503 timeinfo->tm_year = 0;
504
505 // This is the default format used by asctime without the terminating new line.
506 if (!format)
507 format = "%a %b %d %Y %H:%M:%S";
508
509 char buffer[512];
510 if (!strftime(buffer, sizeof(buffer), format, timeinfo))
511 buffer[0] = '\0';
512
513 return buffer;
514 }
515
GenRandomStr(unsigned int length,bool printable)516 std::string InspIRCd::GenRandomStr(unsigned int length, bool printable)
517 {
518 std::vector<char> str(length);
519 GenRandom(&str[0], length);
520 if (printable)
521 for (size_t i = 0; i < length; i++)
522 str[i] = 0x3F + (str[i] & 0x3F);
523 return std::string(&str[0], str.size());
524 }
525
526 // NOTE: this has a slight bias for lower values if max is not a power of 2.
527 // Don't use it if that matters.
GenRandomInt(unsigned long max)528 unsigned long InspIRCd::GenRandomInt(unsigned long max)
529 {
530 unsigned long rv;
531 GenRandom((char*)&rv, sizeof(rv));
532 return rv % max;
533 }
534
535 // This is overridden by a higher-quality algorithm when TLS (SSL) support is loaded
DefaultGenRandom(char * output,size_t max)536 void InspIRCd::DefaultGenRandom(char* output, size_t max)
537 {
538 #if defined HAS_ARC4RANDOM_BUF
539 arc4random_buf(output, max);
540 #else
541 for (unsigned int i = 0; i < max; ++i)
542 # ifdef _WIN32
543 {
544 unsigned int uTemp;
545 if(rand_s(&uTemp) != 0)
546 output[i] = rand();
547 else
548 output[i] = uTemp;
549 }
550 # else
551 output[i] = random();
552 # endif
553 #endif
554 }
555