unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive |
ircd.c (31812B)
1 /************************************************************************ 2 * Unreal Internet Relay Chat Daemon, src/ircd.c 3 * Copyright (C) 1989-1990 Jarkko Oikarinen and 4 * University of Oulu, Computing Center 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 1, or (at your option) 9 * any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 */ 20 21 #include "unrealircd.h" 22 #include <ares.h> 23 24 #ifdef __FreeBSD__ 25 char *malloc_options = "h" MALLOC_FLAGS_EXTRA; 26 #endif 27 28 /* Forward declarations */ 29 void server_reboot(const char *); 30 void restart(const char *); 31 static void open_debugfile(), setup_signals(); 32 33 EVENT(loop_event) 34 { 35 if (loop.do_garbage_collect == 1) { 36 garbage_collect(NULL); 37 } 38 } 39 40 EVENT(garbage_collect) 41 { 42 extern int freelinks; 43 extern Link *freelink; 44 Link p; 45 int ii; 46 47 if (loop.do_garbage_collect == 1) 48 unreal_log(ULOG_INFO, "main", "GARBAGE_COLLECT_STARTED", NULL, "Doing garbage collection..."); 49 if (freelinks > HOW_MANY_FREELINKS_ALLOWED) { 50 ii = freelinks; 51 while (freelink && (freelinks > HOW_MANY_FREELINKS_ALLOWED)) { 52 freelinks--; 53 p.next = freelink; 54 freelink = freelink->next; 55 safe_free(p.next); 56 } 57 if (loop.do_garbage_collect == 1) { 58 loop.do_garbage_collect = 0; 59 unreal_log(ULOG_INFO, "main", "GARBAGE_COLLECT_STARTED", NULL, "Cleaned up $count garbage blocks", 60 log_data_integer("count", (ii - freelinks))); 61 } 62 } 63 if (loop.do_garbage_collect == 1) 64 loop.do_garbage_collect = 0; 65 } 66 67 /** Does this user match any TKL's? */ 68 int match_tkls(Client *client) 69 { 70 ConfigItem_ban *bconf = NULL; 71 char banbuf[1024]; 72 73 /* Process dynamic *LINES */ 74 if (find_tkline_match(client, 0)) 75 return 1; /* user killed */ 76 77 find_shun(client); /* check for shunned and take action, if so */ 78 79 if (IsUser(client)) 80 { 81 /* Check ban realname { } */ 82 if (!ValidatePermissionsForPath("immune",client,NULL,NULL,NULL) && (bconf = find_ban(NULL, client->info, CONF_BAN_REALNAME))) 83 { 84 unreal_log(ULOG_INFO, "tkl", "BAN_REALNAME", client, 85 "Banned client $client.details due to realname ban: $reason", 86 log_data_string("reason", bconf->reason ? bconf->reason : "no reason")); 87 88 if (bconf->reason) { 89 if (IsUser(client)) 90 snprintf(banbuf, sizeof(banbuf), "User has been banned (%s)", bconf->reason); 91 else 92 snprintf(banbuf, sizeof(banbuf), "Banned (%s)", bconf->reason); 93 exit_client(client, NULL, banbuf); 94 } else { 95 if (IsUser(client)) 96 exit_client(client, NULL, "User has been banned"); 97 else 98 exit_client(client, NULL, "Banned"); 99 } 100 return 1; /* stop processing, client is dead now */ 101 } 102 } 103 104 if (loop.do_bancheck_spamf_user && IsUser(client) && find_spamfilter_user(client, SPAMFLAG_NOWARN)) 105 return 1; 106 107 if (loop.do_bancheck_spamf_away && IsUser(client) && 108 client->user->away != NULL && 109 match_spamfilter(client, client->user->away, SPAMF_AWAY, "AWAY", NULL, SPAMFLAG_NOWARN, NULL)) 110 { 111 return 1; 112 } 113 114 return 0; 115 } 116 117 /** Time out connections that are still in handshake. */ 118 EVENT(handshake_timeout) 119 { 120 Client *client, *next; 121 122 list_for_each_entry_safe(client, next, &unknown_list, lclient_node) 123 { 124 if (client->local->creationtime && 125 ((TStime() - client->local->creationtime) > iConf.handshake_timeout) && 126 !(client->local->listener && (client->local->listener->socket_type == SOCKET_TYPE_UNIX))) 127 { 128 Hook *h; 129 int n = HOOK_CONTINUE; 130 const char *quitreason = "Registration Timeout"; 131 char reasonbuf[512]; 132 133 if (client->server && *client->server->by) 134 continue; /* handled by server module */ 135 136 for (h = Hooks[HOOKTYPE_PRE_LOCAL_HANDSHAKE_TIMEOUT]; h; h = h->next) 137 { 138 n = (*(h->func.intfunc))(client, &quitreason); 139 if (n == HOOK_ALLOW) 140 break; 141 } 142 if (n == HOOK_ALLOW) 143 continue; /* Do not exit the client due to registration timeout */ 144 145 /* Work on a copy here, since the 'quitreason' may point to 146 * some kind of buffer that gets freed in the exit code. 147 */ 148 strlcpy(reasonbuf, quitreason ? quitreason : "Registration Timeout", sizeof(reasonbuf)); 149 exit_client(client, NULL, reasonbuf); 150 continue; 151 } 152 } 153 } 154 155 /** Ping individual user, and check for ping timeout */ 156 void check_ping(Client *client) 157 { 158 char scratch[64]; 159 int ping = 0; 160 161 ping = client->local->class ? client->local->class->pingfreq : iConf.handshake_timeout; 162 163 /* If ping is less than or equal to the last time we received a command from them */ 164 if (ping > (TStime() - client->local->last_msg_received)) 165 return; /* some recent command was executed */ 166 167 if ( 168 /* If we have sent a ping */ 169 (IsPingSent(client) 170 /* And they had 2x ping frequency to respond */ 171 && ((TStime() - client->local->last_msg_received) >= (2 * ping))) 172 || 173 /* Or isn't registered and time spent is larger than ping (CONNECTTIMEOUT).. */ 174 (!IsRegistered(client) && (TStime() - client->local->fake_lag >= ping)) 175 ) 176 { 177 if (IsServer(client) || IsConnecting(client) || 178 IsHandshake(client) || IsTLSConnectHandshake(client)) 179 { 180 unreal_log(ULOG_ERROR, "link", "LINK_DISCONNECTED", client, 181 "Lost server link to $client [$client.ip]: No response (Ping timeout)", 182 client->server->conf ? log_data_link_block(client->server->conf) : NULL); 183 SetServerDisconnectLogged(client); 184 } 185 ircsnprintf(scratch, sizeof(scratch), "Ping timeout: %lld seconds", 186 (long long) (TStime() - client->local->last_msg_received)); 187 exit_client(client, NULL, scratch); 188 return; 189 } 190 else if (IsRegistered(client) && !IsPingSent(client)) 191 { 192 /* Time to send a PING */ 193 SetPingSent(client); 194 ClearPingWarning(client); 195 /* not nice but does the job */ 196 client->local->last_msg_received = TStime() - ping; 197 sendto_one(client, NULL, "PING :%s", me.name); 198 } 199 else if (!IsPingWarning(client) && PINGWARNING > 0 && 200 (IsServer(client) || IsHandshake(client) || IsConnecting(client) || 201 IsTLSConnectHandshake(client)) && 202 (TStime() - client->local->last_msg_received) >= (ping + PINGWARNING)) 203 { 204 SetPingWarning(client); 205 unreal_log(ULOG_WARNING, "link", "LINK_UNRELIABLE", client, 206 "Warning, no response from $client for $time_delta seconds", 207 log_data_integer("time_delta", PINGWARNING), 208 client->server->conf ? log_data_link_block(client->server->conf) : NULL); 209 } 210 211 return; 212 } 213 214 /** Check registered connections for ping timeout. Also, check for server bans. */ 215 EVENT(check_pings) 216 { 217 Client *client, *next; 218 219 list_for_each_entry_safe(client, next, &lclient_list, lclient_node) 220 { 221 /* Check TKLs for this user */ 222 if (loop.do_bancheck && match_tkls(client)) 223 continue; 224 check_ping(client); 225 /* don't touch 'client' after this as it may have been killed */ 226 } 227 228 list_for_each_entry_safe(client, next, &server_list, special_node) 229 { 230 check_ping(client); 231 } 232 233 loop.do_bancheck = loop.do_bancheck_spamf_user = loop.do_bancheck_spamf_away = 0; 234 /* done */ 235 } 236 237 /** Check for clients that are pending to be terminated */ 238 EVENT(check_deadsockets) 239 { 240 Client *client, *next; 241 time_t deadline = TStime() - 10; // TODO: make TLS handshake timeout configurable, hardcoded to 10s atm 242 243 list_for_each_entry_safe(client, next, &unknown_list, lclient_node) 244 { 245 /* No need to notify opers here. It's already done when dead socket is set */ 246 if (IsDeadSocket(client)) 247 { 248 if (!quick_close && (client->local->creationtime > deadline) && IsTLSHandshake(client)) 249 continue; /* give the client some more time */ 250 deadsocket_exit(client, 0); 251 continue; 252 } 253 } 254 255 list_for_each_entry_safe(client, next, &lclient_list, lclient_node) 256 { 257 /* No need to notify opers here. It's already done when dead socket is set */ 258 if (IsDeadSocket(client)) 259 { 260 ClearDeadSocket(client); /* CPR. So we send the error. */ 261 exit_client(client, NULL, client->local->error_str ? client->local->error_str : "Dead socket"); 262 continue; 263 } 264 } 265 266 /* Next is for clients that are already exited (unlike the above). 267 * The client is already out of all lists (channels, invites, etc etc) 268 * and 90% has been freed. Here we actually free the remaining parts. 269 * We don't have to send anything anymore since the socket is already closed. 270 */ 271 list_for_each_entry_safe(client, next, &dead_list, client_node) 272 { 273 if (!IsDead(client)) 274 abort(); /* impossible */ 275 list_del(&client->client_node); 276 free_client(client); 277 } 278 } 279 280 /* 281 ** bad_command 282 ** This is called when the commandline is not acceptable. 283 ** Give error message and exit without starting anything. 284 */ 285 static int bad_command(const char *argv0) 286 { 287 #ifndef _WIN32 288 if (!argv0) 289 argv0 = "unrealircd"; 290 291 printf("ERROR: Incorrect command line argument encountered.\n" 292 "This is the unrealircd BINARY. End-users should NOT call this binary directly.\n" 293 "Please run the SCRIPT instead: %s/unrealircd\n", SCRIPTDIR); 294 printf("Server not started\n\n"); 295 #else 296 if (!IsService) { 297 MessageBox(NULL, 298 "Usage: UnrealIRCd [-f configfile]\n", 299 "UnrealIRCD/32", MB_OK); 300 } 301 #endif 302 return (-1); 303 } 304 305 char chess[] = { 306 85, 110, 114, 101, 97, 108, 0 307 }; 308 309 extern void applymeblock(void); 310 311 extern MODVAR Event *events; 312 313 /** This functions resets a couple of timers and does other things that 314 * are absolutely cruicial when the clock is adjusted - particularly 315 * when the clock goes backwards. -- Syzop 316 */ 317 void fix_timers(void) 318 { 319 int i, cnt; 320 Client *client; 321 Event *e; 322 ConfigItem_link *lnk; 323 324 list_for_each_entry(client, &lclient_list, lclient_node) 325 { 326 if (client->local->fake_lag > TStime()) 327 client->local->fake_lag = TStime(); 328 if (client->local->last_msg_received > TStime()) 329 client->local->last_msg_received = TStime(); 330 if (client->local->idle_since > TStime()) 331 client->local->idle_since = TStime(); 332 333 /* users */ 334 if (MyUser(client)) 335 { 336 if (client->local->nexttarget > TStime()) 337 client->local->nexttarget = TStime(); 338 } 339 } 340 341 /* Reset all event timers */ 342 for (e = events; e; e = e->next) 343 { 344 if (e->last_run.tv_sec > TStime()) 345 { 346 e->last_run.tv_sec = TStime()-1; 347 e->last_run.tv_usec = 0; 348 } 349 } 350 351 /* Make sure autoconnect for servers still works (lnk->hold) */ 352 for (lnk = conf_link; lnk; lnk = lnk->next) 353 { 354 int t = lnk->class ? lnk->class->connfreq : 90; 355 356 if (lnk->hold > TStime() + t) 357 { 358 lnk->hold = TStime() + (t / 2); /* compromise */ 359 } 360 } 361 362 // FIXME: add some hook call here!! 363 } 364 365 366 /* MY tdiff... because 'double' sucks. 367 * This should work until 2038, and very likely after that as well 368 * because 'long' should be 64 bit on all systems by then... -- Syzop 369 */ 370 #define mytdiff(a, b) ((long)a - (long)b) 371 372 #define NEGATIVE_SHIFT_WARN -15 373 #define POSITIVE_SHIFT_WARN 20 374 375 void detect_timeshift_and_warn(void) 376 { 377 static time_t highesttimeofday=0, oldtimeofday=0, lasthighwarn=0; 378 379 if (oldtimeofday == 0) 380 oldtimeofday = timeofday; /* pretend everything is ok the first time.. */ 381 382 if (mytdiff(timeofday, oldtimeofday) < NEGATIVE_SHIFT_WARN) 383 { 384 /* tdiff = # of seconds of time set backwards (positive number! eg: 60) */ 385 time_t tdiff = oldtimeofday - timeofday; 386 unreal_log(ULOG_WARNING, "system", "SYSTEM_CLOCK_JUMP_BACKWARDS", NULL, 387 "System clock jumped back in time ~$time_delta seconds ($time_from -> $time_to)\n" 388 "Incorrect time for IRC servers is a serious problem. " 389 "Time being set backwards (system clock changed) is " 390 "even more serious and can cause clients to freeze, channels to be " 391 "taken over, and other issues.\n" 392 "Please be sure your clock is always synchronized before the IRCd is started!", 393 log_data_integer("time_delta", tdiff), 394 log_data_timestamp("time_from", oldtimeofday), 395 log_data_timestamp("time_to", timeofday)); 396 fix_timers(); 397 } else 398 if (mytdiff(timeofday, oldtimeofday) > POSITIVE_SHIFT_WARN) /* do not set too low or you get false positives */ 399 { 400 /* tdiff = # of seconds of time set forward (eg: 60) */ 401 time_t tdiff = timeofday - oldtimeofday; 402 unreal_log(ULOG_WARNING, "system", "SYSTEM_CLOCK_JUMP_FORWARDS", NULL, 403 "System clock jumped ~$time_delta seconds forward ($time_from -> $time_to)\n" 404 "Incorrect time for IRC servers is a serious problem. " 405 "Time being adjusted (by changing the system clock) " 406 "more than a few seconds forward/backward can lead to serious issues.\n" 407 "Please be sure your clock is always synchronized before the IRCd is started!", 408 log_data_integer("time_delta", tdiff), 409 log_data_timestamp("time_from", oldtimeofday), 410 log_data_timestamp("time_to", timeofday)); 411 fix_timers(); 412 } 413 414 if (highesttimeofday+NEGATIVE_SHIFT_WARN > timeofday) 415 { 416 if (lasthighwarn > timeofday) 417 lasthighwarn = timeofday; 418 if (timeofday - lasthighwarn > 300) 419 { 420 unreal_log(ULOG_WARNING, "system", "SYSTEM_CLOCK_JUMP_BACKWARDS_PREVIOUSLY", NULL, 421 "The system clock previously went backwards. Waiting for time to be OK again. This will be in $time_delta seconds.", 422 log_data_integer("time_delta", highesttimeofday - timeofday), 423 log_data_timestamp("time_from", highesttimeofday), 424 log_data_timestamp("time_to", timeofday)); 425 lasthighwarn = timeofday; 426 } 427 } else { 428 highesttimeofday = timeofday; 429 } 430 431 oldtimeofday = timeofday; 432 } 433 434 #define DETECT_HIGH_CONNECTION_RATE_SAMPLE_TIME 5 435 436 EVENT(detect_high_connection_rate) 437 { 438 static time_t last_detect_high_connection_rate_warning = 0; 439 440 /* 0 is special, means "never" */ 441 if (iConf.high_connection_rate == 0) 442 { 443 quick_close = 0; 444 connections_past_period=0; /* reset */ 445 return; 446 } 447 448 if (connections_past_period > iConf.high_connection_rate*DETECT_HIGH_CONNECTION_RATE_SAMPLE_TIME) 449 { 450 quick_close = 1; 451 } else { 452 quick_close = 0; 453 } 454 455 if (OpenFiles >= maxclients-10) 456 quick_close = 1; 457 458 /* Send a warning to IRCOps every XYZ time */ 459 if (quick_close && (TStime() - last_detect_high_connection_rate_warning > 600) && connections_past_period) 460 { 461 if (connections_past_period >= iConf.high_connection_rate*DETECT_HIGH_CONNECTION_RATE_SAMPLE_TIME) 462 { 463 unreal_log(ULOG_WARNING, "htm", "HIGH_CONNECTION_RATE", NULL, 464 "High rate of connection attempts detected: $connects_per_second/sec exceeds $limit/sec: some minor functionality is now disabled. " 465 "This could be an attack, or lots of genuine users connecting after a network outage.\n" 466 "This message will appear every 10 minutes for as long as this is the case. " 467 "You will NOT get a notification if all is normal again (which is evaluated every $sample_time seconds). " 468 "See https://www.unrealircd.org/docs/FAQ#hi-conn-rate", 469 log_data_integer("connects_per_second", connections_past_period/DETECT_HIGH_CONNECTION_RATE_SAMPLE_TIME), 470 log_data_integer("limit", iConf.high_connection_rate), 471 log_data_integer("sample_time", DETECT_HIGH_CONNECTION_RATE_SAMPLE_TIME)); 472 } else { 473 unreal_log(ULOG_WARNING, "htm", "HIGH_CONNECTION_RATE", NULL, 474 "High amount of connections in use ($connections is near limit of $maxclients maximum clients). Some minor functionality is now disabled. " 475 "This could be an attack, or lots of genuine users connecting.\n" 476 "This message will appear every 10 minutes for as long as this is the case. " 477 "You will NOT get a notification if all is normal again (which is evaluated every $sample_time seconds). " 478 "See https://www.unrealircd.org/docs/FAQ#hi-conn-rate", 479 log_data_integer("connections", OpenFiles), 480 log_data_integer("maxclients", maxclients), 481 log_data_integer("sample_time", DETECT_HIGH_CONNECTION_RATE_SAMPLE_TIME)); 482 } 483 last_detect_high_connection_rate_warning = TStime(); 484 } 485 486 connections_past_period=0; /* reset */ 487 } 488 489 void SetupEvents(void) 490 { 491 /* Start events */ 492 EventAdd(NULL, "tunefile", save_tunefile, NULL, 300*1000, 0); 493 EventAdd(NULL, "garbage", garbage_collect, NULL, GARBAGE_COLLECT_EVERY*1000, 0); 494 EventAdd(NULL, "loop", loop_event, NULL, 1000, 0); 495 EventAdd(NULL, "unrealdns_removeoldrecords", unrealdns_removeoldrecords, NULL, 15000, 0); 496 EventAdd(NULL, "check_pings", check_pings, NULL, 1000, 0); 497 EventAdd(NULL, "check_deadsockets", check_deadsockets, NULL, 1000, 0); 498 EventAdd(NULL, "handshake_timeout", handshake_timeout, NULL, 1000, 0); 499 EventAdd(NULL, "tls_check_expiry", tls_check_expiry, NULL, (86400/2)*1000, 0); 500 EventAdd(NULL, "unrealdb_expire_secret_cache", unrealdb_expire_secret_cache, NULL, 61000, 0); 501 EventAdd(NULL, "memory_log_cleaner", memory_log_cleaner, NULL, 61500, 0); 502 EventAdd(NULL, "detect_high_connection_rate", detect_high_connection_rate, NULL, 1000*DETECT_HIGH_CONNECTION_RATE_SAMPLE_TIME, 0); 503 EventAdd(NULL, "central_spamfilter_download_evt", central_spamfilter_download_evt, NULL, 5000, 0); 504 } 505 506 /** The main function. This will call SocketLoop() once the server is ready. */ 507 #ifndef _WIN32 508 int main(int argc, char *argv[]) 509 #else 510 int InitUnrealIRCd(int argc, char *argv[]) 511 #endif 512 { 513 #ifndef _WIN32 514 uid_t uid, euid; 515 gid_t gid, egid; 516 #endif 517 #ifdef HAVE_PSTAT 518 union pstun pstats; 519 #endif 520 #ifndef _WIN32 521 struct rlimit corelim; 522 #endif 523 524 gettimeofday(&timeofday_tv, NULL); 525 timeofday = timeofday_tv.tv_sec; 526 527 safe_strdup(configfile, CONFIGFILE); 528 529 init_random(); /* needs to be done very early!! */ 530 if (sodium_init() < 0) 531 { 532 fprintf(stderr, "Failed to initialize sodium library -- error accessing random device?\n"); 533 exit(-1); 534 } 535 536 memset(&botmotd, '\0', sizeof(MOTDFile)); 537 memset(&rules, '\0', sizeof(MOTDFile)); 538 memset(&opermotd, '\0', sizeof(MOTDFile)); 539 memset(&motd, '\0', sizeof(MOTDFile)); 540 memset(&smotd, '\0', sizeof(MOTDFile)); 541 memset(&svsmotd, '\0', sizeof(MOTDFile)); 542 memset(&me, 0, sizeof(me)); 543 me.local = safe_alloc(sizeof(LocalClient)); 544 memset(&loop, 0, sizeof(loop)); 545 546 init_hash(); 547 548 SetupEvents(); 549 550 #ifdef _WIN32 551 CreateMutex(NULL, FALSE, "UnrealMutex"); 552 SetErrorMode(SEM_FAILCRITICALERRORS); 553 #endif 554 #if !defined(_WIN32) && !defined(_AMIGA) 555 uid = getuid(); 556 euid = geteuid(); 557 gid = getgid(); 558 egid = getegid(); 559 560 if (euid == 0) 561 { 562 fprintf(stderr, 563 "** ERROR **\n" 564 "You attempted to run UnrealIRCd as root. This is VERY DANGEROUS\n" 565 "as any compromise of your UnrealIRCd will result in full\n" 566 "privileges to the attacker on the entire machine.\n" 567 "You MUST start UnrealIRCd as a different user!\n" 568 "\n" 569 "For more information, see:\n" 570 "https://www.unrealircd.org/docs/Do_not_run_as_root\n" 571 "\n"); 572 exit(1); 573 } 574 #endif 575 #ifndef _WIN32 576 myargv = argv; 577 #else 578 cmdLine = GetCommandLine(); 579 #endif 580 #ifndef _WIN32 581 (void)umask(077); /* better safe than sorry --SRB */ 582 #else 583 init_winsock(); 584 #endif 585 setup_signals(); 586 587 memset(&irccounts, '\0', sizeof(irccounts)); 588 irccounts.servers = 1; 589 590 mp_pool_init(); 591 dbuf_init(); 592 initlists(); 593 initlist_channels(); 594 595 early_init_tls(); 596 url_init(); 597 tkl_init(); 598 umode_init(); 599 extcmode_init(); 600 efunctions_init(); 601 clear_scache_hash_table(); 602 #ifndef _WIN32 603 /* Make it so we can dump core */ 604 corelim.rlim_cur = corelim.rlim_max = RLIM_INFINITY; 605 setrlimit(RLIMIT_CORE, &corelim); 606 #endif 607 init_sys(); 608 609 #if !defined(_WIN32) 610 #ifndef _WIN32 611 mkdir(TMPDIR, S_IRUSR|S_IWUSR|S_IXUSR); /* Create the tmp dir, if it doesn't exist */ 612 mkdir(CACHEDIR, S_IRUSR|S_IWUSR|S_IXUSR); /* Create the cache dir, if it doesn't exist */ 613 #else 614 mkdir(TMPDIR); 615 mkdir(CACHEDIR); 616 #endif 617 if (chdir(TMPDIR)) { 618 # ifndef _WIN32 619 perror("chdir"); 620 fprintf(stderr, "ERROR: Unable to change to directory '%s'\n", TMPDIR); 621 # else 622 if (!IsService) { 623 MessageBox(NULL, strerror(GetLastError()), 624 "UnrealIRCD/32: chdir()", MB_OK); 625 } 626 # endif 627 exit(-1); 628 } 629 #endif 630 631 /* 632 * ** All command line parameters have the syntax "-fstring" 633 * ** or "-f string" (e.g. the space is optional). String may 634 * ** be empty. Flag characters cannot be concatenated (like 635 * ** "-fxyz"), it would conflict with the form "-fstring". 636 */ 637 while (--argc > 0 && (*++argv)[0] == '-') { 638 char *p = argv[0] + 1; 639 int flag = *p++; 640 if (flag == '\0' || *p == '\0') { 641 if (argc > 1 && argv[1][0] != '-') { 642 p = *++argv; 643 argc -= 1; 644 } else 645 p = ""; 646 } 647 switch (flag) 648 { 649 case 'F': 650 bootopt |= BOOT_NOFORK; 651 break; 652 case 'f': 653 #ifndef _WIN32 654 if ((uid != euid) || (gid != egid)) 655 { 656 printf("ERROR: Command line config with a setuid/setgid ircd is not allowed"); 657 exit(1); 658 } 659 #endif 660 safe_strdup(configfile, p); 661 convert_to_absolute_path(&configfile, CONFDIR); 662 break; 663 #if 0 664 case 'S': 665 charsys_dump_table(p ? p : "*"); 666 //unrealdb_test(); 667 #endif 668 #ifndef _WIN32 669 case 't': 670 bootopt |= BOOT_TTY; 671 break; 672 case 'v': 673 (void)printf("%s\n", version); 674 #else 675 case 'v': 676 if (!IsService) { 677 MessageBox(NULL, version, 678 "UnrealIRCD/Win32 version", MB_OK); 679 } 680 #endif 681 exit(0); 682 case 'C': 683 config_verbose = atoi(p); 684 break; 685 case 'c': 686 loop.config_test = 1; 687 break; 688 case 'x': 689 #ifdef DEBUGMODE 690 debuglevel = atoi(p); 691 debugmode = *p ? p : "0"; 692 bootopt |= BOOT_DEBUG; 693 break; 694 #else 695 # ifndef _WIN32 696 (void)fprintf(stderr, 697 "%s: DEBUGMODE must be defined for -x y\n", 698 myargv[0]); 699 # else 700 if (!IsService) { 701 MessageBox(NULL, 702 "DEBUGMODE must be defined for -x option", 703 "UnrealIRCD/32", MB_OK); 704 } 705 # endif 706 exit(0); 707 #endif 708 case 'K': 709 { 710 char *p = NULL; 711 if (chdir(TMPDIR) < 0) 712 { 713 fprintf(stderr, "Could not change to directory '%s'\n", TMPDIR); 714 exit(1); 715 } 716 fprintf(stderr, "Starting crash test!\n"); 717 *p = 'a'; 718 fprintf(stderr, "It is impossible to get here\n"); 719 exit(0); 720 } 721 case 'R': 722 report_crash(); 723 exit(0); 724 #ifndef _WIN32 725 case 'm': 726 modulemanager(argc, argv); 727 exit(0); 728 #endif 729 case '8': 730 utf8_test(); 731 exit(0); 732 case 'L': 733 loop.boot_function = link_generator; 734 break; 735 default: 736 #ifndef _WIN32 737 return bad_command(myargv[0]); 738 #else 739 return bad_command(NULL); 740 #endif 741 break; 742 } 743 } 744 745 #ifndef _WIN32 746 /* 747 * didn't set debuglevel 748 */ 749 /* 750 * but asked for debugging output to tty 751 */ 752 if ((debuglevel < 0) && (bootopt & BOOT_TTY)) { 753 (void)fprintf(stderr, 754 "you specified -t without -x. use -x <n>\n"); 755 exit(-1); 756 } 757 #endif 758 759 /* HACK! This ifndef should be removed when the restart-on-w32-brings-up-dialog bug 760 * is fixed. This is just an ugly "ignore the invalid parameter" thing ;). -- Syzop 761 */ 762 #ifndef _WIN32 763 if (argc > 0) 764 return bad_command(myargv[0]); /* This should exit out */ 765 #endif 766 #ifndef _WIN32 767 fprintf(stderr, "%s", unreallogo); 768 fprintf(stderr, " v%s\n\n", VERSIONONLY); 769 fprintf(stderr, "UnrealIRCd is brought to you by Bram Matthys (Syzop),\n" 770 "Krzysztof Beresztant (k4be), Gottem and i\n\n"); 771 772 fprintf(stderr, "UnrealIRCd is free and Open Source software. " 773 "If you can, consider making a donation at " 774 "https://www.unrealircd.org/index/donations " 775 "to support us.\n\n"); 776 777 fprintf(stderr, "UnrealIRCd is using the following libraries:\n"); 778 fprintf(stderr, "* %s\n", SSLeay_version(SSLEAY_VERSION)); 779 fprintf(stderr, "* libsodium %s\n", sodium_version_string()); 780 #ifdef USE_LIBCURL 781 fprintf(stderr, "* %s\n", curl_version()); 782 #endif 783 fprintf(stderr, "* c-ares %s\n", ares_version(NULL)); 784 fprintf(stderr, "* %s\n", pcre2_version()); 785 #endif 786 #if JANSSON_VERSION_HEX >= 0x020D00 787 fprintf(stderr, "* jansson %s\n", jansson_version_str()); 788 #endif 789 check_user_limit(); 790 #ifndef _WIN32 791 fprintf(stderr, "\n"); 792 fprintf(stderr, "This server can handle %d concurrent sockets (%d clients + %d reserve)\n\n", 793 maxclients+reserved_fds, maxclients, reserved_fds); 794 #endif 795 init_CommandHash(); 796 initwhowas(); 797 initstats(); 798 if (!loop.config_test) 799 DeleteTempModules(); 800 #if !defined(_WIN32) && !defined(_AMIGA) && !defined(OSXTIGER) && DEFAULT_PERMISSIONS != 0 801 /* Hack to stop people from being able to read the config file */ 802 (void)chmod(CPATH, DEFAULT_PERMISSIONS); 803 #endif 804 init_dynconf(); 805 clicap_init(); 806 /* 807 * Add default class 808 */ 809 default_class = safe_alloc(sizeof(ConfigItem_class)); 810 default_class->flag.permanent = 1; 811 default_class->pingfreq = 120; 812 default_class->maxclients = 100; 813 default_class->sendq = DEFAULT_RECVQ; 814 default_class->name = "default"; 815 AddListItem(default_class, conf_class); 816 if (config_read_start() < 0) 817 exit(-1); 818 while (!is_config_read_finished()) 819 { 820 gettimeofday(&timeofday_tv, NULL); 821 timeofday = timeofday_tv.tv_sec; 822 url_socket_timeout(NULL); 823 unrealdns_timeout(NULL); 824 fd_select(500); 825 } 826 if (config_test() < 0) 827 exit(-1); 828 load_tunefile(); 829 make_umodestr(); 830 SetListening(&me); 831 me.local->fd = -1; 832 SetMe(&me); 833 make_server(&me); 834 umodes_check_for_changes(); 835 charsys_check_for_changes(); 836 if (!find_command_simple("PRIVMSG")) 837 { 838 config_error("Someone forgot to load modules with proper commands in them. READ THE DOCUMENTATION"); 839 exit(-4); 840 } 841 842 if (!init_tls()) 843 { 844 config_error("Failed to load TLS (see errors above). UnrealIRCd can not start."); 845 #ifdef _WIN32 846 win_error(); /* display error dialog box */ 847 #endif 848 exit(9); 849 } 850 if (loop.config_test) 851 { 852 unreal_log(ULOG_INFO, "config", "CONFIG_PASSED", NULL, "Configuration test passed OK"); 853 fflush(stderr); 854 exit(0); 855 } 856 if (loop.boot_function) 857 loop.boot_function(); 858 open_debugfile(); 859 me.local->port = 6667; /* pointless? */ 860 applymeblock(); 861 #ifdef HAVE_SYSLOG 862 openlog("ircd", LOG_PID | LOG_NDELAY, LOG_DAEMON); 863 #endif 864 config_run(); 865 unreal_log(ULOG_INFO, "main", "UNREALIRCD_START", NULL, "UnrealIRCd started."); 866 867 read_motd(conf_files->botmotd_file, &botmotd); 868 read_motd(conf_files->rules_file, &rules); 869 read_motd(conf_files->opermotd_file, &opermotd); 870 read_motd(conf_files->motd_file, &motd); 871 read_motd(conf_files->smotd_file, &smotd); 872 read_motd(conf_files->svsmotd_file, &svsmotd); 873 874 me.hopcount = 0; 875 me.local->authfd = -1; 876 me.user = NULL; 877 me.direction = &me; 878 879 /* 880 * This listener will never go away 881 */ 882 me_hash = find_or_add(me.name); 883 timeofday = time(NULL); 884 me.local->last_msg_received = me.local->fake_lag = me.local->creationtime = me.server->boottime = TStime(); 885 me.server->features.protocol = UnrealProtocol; 886 safe_strdup(me.server->features.software, version); 887 add_to_client_hash_table(me.name, &me); 888 add_to_id_hash_table(me.id, &me); 889 list_add(&me.client_node, &global_server_list); 890 #if !defined(_AMIGA) && !defined(_WIN32) && !defined(NO_FORKING) 891 if (!(bootopt & BOOT_NOFORK)) 892 { 893 pid_t p; 894 p = fork(); 895 if (p < 0) 896 { 897 fprintf(stderr, "Could not create background job. Call to fork() failed: %s\n", 898 strerror(errno)); 899 exit(-1); 900 } 901 if (p > 0) 902 { 903 /* Background job created and we are the parent. We can terminate. */ 904 exit(0); 905 } 906 /* Background process (child) continues below... */ 907 close_std_descriptors(); 908 fd_fork(); 909 loop.forked = 1; 910 } 911 #endif 912 #ifdef _WIN32 913 loop.forked = 1; 914 #endif 915 916 fix_timers(); 917 write_pidfile(); 918 loop.booted = 1; 919 #if defined(HAVE_SETPROCTITLE) 920 setproctitle("%s", me.name); 921 #elif defined(HAVE_PSTAT) 922 pstats.pst_command = me.name; 923 pstat(PSTAT_SETCMD, pstats, strlen(me.name), 0, 0); 924 #elif defined(HAVE_PSSTRINGS) 925 PS_STRINGS->ps_nargvstr = 1; 926 PS_STRINGS->ps_argvstr = me.name; 927 #endif 928 module_loadall(); 929 loop.config_status = CONFIG_STATUS_COMPLETE; 930 931 #ifndef _WIN32 932 SocketLoop(NULL); 933 #endif 934 return 1; 935 } 936 937 /** The main loop that the server will run all the time. 938 * On Windows this is a thread, on *NIX we simply jump here from main() 939 * when the server is ready. 940 */ 941 void SocketLoop(void *dummy) 942 { 943 struct timeval doevents_tv, process_clients_tv; 944 945 memset(&doevents_tv, 0, sizeof(doevents_tv)); 946 memset(&process_clients_tv, 0, sizeof(process_clients_tv)); 947 948 while (1) 949 { 950 gettimeofday(&timeofday_tv, NULL); 951 timeofday = timeofday_tv.tv_sec; 952 953 detect_timeshift_and_warn(); 954 955 if (minimum_msec_since_last_run(&doevents_tv, 250)) 956 DoEvents(); 957 958 /* Update statistics */ 959 if (irccounts.clients > irccounts.global_max) 960 irccounts.global_max = irccounts.clients; 961 if (irccounts.me_clients > irccounts.me_max) 962 irccounts.me_max = irccounts.me_clients; 963 964 /* Process I/O */ 965 fd_select(SOCKETLOOP_MAX_DELAY); 966 967 if (minimum_msec_since_last_run(&process_clients_tv, 200)) 968 process_clients(); 969 970 /* Check if there are pending "actions". 971 * These are actions that should be done outside of 972 * process_clients() and fd_select() when we are not 973 * processing any clients. 974 */ 975 if (dorehash) 976 { 977 request_rehash(NULL); 978 dorehash = 0; 979 } 980 if (dorestart) 981 { 982 server_reboot("SIGINT"); 983 } 984 if (doreloadcert) 985 { 986 unreal_log(ULOG_INFO, "config", "CONFIG_RELOAD_TLS", NULL, "Reloading all TLS related data (./unrealircd reloadtls)"); 987 reinit_tls(); 988 doreloadcert = 0; 989 } 990 /* If rehashing, check if we are done. */ 991 if (loop.rehashing && is_config_read_finished()) 992 rehash_internal(loop.rehash_save_client); 993 } 994 } 995 996 /* 997 * open_debugfile 998 * 999 * If the -t option is not given on the command line when the server is 1000 * started, all debugging output is sent to the file set by LPATH in config.h 1001 * If the debuglevel is not set from the command line by -x, use /dev/null 1002 * as the dummy logfile as long as DEBUGMODE has been defined, else don't 1003 * waste the fd. 1004 */ 1005 static void open_debugfile(void) 1006 { 1007 #ifdef DEBUGMODE 1008 int fd; 1009 Client *client; 1010 if (debuglevel >= 0) { 1011 client = make_client(NULL, NULL); 1012 client->local->fd = 2; 1013 SetLog(client); 1014 client->local->port = debuglevel; 1015 client->flags = 0; 1016 1017 strlcpy(client->local->sockhost, me.local->sockhost, sizeof client->local->sockhost); 1018 # ifndef _WIN32 1019 /*(void)printf("isatty = %d ttyname = %#x\n", 1020 isatty(2), (u_int)ttyname(2)); */ 1021 if (!(bootopt & BOOT_TTY)) { /* leave debugging output on fd 2 */ 1022 if (truncate(LOGFILE, 0) < 0) 1023 fprintf(stderr, "WARNING: could not truncate log file '%s'\n", LOGFILE); 1024 if ((fd = open(LOGFILE, O_WRONLY | O_CREAT, 0600)) < 0) 1025 if ((fd = open("/dev/null", O_WRONLY)) < 0) 1026 exit(-1); 1027 1028 #if 1 1029 client->local->fd = fd; 1030 debugfd = fd; 1031 #else 1032 /* if (fd != 2) { 1033 (void)dup2(fd, 2); 1034 (void)close(fd); 1035 } -- hands off stderr! */ 1036 #endif 1037 strlcpy(client->name, LOGFILE, sizeof(client->name)); 1038 } else if (isatty(2) && ttyname(2)) 1039 strlcpy(client->name, ttyname(2), sizeof(client->name)); 1040 else 1041 # endif 1042 strlcpy(client->name, "FD2-Pipe", sizeof(client->name)); 1043 } 1044 #endif 1045 } 1046 1047 static void setup_signals() 1048 { 1049 #ifndef _WIN32 1050 struct sigaction act; 1051 act.sa_handler = SIG_IGN; 1052 act.sa_flags = 0; 1053 (void)sigemptyset(&act.sa_mask); 1054 (void)sigaddset(&act.sa_mask, SIGPIPE); 1055 (void)sigaddset(&act.sa_mask, SIGALRM); 1056 #ifdef SIGWINCH 1057 (void)sigaddset(&act.sa_mask, SIGWINCH); 1058 (void)sigaction(SIGWINCH, &act, NULL); 1059 #endif 1060 (void)sigaction(SIGPIPE, &act, NULL); 1061 act.sa_handler = ignore_this_signal; 1062 (void)sigaction(SIGALRM, &act, NULL); 1063 act.sa_handler = s_rehash; 1064 (void)sigemptyset(&act.sa_mask); 1065 (void)sigaddset(&act.sa_mask, SIGHUP); 1066 (void)sigaction(SIGHUP, &act, NULL); 1067 act.sa_handler = s_restart; 1068 (void)sigaddset(&act.sa_mask, SIGINT); 1069 (void)sigaction(SIGINT, &act, NULL); 1070 act.sa_handler = s_die; 1071 (void)sigaddset(&act.sa_mask, SIGTERM); 1072 (void)sigaction(SIGTERM, &act, NULL); 1073 act.sa_handler = s_reloadcert; 1074 (void)sigemptyset(&act.sa_mask); 1075 (void)sigaddset(&act.sa_mask, SIGUSR1); 1076 (void)sigaction(SIGUSR1, &act, NULL); 1077 #endif 1078 }