unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
serv.c (33816B)
1 /* 2 * Unreal Internet Relay Chat Daemon, src/serv.c 3 * Copyright (C) 1990 Jarkko Oikarinen and 4 * University of Oulu, Computing Center 5 * 6 * See file AUTHORS in IRC package for additional names of 7 * the programmers. 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 1, or (at your option) 12 * any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 */ 23 24 /** @file 25 * @brief Server-related functions 26 */ 27 28 /* s_serv.c 2.55 2/7/94 (C) 1988 University of Oulu, Computing Center and Jarkko Oikarinen */ 29 30 #include "unrealircd.h" 31 #include <ares.h> 32 #ifndef _WIN32 33 /* for uname(), is POSIX so should be OK... */ 34 #include <sys/utsname.h> 35 #endif 36 37 MODVAR int max_connection_count = 1, max_client_count = 1; 38 extern int do_garbage_collect; 39 /* We need all these for cached MOTDs -- codemastr */ 40 extern char *buildid; 41 MOTDFile opermotd; 42 MOTDFile rules; 43 MOTDFile motd; 44 MOTDFile svsmotd; 45 MOTDFile botmotd; 46 MOTDFile smotd; 47 48 /** Hash list of TKL entries */ 49 MODVAR TKL *tklines[TKLISTLEN]; 50 /** 2D hash list of TKL entries + IP address */ 51 MODVAR TKL *tklines_ip_hash[TKLIPHASHLEN1][TKLIPHASHLEN2]; 52 int MODVAR spamf_ugly_vchanoverride = 0; 53 54 void read_motd(const char *filename, MOTDFile *motd); 55 void do_read_motd(const char *filename, MOTDFile *themotd); 56 57 extern MOTDLine *find_file(char *, short); 58 59 void reread_motdsandrules(); 60 61 #if defined(__GNUC__) 62 /* Temporarily ignore for this function. FIXME later!!! */ 63 #pragma GCC diagnostic push 64 #pragma GCC diagnostic ignored "-Wformat-nonliteral" 65 #endif 66 67 /** Send a message upstream if necessary and check if it's for us. 68 * @param client The sender 69 * @param mtags Message tags associated with this message 70 * @param command The command (eg: "NOTICE") 71 * @param server This indicates parv[server] contains the destination 72 * @param parc Parameter count (MAX 8!!) 73 * @param parv Parameter values (MAX 8!!) 74 * @note While sending parv[server] is replaced with the name of the matched client 75 * (virtually, as parv[] is not actually written to) 76 */ 77 int hunt_server(Client *client, MessageTag *mtags, const char *command, int server, int parc, const char *parv[]) 78 { 79 Client *acptr; 80 const char *saved; 81 int i; 82 char buf[1024]; 83 84 if (strchr(command, '%') || strchr(command, ' ')) 85 { 86 unreal_log(ULOG_ERROR, "main", "BUG_HUNT_SERVER", client, 87 "[BUG] hunt_server called with command '$command' but it may not contain " 88 "spaces or percentage signs nowadays, it must be ONLY the command.", 89 log_data_string("command", command)); 90 abort(); 91 } 92 93 /* This would be strange and bad. Previous version assumed "it's for me". Hmm.. okay. */ 94 if (parc <= server || BadPtr(parv[server])) 95 return HUNTED_ISME; 96 97 acptr = find_client(parv[server], NULL); 98 99 /* find_client() may find a variety of clients. Only servers/persons please, no 'unknowns'. */ 100 if (acptr && MyConnect(acptr) && !IsMe(acptr) && !IsUser(acptr) && !IsServer(acptr)) 101 acptr = NULL; 102 103 if (!acptr) 104 { 105 sendnumeric(client, ERR_NOSUCHSERVER, parv[server]); 106 return HUNTED_NOSUCH; 107 } 108 109 if (IsMe(acptr) || MyUser(acptr)) 110 return HUNTED_ISME; 111 112 /* Never send the message back from where it came from */ 113 if (acptr->direction == client->direction) 114 { 115 sendnumeric(client, ERR_NOSUCHSERVER, parv[server]); 116 return HUNTED_NOSUCH; 117 } 118 119 /* This puts all parv[] arguments in 'buf' 120 * Taken from concat_params() but this one is 121 * with parv[server] magic replacement. 122 */ 123 *buf = '\0'; 124 for (i = 1; i < parc; i++) 125 { 126 const char *param = parv[i]; 127 128 if (!param) 129 break; 130 131 /* The magic parv[server] replacement: 132 * this replaces eg 'User' with '001' in S2S traffic. 133 */ 134 if (i == server) 135 param = acptr->id; 136 137 if (*buf) 138 strlcat(buf, " ", sizeof(buf)); 139 140 if (strchr(param, ' ') || (*param == ':')) 141 { 142 /* Last parameter, with : */ 143 strlcat(buf, ":", sizeof(buf)); 144 strlcat(buf, parv[i], sizeof(buf)); 145 break; 146 } 147 strlcat(buf, parv[i], sizeof(buf)); 148 } 149 150 sendto_one(acptr, mtags, ":%s %s %s", client->id, command, buf); 151 152 return HUNTED_PASS; 153 } 154 155 #if defined(__GNUC__) 156 #pragma GCC diagnostic pop 157 #endif 158 159 #ifndef _WIN32 160 /** Grab operating system name on Windows (outdated) */ 161 char *getosname(void) 162 { 163 static char buf[1024]; 164 struct utsname osinf; 165 char *p; 166 167 memset(&osinf, 0, sizeof(osinf)); 168 if (uname(&osinf) != 0) 169 return "<unknown>"; 170 snprintf(buf, sizeof(buf), "%s %s %s %s %s", 171 osinf.sysname, 172 osinf.nodename, 173 osinf.release, 174 osinf.version, 175 osinf.machine); 176 /* get rid of cr/lf */ 177 for (p=buf; *p; p++) 178 if ((*p == '\n') || (*p == '\r')) 179 { 180 *p = '\0'; 181 break; 182 } 183 return buf; 184 } 185 #endif 186 187 /** Helper function to send version strings */ 188 void send_version(Client *client, int remote) 189 { 190 int i; 191 192 for (i = 0; ISupportStrings[i]; i++) 193 { 194 if (remote) 195 sendnumeric(client, RPL_REMOTEISUPPORT, ISupportStrings[i]); 196 else 197 sendnumeric(client, RPL_ISUPPORT, ISupportStrings[i]); 198 } 199 } 200 201 /** VERSION command: 202 * Syntax: VERSION [server] 203 */ 204 CMD_FUNC(cmd_version) 205 { 206 /* Only allow remote VERSIONs if registered -- Syzop */ 207 if (!IsUser(client) && !IsServer(client)) 208 { 209 send_version(client, 0); 210 return; 211 } 212 213 if (hunt_server(client, recv_mtags, "VERSION", 1, parc, parv) == HUNTED_ISME) 214 { 215 sendnumeric(client, RPL_VERSION, version, debugmode, me.name, 216 (ValidatePermissionsForPath("server:info",client,NULL,NULL,NULL) ? serveropts : "0"), 217 extraflags ? extraflags : "", 218 tainted ? "3" : "", 219 (ValidatePermissionsForPath("server:info",client,NULL,NULL,NULL) ? MYOSNAME : "*"), 220 UnrealProtocol); 221 if (ValidatePermissionsForPath("server:info",client,NULL,NULL,NULL)) 222 { 223 sendnotice(client, "%s", SSLeay_version(SSLEAY_VERSION)); 224 sendnotice(client, "libsodium %s", sodium_version_string()); 225 #ifdef USE_LIBCURL 226 sendnotice(client, "%s", curl_version()); 227 #endif 228 sendnotice(client, "c-ares %s", ares_version(NULL)); 229 sendnotice(client, "%s", pcre2_version()); 230 #if JANSSON_VERSION_HEX >= 0x020D00 231 sendnotice(client, "jansson %s\n", jansson_version_str()); 232 #endif 233 } 234 if (MyUser(client)) 235 send_version(client,0); 236 else 237 send_version(client,1); 238 } 239 } 240 241 char *num = NULL; 242 243 /** Send all our PROTOCTL messages to remote server. 244 * We send multiple PROTOCTL's since 4.x. If this breaks your services 245 * because you fail to maintain PROTOCTL state, then fix them! 246 */ 247 void send_proto(Client *client, ConfigItem_link *aconf) 248 { 249 ISupport *prefix = ISupportFind("PREFIX"); 250 251 /* CAUTION: If adding a token to an existing PROTOCTL line below, 252 * then ensure that MAXPARA is not reached! 253 */ 254 255 /* First line */ 256 sendto_one(client, NULL, "PROTOCTL NOQUIT NICKv2 SJOIN SJOIN2 UMODE2 VL SJ3 TKLEXT TKLEXT2 NICKIP ESVID NEXTBANS %s %s", 257 iConf.ban_setter_sync ? "SJSBY" : "", 258 ClientCapabilityFindReal("message-tags") ? "MTAGS" : ""); 259 260 /* Second line */ 261 sendto_one(client, NULL, "PROTOCTL CHANMODES=%s%s,%s,%s,%s USERMODES=%s BOOTED=%lld PREFIX=%s SID=%s MLOCK TS=%lld EXTSWHOIS", 262 CHPAR1, EXPAR1, EXPAR2, EXPAR3, EXPAR4, 263 umodestring, (long long)me.local->fake_lag, prefix->value, 264 me.id, (long long)TStime()); 265 266 /* Third line */ 267 sendto_one(client, NULL, "PROTOCTL NICKCHARS=%s CHANNELCHARS=%s", 268 charsys_get_current_languages(), 269 allowed_channelchars_valtostr(iConf.allowed_channelchars)); 270 } 271 272 #ifndef IRCDTOTALVERSION 273 #define IRCDTOTALVERSION BASE_VERSION "-" PATCH1 PATCH2 PATCH3 PATCH4 PATCH5 PATCH6 PATCH7 PATCH8 PATCH9 274 #endif 275 276 /** Special filter for remote commands */ 277 int remotecmdfilter(Client *client, int parc, const char *parv[]) 278 { 279 /* no remote requests permitted from non-ircops */ 280 if (MyUser(client) && !ValidatePermissionsForPath("server:remote",client,NULL,NULL,NULL) && !BadPtr(parv[1])) 281 { 282 sendnumeric(client, ERR_NOPRIVILEGES); 283 return 1; /* STOP */ 284 } 285 286 /* same as above, but in case an old server forwards a request to us: we ignore it */ 287 if (!MyUser(client) && !ValidatePermissionsForPath("server:remote",client,NULL,NULL,NULL)) 288 return 1; /* STOP (return) */ 289 290 return 0; /* Continue */ 291 } 292 293 /** Output for /INFO */ 294 char *unrealinfo[] = 295 { 296 "This release was brought to you by the following people:", 297 "", 298 "Head coder:", 299 "* Bram Matthys (Syzop) <syzop@unrealircd.org>", 300 "", 301 "Coders:", 302 "* Krzysztof Beresztant (k4be) <k4be@unrealircd.org>", 303 "* Gottem <gottem@unrealircd.org>", 304 "* i <i@unrealircd.org>", 305 "", 306 "Past UnrealIRCd 4.x coders/contributors:", 307 "* Heero, binki, nenolod, ..", 308 "", 309 "Past UnrealIRCd 3.2.x coders/contributors:", 310 "* Stskeeps (ret. head coder / project leader)", 311 "* codemastr (ret. u3.2 head coder)", 312 "* aquanight, WolfSage, ..", 313 "* McSkaf, Zogg, NiQuiL, chasm, llthangel, nighthawk, ..", 314 NULL 315 }; 316 317 /** Send /INFO output */ 318 void cmd_info_send(Client *client) 319 { 320 char **text = unrealinfo; 321 322 sendnumericfmt(client, RPL_INFO, ":========== %s ==========", IRCDTOTALVERSION); 323 324 while (*text) 325 sendnumericfmt(client, RPL_INFO, ":| %s", *text++); 326 327 sendnumericfmt(client, RPL_INFO, ":|"); 328 sendnumericfmt(client, RPL_INFO, ":|"); 329 sendnumericfmt(client, RPL_INFO, ":| Credits - Type /CREDITS"); 330 sendnumericfmt(client, RPL_INFO, ":|"); 331 sendnumericfmt(client, RPL_INFO, ":| This is an UnrealIRCd-style server"); 332 sendnumericfmt(client, RPL_INFO, ":| If you find any bugs, please report them at:"); 333 sendnumericfmt(client, RPL_INFO, ":| https://bugs.unrealircd.org/"); 334 sendnumericfmt(client, RPL_INFO, ":| UnrealIRCd Homepage: https://www.unrealircd.org"); 335 sendnumericfmt(client, RPL_INFO, ":============================================"); 336 sendnumericfmt(client, RPL_INFO, ":Birth Date: %s, compile # %s", creation, generation); 337 sendnumericfmt(client, RPL_INFO, ":On-line since %s", myctime(me.local->creationtime)); 338 sendnumericfmt(client, RPL_INFO, ":ReleaseID (%s)", buildid); 339 sendnumeric(client, RPL_ENDOFINFO); 340 } 341 342 /** The INFO command. 343 * Syntax: INFO [server] 344 */ 345 CMD_FUNC(cmd_info) 346 { 347 if (remotecmdfilter(client, parc, parv)) 348 return; 349 350 if (hunt_server(client, recv_mtags, "INFO", 1, parc, parv) == HUNTED_ISME) 351 cmd_info_send(client); 352 } 353 354 /** LICENSE command 355 * Syntax: LICENSE [server] 356 */ 357 CMD_FUNC(cmd_license) 358 { 359 char **text = gnulicense; 360 361 if (remotecmdfilter(client, parc, parv)) 362 return; 363 364 if (hunt_server(client, recv_mtags, "LICENSE", 1, parc, parv) == HUNTED_ISME) 365 { 366 while (*text) 367 sendnumeric(client, RPL_INFO, *text++); 368 369 sendnumeric(client, RPL_INFO, ""); 370 sendnumeric(client, RPL_ENDOFINFO); 371 } 372 } 373 374 /** CREDITS command 375 * Syntax: CREDITS [servername] 376 */ 377 CMD_FUNC(cmd_credits) 378 { 379 char **text = unrealcredits; 380 381 if (remotecmdfilter(client, parc, parv)) 382 return; 383 384 if (hunt_server(client, recv_mtags, "CREDITS", 1, parc, parv) == HUNTED_ISME) 385 { 386 while (*text) 387 sendnumeric(client, RPL_INFO, *text++); 388 389 sendnumeric(client, RPL_INFO, ""); 390 sendnumericfmt(client, RPL_INFO, ":Birth Date: %s, compile # %s", creation, generation); 391 sendnumericfmt(client, RPL_INFO, ":On-line since %s", myctime(me.local->creationtime)); 392 sendnumeric(client, RPL_ENDOFINFO); 393 } 394 } 395 396 /** Return flags for a client (connection), eg 's' for TLS - used in STATS L/l */ 397 const char *get_client_status(Client *client) 398 { 399 static char buf[10]; 400 char *p = buf; 401 402 *p = '\0'; 403 *p++ = '['; 404 if (IsListening(client)) 405 { 406 if (client->umodes & LISTENER_NORMAL) 407 *p++ = '*'; 408 if (client->umodes & LISTENER_SERVERSONLY) 409 *p++ = 'S'; 410 if (client->umodes & LISTENER_CLIENTSONLY) 411 *p++ = 'C'; 412 if (client->umodes & LISTENER_TLS) 413 *p++ = 's'; 414 } 415 else 416 { 417 if (IsTLS(client)) 418 *p++ = 's'; 419 } 420 *p++ = ']'; 421 *p++ = '\0'; 422 return buf; 423 } 424 425 /** ERROR command - used by servers to indicate errors. 426 * Syntax: ERROR :<reason> 427 */ 428 CMD_FUNC(cmd_error) 429 { 430 const char *para; 431 432 if (!MyConnect(client)) 433 return; 434 435 para = (parc > 1 && *parv[1] != '\0') ? parv[1] : "<>"; 436 437 /* Errors from untrusted sources are ignored as any 438 * malicious user can send these, confusing IRCOps etc. 439 * One can always see the errors from the other side anyway. 440 */ 441 if (!IsServer(client) && !client->server) 442 return; 443 444 unreal_log(ULOG_ERROR, "link", "LINK_ERROR_MESSAGE", client, 445 "Error from $client: $error_message", 446 log_data_string("error_message", para), 447 client->server->conf ? log_data_link_block(client->server->conf) : NULL); 448 } 449 450 /** Save the tunefile (such as: highest seen connection count) */ 451 EVENT(save_tunefile) 452 { 453 FILE *tunefile; 454 455 tunefile = fopen(conf_files->tune_file, "w"); 456 if (!tunefile) 457 { 458 char *errstr = strerror(errno); 459 unreal_log(ULOG_WARNING, "config", "WRITE_TUNE_FILE_FAILED", NULL, 460 "Unable to write tunefile '$filename': $system_error", 461 log_data_string("filename", conf_files->tune_file), 462 log_data_string("system_error", errstr)); 463 return; 464 } 465 fprintf(tunefile, "0\n"); 466 fprintf(tunefile, "%d\n", irccounts.me_max); 467 fclose(tunefile); 468 } 469 470 /** Load the tunefile (such as: highest seen connection count) */ 471 void load_tunefile(void) 472 { 473 FILE *tunefile; 474 char buf[1024]; 475 476 tunefile = fopen(conf_files->tune_file, "r"); 477 if (!tunefile) 478 return; 479 /* We ignore the first line, hence the weird looking double fgets here... */ 480 if (!fgets(buf, sizeof(buf), tunefile) || !fgets(buf, sizeof(buf), tunefile)) 481 { 482 char *errstr = strerror(errno); 483 unreal_log(ULOG_WARNING, "config", "READ_TUNE_FILE_FAILED", NULL, 484 "Unable to read tunefile '$filename': $system_error", 485 log_data_string("filename", conf_files->tune_file), 486 log_data_string("system_error", errstr)); 487 } 488 irccounts.me_max = atol(buf); 489 fclose(tunefile); 490 } 491 492 /** Rehash motd and rule files (motd_file/rules_file and all tld entries). */ 493 void rehash_motdrules() 494 { 495 ConfigItem_tld *tlds; 496 497 reread_motdsandrules(); 498 for (tlds = conf_tld; tlds; tlds = tlds->next) 499 { 500 /* read_motd() accepts NULL in first arg and acts sanely */ 501 read_motd(tlds->motd_file, &tlds->motd); 502 read_motd(tlds->rules_file, &tlds->rules); 503 read_motd(tlds->smotd_file, &tlds->smotd); 504 read_motd(tlds->opermotd_file, &tlds->opermotd); 505 read_motd(tlds->botmotd_file, &tlds->botmotd); 506 } 507 } 508 509 /** Rehash motd and rules (only the default files) */ 510 void reread_motdsandrules() 511 { 512 read_motd(conf_files->motd_file, &motd); 513 read_motd(conf_files->rules_file, &rules); 514 read_motd(conf_files->smotd_file, &smotd); 515 read_motd(conf_files->botmotd_file, &botmotd); 516 read_motd(conf_files->opermotd_file, &opermotd); 517 read_motd(conf_files->svsmotd_file, &svsmotd); 518 } 519 520 extern void reinit_resolver(Client *client); 521 522 /** REHASH command - reload configuration file on server(s). 523 * Syntax: see HELPOP REHASH 524 */ 525 CMD_FUNC(cmd_rehash) 526 { 527 int x; 528 529 /* This is one of the (few) commands that cannot be handled 530 * by labeled-response accurately in all circumstances. 531 */ 532 labeled_response_inhibit = 1; 533 534 if (!ValidatePermissionsForPath("server:rehash",client,NULL,NULL,NULL)) 535 { 536 sendnumeric(client, ERR_NOPRIVILEGES); 537 return; 538 } 539 540 if ((parc < 3) || BadPtr(parv[2])) { 541 /* If the argument starts with a '-' (like -motd, -opermotd, etc) then it's 542 * assumed not to be a server. -- Syzop 543 */ 544 if (parv[1] && (parv[1][0] == '-')) 545 x = HUNTED_ISME; 546 else 547 x = hunt_server(client, recv_mtags, "REHASH", 1, parc, parv); 548 } else { 549 if (match_simple("-glob*", parv[1])) /* This is really ugly... hack to make /rehash -global -something work */ 550 { 551 x = HUNTED_ISME; 552 } else { 553 x = hunt_server(client, NULL, "REHASH", 1, parc, parv); 554 } 555 } 556 if (x != HUNTED_ISME) 557 return; /* Now forwarded or server didnt exist */ 558 559 if (!MyConnect(client)) 560 { 561 #ifndef REMOTE_REHASH 562 sendnumeric(client, ERR_NOPRIVILEGES); 563 return; 564 #endif 565 if (parv[2] == NULL) 566 { 567 if (loop.rehashing) 568 { 569 sendnotice(client, "A rehash is already in progress"); 570 return; 571 } 572 remote_rehash_client = client; 573 /* fallthrough... so we deal with this the same way as local rehashes */ 574 } 575 parv[1] = parv[2]; 576 } else { 577 /* Ok this is in an 'else' because it should be only executed for local clients, 578 * but it's totally unrelated to the above ;). 579 */ 580 if (parv[1] && match_simple("-glob*", parv[1])) 581 { 582 /* /REHASH -global [options] */ 583 Client *acptr; 584 585 /* Shift parv's to the left */ 586 parv[1] = parv[2]; 587 parv[2] = NULL; 588 parc--; 589 if (parv[1] && *parv[1] != '-') 590 { 591 sendnotice(client, "You cannot specify a server name after /REHASH -global, for obvious reasons"); 592 return; 593 } 594 /* Broadcast it in an inefficient, but backwards compatible way. */ 595 list_for_each_entry(acptr, &global_server_list, client_node) 596 { 597 if (acptr == &me) 598 continue; 599 sendto_one(acptr, NULL, ":%s REHASH %s %s", 600 client->name, 601 acptr->name, 602 parv[1] ? parv[1] : "-all"); 603 } 604 /* Don't return, continue, because we need to REHASH ourselves as well. */ 605 } 606 } 607 608 if (!BadPtr(parv[1]) && strcasecmp(parv[1], "-all")) 609 { 610 if (*parv[1] == '-') 611 { 612 if (!strncasecmp("-gar", parv[1], 4)) 613 { 614 loop.do_garbage_collect = 1; 615 RunHook(HOOKTYPE_REHASHFLAG, client, parv[1]); 616 return; 617 } 618 if (!strncasecmp("-dns", parv[1], 4)) 619 { 620 reinit_resolver(client); 621 return; 622 } 623 if (match_simple("-ssl*", parv[1]) || match_simple("-tls*", parv[1])) 624 { 625 unreal_log(ULOG_INFO, "config", "CONFIG_RELOAD_TLS", client, "Reloading all TLS related data. [by: $client.details]"); 626 reinit_tls(); 627 return; 628 } 629 RunHook(HOOKTYPE_REHASHFLAG, client, parv[1]); 630 return; 631 } 632 } 633 else 634 { 635 if (loop.rehashing) 636 { 637 sendnotice(client, "ERROR: A rehash is already in progress"); 638 return; 639 } 640 unreal_log(ULOG_INFO, "config", "CONFIG_RELOAD", client, "Rehashing server configuration file [by: $client.details]"); 641 } 642 643 /* Normal rehash, rehash motds&rules too, just like the on in the tld block will :p */ 644 sendnumeric(client, RPL_REHASHING, configfile); 645 request_rehash(client); 646 } 647 648 /** RESTART command - restart the server (discouraged command) 649 * parv[1] - password *OR* reason if no drpass { } block exists 650 * parv[2] - reason for restart (optional & only if drpass block exists) 651 */ 652 CMD_FUNC(cmd_restart) 653 { 654 const char *reason = parv[1]; 655 Client *acptr; 656 657 if (!MyUser(client)) 658 return; 659 660 /* Check permissions */ 661 if (!ValidatePermissionsForPath("server:restart",client,NULL,NULL,NULL)) 662 { 663 sendnumeric(client, ERR_NOPRIVILEGES); 664 return; 665 } 666 667 /* Syntax: /restart */ 668 if (parc == 1) 669 { 670 if (conf_drpass) 671 { 672 sendnumeric(client, ERR_NEEDMOREPARAMS, "RESTART"); 673 return; 674 } 675 } else 676 if (parc >= 2) 677 { 678 /* Syntax: /restart <pass> [reason] */ 679 if (conf_drpass) 680 { 681 if (!Auth_Check(client, conf_drpass->restartauth, parv[1])) 682 { 683 sendnumeric(client, ERR_PASSWDMISMATCH); 684 return; 685 } 686 reason = parv[2]; 687 } 688 } 689 690 list_for_each_entry(acptr, &lclient_list, lclient_node) 691 { 692 if (IsUser(acptr)) 693 sendnotice(acptr, "Server Restarted by %s", client->name); 694 else if (IsServer(acptr)) 695 sendto_one(acptr, NULL, ":%s ERROR :Restarted by %s: %s", 696 me.name, get_client_name(client, TRUE), reason ? reason : "No reason"); 697 } 698 699 server_reboot(reason ? reason : "No reason"); 700 } 701 702 /** Send short message of the day to the client */ 703 void short_motd(Client *client) 704 { 705 ConfigItem_tld *tld; 706 MOTDFile *themotd; 707 MOTDLine *motdline; 708 struct tm *tm; 709 char is_short; 710 711 tm = NULL; 712 is_short = 1; 713 714 tld = find_tld(client); 715 716 /* 717 * Try different sources of short MOTDs, falling back to the 718 * long MOTD. 719 */ 720 themotd = &smotd; 721 if (tld && tld->smotd.lines) 722 themotd = &tld->smotd; 723 724 /* try long MOTDs */ 725 if (!themotd->lines) 726 { 727 is_short = 0; 728 if (tld && tld->motd.lines) 729 themotd = &tld->motd; 730 else 731 themotd = &motd; 732 } 733 734 if (!themotd->lines) 735 { 736 sendnumeric(client, ERR_NOMOTD); 737 return; 738 } 739 if (themotd->last_modified.tm_year) 740 { 741 tm = &themotd->last_modified; /* for readability */ 742 sendnumeric(client, RPL_MOTDSTART, me.name); 743 sendnumericfmt(client, RPL_MOTD, ":- %d/%d/%d %d:%02d", tm->tm_mday, tm->tm_mon + 1, 744 1900 + tm->tm_year, tm->tm_hour, tm->tm_min); 745 } 746 if (is_short) 747 { 748 sendnumeric(client, RPL_MOTD, "This is the short MOTD. To view the complete MOTD type /motd"); 749 sendnumeric(client, RPL_MOTD, ""); 750 } 751 752 motdline = NULL; 753 if (themotd) 754 motdline = themotd->lines; 755 while (motdline) 756 { 757 sendnumeric(client, RPL_MOTD, motdline->line); 758 motdline = motdline->next; 759 } 760 761 if (!is_short) 762 { 763 /* If the admin does not use a short MOTD then we append the SVSMOTD here... 764 * If we did show a short motd then we don't append SVSMOTD, 765 * since they want to keep it short. 766 */ 767 motdline = svsmotd.lines; 768 while (motdline) 769 { 770 sendnumeric(client, RPL_MOTD, motdline->line); 771 motdline = motdline->next; 772 } 773 } 774 775 sendnumeric(client, RPL_ENDOFMOTD); 776 } 777 778 /** Read motd-like file, used for rules/motd/botmotd/opermotd/etc. 779 * @param filename Filename of file to read or URL. NULL is accepted and causes the *motd to be free()d. 780 * @param motd Reference to motd pointer (used for freeing if needed and for asynchronous remote MOTD support) 781 */ 782 void read_motd(const char *filename, MOTDFile *themotd) 783 { 784 FILE *fd; 785 struct tm *tm_tmp; 786 time_t modtime; 787 788 char line[512]; 789 char *tmp; 790 791 MOTDLine *last, *temp; 792 793 free_motd(themotd); 794 795 if (!filename) 796 return; 797 798 fd = fopen(filename, "r"); 799 if (!fd) 800 return; 801 802 /* record file modification time */ 803 modtime = unreal_getfilemodtime(filename); 804 tm_tmp = localtime(&modtime); 805 memcpy(&themotd->last_modified, tm_tmp, sizeof(struct tm)); 806 807 last = NULL; 808 while (fgets(line, sizeof(line), fd)) 809 { 810 if ((tmp = strchr(line, '\n'))) 811 *tmp = '\0'; 812 if ((tmp = strchr(line, '\r'))) 813 *tmp = '\0'; 814 815 if (strlen(line) > 510) 816 line[510] = '\0'; 817 818 temp = safe_alloc(sizeof(MOTDLine)); 819 safe_strdup(temp->line, line); 820 821 if (last) 822 last->next = temp; 823 else 824 /* handle the special case of the first line */ 825 themotd->lines = temp; 826 827 last = temp; 828 } 829 /* the file could be zero bytes long? */ 830 if (last) 831 last->next = NULL; 832 833 fclose(fd); 834 835 return; 836 } 837 838 /** Free the contents of a MOTDFile structure. 839 * The MOTDFile structure itself should be statically 840 * allocated and deallocated. If the caller wants, it must 841 * manually free the MOTDFile structure itself. 842 */ 843 void free_motd(MOTDFile *themotd) 844 { 845 MOTDLine *next, *motdline; 846 847 if (!themotd) 848 return; 849 850 for (motdline = themotd->lines; motdline; motdline = next) 851 { 852 next = motdline->next; 853 safe_free(motdline->line); 854 safe_free(motdline); 855 } 856 857 themotd->lines = NULL; 858 memset(&themotd->last_modified, '\0', sizeof(struct tm)); 859 } 860 861 /** DIE command - terminate the server 862 * DIE [password] 863 */ 864 CMD_FUNC(cmd_die) 865 { 866 Client *acptr; 867 868 if (!MyUser(client)) 869 return; 870 871 if (!ValidatePermissionsForPath("server:die",client,NULL,NULL,NULL)) 872 { 873 sendnumeric(client, ERR_NOPRIVILEGES); 874 return; 875 } 876 877 if (conf_drpass) /* See if we have and DIE/RESTART password */ 878 { 879 if (parc < 2) /* And if so, require a password :) */ 880 { 881 sendnumeric(client, ERR_NEEDMOREPARAMS, "DIE"); 882 return; 883 } 884 if (!Auth_Check(client, conf_drpass->dieauth, parv[1])) 885 { 886 sendnumeric(client, ERR_PASSWDMISMATCH); 887 return; 888 } 889 } 890 891 /* Let the +s know what is going on */ 892 unreal_log(ULOG_INFO, "main", "UNREALIRCD_STOP", client, 893 "Terminating server by request of $client.details"); 894 895 list_for_each_entry(acptr, &lclient_list, lclient_node) 896 { 897 if (IsUser(acptr)) 898 sendnotice(acptr, "Server Terminated by %s", 899 client->name); 900 else if (IsServer(acptr)) 901 sendto_one(acptr, NULL, ":%s ERROR :Terminated by %s", 902 me.name, get_client_name(client, TRUE)); 903 } 904 905 s_die(); 906 } 907 908 /** Server list (network) of pending connections */ 909 PendingNet *pendingnet = NULL; 910 911 /** Add server list (network) from 'client' connection */ 912 void add_pending_net(Client *client, const char *str) 913 { 914 PendingNet *net; 915 PendingServer *srv; 916 char *p, *name; 917 char buf[512]; 918 919 if (BadPtr(str) || !client) 920 return; 921 922 /* Skip any * at the beginning (indicating a reply), 923 * and work on a copy. 924 */ 925 if (*str == '*') 926 strlcpy(buf, str+1, sizeof(buf)); 927 else 928 strlcpy(buf, str, sizeof(buf)); 929 930 /* Allocate */ 931 net = safe_alloc(sizeof(PendingNet)); 932 net->client = client; 933 934 for (name = strtoken(&p, buf, ","); name; name = strtoken(&p, NULL, ",")) 935 { 936 if (!*name) 937 continue; 938 939 srv = safe_alloc(sizeof(PendingServer)); 940 strlcpy(srv->sid, name, sizeof(srv->sid)); 941 AddListItem(srv, net->servers); 942 } 943 944 AddListItem(net, pendingnet); 945 } 946 947 /** Free server list (network) previously added by 'client' */ 948 void free_pending_net(Client *client) 949 { 950 PendingNet *net, *net_next; 951 PendingServer *srv, *srv_next; 952 953 for (net = pendingnet; net; net = net_next) 954 { 955 net_next = net->next; 956 if (net->client == client) 957 { 958 for (srv = net->servers; srv; srv = srv_next) 959 { 960 srv_next = srv->next; 961 safe_free(srv); 962 } 963 DelListItem(net, pendingnet); 964 safe_free(net); 965 /* Don't break, there can be multiple objects */ 966 } 967 } 968 } 969 970 /** Find SID in any server list (network) that is pending, except 'exempt' */ 971 PendingNet *find_pending_net_by_sid_butone(const char *sid, Client *exempt) 972 { 973 PendingNet *net; 974 PendingServer *srv; 975 976 if (BadPtr(sid)) 977 return NULL; 978 979 for (net = pendingnet; net; net = net->next) 980 { 981 if (net->client == exempt) 982 continue; 983 for (srv = net->servers; srv; srv = srv->next) 984 if (!strcmp(srv->sid, sid)) 985 return net; 986 } 987 return NULL; 988 } 989 990 /** Search the pending connections list for any identical sids */ 991 Client *find_pending_net_duplicates(Client *cptr, Client **srv, char **sid) 992 { 993 PendingNet *net, *other; 994 PendingServer *s; 995 996 *srv = NULL; 997 *sid = NULL; 998 999 for (net = pendingnet; net; net = net->next) 1000 { 1001 if (net->client != cptr) 1002 continue; 1003 /* Ok, found myself */ 1004 for (s = net->servers; s; s = s->next) 1005 { 1006 char *curr_sid = s->sid; 1007 other = find_pending_net_by_sid_butone(curr_sid, cptr); 1008 if (other) 1009 { 1010 *srv = net->client; 1011 *sid = s->sid; 1012 return other->client; /* Found another (pending) server with identical numeric */ 1013 } 1014 } 1015 } 1016 1017 return NULL; 1018 } 1019 1020 /** Like find_pending_net_duplicates() but the other way around? Eh.. */ 1021 Client *find_non_pending_net_duplicates(Client *client) 1022 { 1023 PendingNet *net; 1024 PendingServer *s; 1025 Client *acptr; 1026 1027 for (net = pendingnet; net; net = net->next) 1028 { 1029 if (net->client != client) 1030 continue; 1031 /* Ok, found myself */ 1032 for (s = net->servers; s; s = s->next) 1033 { 1034 acptr = find_server(s->sid, NULL); 1035 if (acptr) 1036 return acptr; /* Found another (fully CONNECTED) server with identical numeric */ 1037 } 1038 } 1039 1040 return NULL; 1041 } 1042 1043 /** Parse CHANMODES= in PROTOCTL */ 1044 void parse_chanmodes_protoctl(Client *client, const char *str) 1045 { 1046 char *modes, *p; 1047 char copy[256]; 1048 1049 strlcpy(copy, str, sizeof(copy)); 1050 1051 modes = strtoken(&p, copy, ","); 1052 if (modes) 1053 { 1054 safe_strdup(client->server->features.chanmodes[0], modes); 1055 modes = strtoken(&p, NULL, ","); 1056 if (modes) 1057 { 1058 safe_strdup(client->server->features.chanmodes[1], modes); 1059 modes = strtoken(&p, NULL, ","); 1060 if (modes) 1061 { 1062 safe_strdup(client->server->features.chanmodes[2], modes); 1063 modes = strtoken(&p, NULL, ","); 1064 if (modes) 1065 { 1066 safe_strdup(client->server->features.chanmodes[3], modes); 1067 } 1068 } 1069 } 1070 } 1071 } 1072 1073 static char previous_langsinuse[512]; 1074 static int previous_langsinuse_ready = 0; 1075 1076 /** Check the nick character system (set::allowed-nickchars) for changes. 1077 * If there are changes, then we broadcast the new PROTOCTL NICKCHARS= to all servers. 1078 */ 1079 void charsys_check_for_changes(void) 1080 { 1081 const char *langsinuse = charsys_get_current_languages(); 1082 /* already called by charsys_finish() */ 1083 safe_strdup(me.server->features.nickchars, langsinuse); 1084 1085 if (!previous_langsinuse_ready) 1086 { 1087 previous_langsinuse_ready = 1; 1088 strlcpy(previous_langsinuse, langsinuse, sizeof(previous_langsinuse)); 1089 return; /* not booted yet. then we are done here. */ 1090 } 1091 1092 if (strcmp(langsinuse, previous_langsinuse)) 1093 { 1094 unreal_log(ULOG_INFO, "charsys", "NICKCHARS_CHANGED", NULL, 1095 "Permitted nick characters changed at runtime: $old_nickchars -> $new_nickchars", 1096 log_data_string("old_nickchars", previous_langsinuse), 1097 log_data_string("new_nickchars", langsinuse)); 1098 /* Broadcast change to all (locally connected) servers */ 1099 sendto_server(NULL, 0, 0, NULL, "PROTOCTL NICKCHARS=%s", langsinuse); 1100 } 1101 1102 strlcpy(previous_langsinuse, langsinuse, sizeof(previous_langsinuse)); 1103 } 1104 1105 /** Check if supplied server name is valid, that is: does not contain forbidden characters etc */ 1106 int valid_server_name(const char *name) 1107 { 1108 const char *p; 1109 1110 if (!valid_host(name, 0)) 1111 return 0; /* invalid hostname */ 1112 1113 if (!strchr(name, '.')) 1114 return 0; /* no dot */ 1115 1116 return 1; 1117 } 1118 1119 /** Check if the supplied name is a valid SID, as in: syntax. */ 1120 int valid_sid(const char *name) 1121 { 1122 if (strlen(name) != 3) 1123 return 0; 1124 if (!isdigit(*name)) 1125 return 0; 1126 if (!isdigit(name[1]) && !isupper(name[1])) 1127 return 0; 1128 if (!isdigit(name[2]) && !isupper(name[2])) 1129 return 0; 1130 return 1; 1131 } 1132 1133 /** Check if the supplied name is a valid UID, as in: syntax. */ 1134 int valid_uid(const char *name) 1135 { 1136 const char *p; 1137 1138 /* Enforce at least some minimum length */ 1139 if (strlen(name) < 6) 1140 return 0; 1141 1142 /* UID cannot be larger than IDLEN or it would be cut off later */ 1143 if (strlen(name) > IDLEN) 1144 return 0; 1145 1146 /* Must start with a digit */ 1147 if (!isdigit(*name)) 1148 return 0; 1149 1150 /* For all the remaining characters: digit or uppercase character */ 1151 for (p = name+1; *p; p++) 1152 if (!isdigit(*p) && !isupper(*p)) 1153 return 0; 1154 1155 return 1; 1156 } 1157 1158 /** Initialize the TKL subsystem */ 1159 void tkl_init(void) 1160 { 1161 memset(tklines, 0, sizeof(tklines)); 1162 memset(tklines_ip_hash, 0, sizeof(tklines_ip_hash)); 1163 } 1164 1165 /** Called when a server link is lost. 1166 * Used for logging only, API users can use the HOOKTYPE_SERVER_QUIT hook. 1167 */ 1168 void lost_server_link(Client *client, const char *tls_error_string) 1169 { 1170 if (IsServer(client)) 1171 { 1172 /* An already established link is now lost. */ 1173 // FIXME: we used to broadcast this GLOBALLY.. not anymore since the U6 rewrite.. is that what we want? 1174 if (tls_error_string) 1175 { 1176 /* TLS */ 1177 unreal_log(ULOG_ERROR, "link", "LINK_DISCONNECTED", client, 1178 "Lost server link to $client [$client.ip]: $tls_error_string", 1179 log_data_string("tls_error_string", tls_error_string), 1180 client->server->conf ? log_data_link_block(client->server->conf) : NULL); 1181 } else { 1182 /* NON-TLS */ 1183 unreal_log(ULOG_ERROR, "link", "LINK_DISCONNECTED", client, 1184 "Lost server link to $client [$client.ip]: $socket_error", 1185 log_data_socket_error(client->local->fd), 1186 client->server->conf ? log_data_link_block(client->server->conf) : NULL); 1187 } 1188 } else { 1189 /* A link attempt failed (it was never a fully connected server) */ 1190 /* We send these to local ops only */ 1191 if (tls_error_string) 1192 { 1193 /* TLS */ 1194 if (client->server->conf) 1195 { 1196 unreal_log(ULOG_ERROR, "link", "LINK_ERROR_CONNECT", client, 1197 client->server->conf->outgoing.file 1198 ? "Unable to link with server $client [$link_block.file]: $tls_error_string" 1199 : "Unable to link with server $client [$link_block.ip:$link_block.port]: $tls_error_string", 1200 log_data_string("tls_error_string", tls_error_string), 1201 log_data_link_block(client->server->conf)); 1202 } else { 1203 unreal_log(ULOG_ERROR, "link", "LINK_ERROR_CONNECT", client, 1204 "Unable to link with server $client: $tls_error_string", 1205 log_data_string("tls_error_string", tls_error_string)); 1206 } 1207 } else { 1208 /* non-TLS */ 1209 if (client->server->conf) 1210 { 1211 unreal_log(ULOG_ERROR, "link", "LINK_ERROR_CONNECT", client, 1212 client->server->conf->outgoing.file 1213 ? "Unable to link with server $client [$link_block.file]: $socket_error" 1214 : "Unable to link with server $client [$link_block.ip:$link_block.port]: $socket_error", 1215 log_data_socket_error(client->local->fd), 1216 log_data_link_block(client->server->conf)); 1217 } else { 1218 unreal_log(ULOG_ERROR, "link", "LINK_ERROR_CONNECT", client, 1219 "Unable to link with server $client: $socket_error", 1220 log_data_socket_error(client->local->fd)); 1221 } 1222 } 1223 } 1224 SetServerDisconnectLogged(client); 1225 } 1226 1227 /** Reject an insecure (outgoing) server link that isn't TLS. 1228 * This function is void and not int because it can be called from other void functions 1229 */ 1230 void reject_insecure_server(Client *client) 1231 { 1232 unreal_log(ULOG_ERROR, "link", "SERVER_STARTTLS_FAILED", client, 1233 "Could not link with server $client with TLS enabled. " 1234 "Please check logs on the other side of the link. " 1235 "If you insist with insecure linking then you can set link::options::outgoing::insecure " 1236 "(NOT recommended!)."); 1237 dead_socket(client, "Rejected server link without TLS"); 1238 } 1239 1240 /** Start server handshake - called after the outgoing connection has been established. 1241 * @param client The remote server 1242 */ 1243 void start_server_handshake(Client *client) 1244 { 1245 ConfigItem_link *aconf = client->server ? client->server->conf : NULL; 1246 1247 if (!aconf) 1248 { 1249 /* Should be impossible. */ 1250 unreal_log(ULOG_ERROR, "link", "BUG_LOST_CONFIGURATION_ON_HANDSHAKE", client, 1251 "Lost configuration while connecting to $client.details"); 1252 return; 1253 } 1254 1255 RunHook(HOOKTYPE_SERVER_HANDSHAKE_OUT, client); 1256 1257 sendto_one(client, NULL, "PASS :%s", (aconf->auth->type == AUTHTYPE_PLAINTEXT) ? aconf->auth->data : "*"); 1258 1259 send_protoctl_servers(client, 0); 1260 send_proto(client, aconf); 1261 /* Sending SERVER message moved to cmd_protoctl, so it's send after the first PROTOCTL 1262 * that we receive from the remote server. -- Syzop 1263 */ 1264 }