unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

ircd.c (29336B)

      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 
    242 	list_for_each_entry_safe(client, next, &unknown_list, lclient_node)
    243 	{
    244 		/* No need to notify opers here. It's already done when dead socket is set */
    245 		if (IsDeadSocket(client))
    246 		{
    247 			ClearDeadSocket(client); /* CPR. So we send the error. */
    248 			exit_client(client, NULL, client->local->error_str ? client->local->error_str : "Dead socket");
    249 			continue;
    250 		}
    251 	}
    252 
    253 	list_for_each_entry_safe(client, next, &lclient_list, lclient_node)
    254 	{
    255 		/* No need to notify opers here. It's already done when dead socket is set */
    256 		if (IsDeadSocket(client))
    257 		{
    258 			ClearDeadSocket(client); /* CPR. So we send the error. */
    259 			exit_client(client, NULL, client->local->error_str ? client->local->error_str : "Dead socket");
    260 			continue;
    261 		}
    262 	}
    263 
    264 	/* Next is for clients that are already exited (unlike the above).
    265 	 * The client is already out of all lists (channels, invites, etc etc)
    266 	 * and 90% has been freed. Here we actually free the remaining parts.
    267 	 * We don't have to send anything anymore.
    268 	 */
    269 	list_for_each_entry_safe(client, next, &dead_list, client_node)
    270 	{
    271 		if (!IsDead(client))
    272 			abort(); /* impossible */
    273 		list_del(&client->client_node);
    274 		free_client(client);
    275 	}
    276 }
    277 
    278 /*
    279 ** bad_command
    280 **	This is called when the commandline is not acceptable.
    281 **	Give error message and exit without starting anything.
    282 */
    283 static int bad_command(const char *argv0)
    284 {
    285 #ifndef _WIN32
    286 	if (!argv0)
    287 		argv0 = "unrealircd";
    288 
    289 	printf("ERROR: Incorrect command line argument encountered.\n"
    290 	       "This is the unrealircd BINARY. End-users should NOT call this binary directly.\n"
    291 	       "Please run the SCRIPT instead: %s/unrealircd\n", SCRIPTDIR);
    292 	printf("Server not started\n\n");
    293 #else
    294 	if (!IsService) {
    295 		MessageBox(NULL,
    296 		    "Usage: UnrealIRCd [-f configfile]\n",
    297 		    "UnrealIRCD/32", MB_OK);
    298 	}
    299 #endif
    300 	return (-1);
    301 }
    302 
    303 char chess[] = {
    304 	85, 110, 114, 101, 97, 108, 0
    305 };
    306 
    307 extern void applymeblock(void);
    308 
    309 extern MODVAR Event *events;
    310 
    311 /** This functions resets a couple of timers and does other things that
    312  * are absolutely cruicial when the clock is adjusted - particularly
    313  * when the clock goes backwards. -- Syzop
    314  */
    315 void fix_timers(void)
    316 {
    317 	int i, cnt;
    318 	Client *client;
    319 	Event *e;
    320 	struct ThrottlingBucket *thr;
    321 	ConfigItem_link *lnk;
    322 
    323 	list_for_each_entry(client, &lclient_list, lclient_node)
    324 	{
    325 		if (client->local->fake_lag > TStime())
    326 			client->local->fake_lag = TStime();
    327 		if (client->local->last_msg_received > TStime())
    328 			client->local->last_msg_received = TStime();
    329 		if (client->local->idle_since > TStime())
    330 			client->local->idle_since = TStime();
    331 
    332 		/* users */
    333 		if (MyUser(client))
    334 		{
    335 			if (client->local->next_nick_allowed > TStime())
    336 				client->local->next_nick_allowed = TStime();
    337 			if (client->local->nexttarget > TStime())
    338 				client->local->nexttarget = TStime();
    339 		}
    340 	}
    341 
    342 	/* Reset all event timers */
    343 	for (e = events; e; e = e->next)
    344 	{
    345 		if (e->last_run.tv_sec > TStime())
    346 		{
    347 			e->last_run.tv_sec = TStime()-1;
    348 			e->last_run.tv_usec = 0;
    349 		}
    350 	}
    351 
    352 	/* For throttling we only have to deal with time jumping backward, which
    353 	 * is a real problem as if the jump was, say, 900 seconds, then it would
    354 	 * (potentially) throttle for 900 seconds.
    355 	 * Time going forward is "no problem", it just means we expire our entries
    356 	 * sonner than we should.
    357 	 */
    358 	cnt = 0;
    359 	for (i = 0; i < THROTTLING_HASH_TABLE_SIZE; i++)
    360 	{
    361 		for (thr = ThrottlingHash[i]; thr; thr = thr->next)
    362 		{
    363 			if (thr->since > TStime())
    364 				thr->since = TStime();
    365 		}
    366 	}
    367 
    368 	/* Make sure autoconnect for servers still works (lnk->hold) */
    369 	for (lnk = conf_link; lnk; lnk = lnk->next)
    370 	{
    371 		int t = lnk->class ? lnk->class->connfreq : 90;
    372 
    373 		if (lnk->hold > TStime() + t)
    374 		{
    375 			lnk->hold = TStime() + (t / 2); /* compromise */
    376 		}
    377 	}
    378 }
    379 
    380 
    381 /* MY tdiff... because 'double' sucks.
    382  * This should work until 2038, and very likely after that as well
    383  * because 'long' should be 64 bit on all systems by then... -- Syzop
    384  */
    385 #define mytdiff(a, b)   ((long)a - (long)b)
    386 
    387 #define NEGATIVE_SHIFT_WARN	-15
    388 #define POSITIVE_SHIFT_WARN	20
    389 
    390 void detect_timeshift_and_warn(void)
    391 {
    392 	static time_t highesttimeofday=0, oldtimeofday=0, lasthighwarn=0;
    393 
    394 	if (oldtimeofday == 0)
    395 		oldtimeofday = timeofday; /* pretend everything is ok the first time.. */
    396 
    397 	if (mytdiff(timeofday, oldtimeofday) < NEGATIVE_SHIFT_WARN)
    398 	{
    399 		/* tdiff = # of seconds of time set backwards (positive number! eg: 60) */
    400 		time_t tdiff = oldtimeofday - timeofday;
    401 		unreal_log(ULOG_WARNING, "system", "SYSTEM_CLOCK_JUMP_BACKWARDS", NULL,
    402 		           "System clock jumped back in time ~$time_delta seconds ($time_from -> $time_to)\n"
    403 		           "Incorrect time for IRC servers is a serious problem. "
    404 		           "Time being set backwards (system clock changed) is "
    405 		           "even more serious and can cause clients to freeze, channels to be "
    406 		           "taken over, and other 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 	} else
    413 	if (mytdiff(timeofday, oldtimeofday) > POSITIVE_SHIFT_WARN) /* do not set too low or you get false positives */
    414 	{
    415 		/* tdiff = # of seconds of time set forward (eg: 60) */
    416 		time_t tdiff = timeofday - oldtimeofday;
    417 		unreal_log(ULOG_WARNING, "system", "SYSTEM_CLOCK_JUMP_FORWARDS", NULL,
    418 		           "System clock jumped ~$time_delta seconds forward ($time_from -> $time_to)\n"
    419 		           "Incorrect time for IRC servers is a serious problem. "
    420 		           "Time being adjusted (by changing the system clock) "
    421 		           "more than a few seconds forward/backward can lead to serious issues.\n"
    422 		           "Please be sure your clock is always synchronized before the IRCd is started!",
    423 		           log_data_integer("time_delta", tdiff),
    424 		           log_data_timestamp("time_from", oldtimeofday),
    425 		           log_data_timestamp("time_to", timeofday));
    426 		fix_timers();
    427 	}
    428 
    429 	if (highesttimeofday+NEGATIVE_SHIFT_WARN > timeofday)
    430 	{
    431 		if (lasthighwarn > timeofday)
    432 			lasthighwarn = timeofday;
    433 		if (timeofday - lasthighwarn > 300)
    434 		{
    435 			unreal_log(ULOG_WARNING, "system", "SYSTEM_CLOCK_JUMP_BACKWARDS_PREVIOUSLY", NULL,
    436 				   "The system clock previously went backwards. Waiting for time to be OK again. This will be in $time_delta seconds.",
    437 				   log_data_integer("time_delta", highesttimeofday - timeofday),
    438 				   log_data_timestamp("time_from", highesttimeofday),
    439 				   log_data_timestamp("time_to", timeofday));
    440 			lasthighwarn = timeofday;
    441 		}
    442 	} else {
    443 		highesttimeofday = timeofday;
    444 	}
    445 
    446 	oldtimeofday = timeofday;
    447 }
    448 
    449 void SetupEvents(void)
    450 {
    451 	/* Start events */
    452 	EventAdd(NULL, "tunefile", save_tunefile, NULL, 300*1000, 0);
    453 	EventAdd(NULL, "garbage", garbage_collect, NULL, GARBAGE_COLLECT_EVERY*1000, 0);
    454 	EventAdd(NULL, "loop", loop_event, NULL, 1000, 0);
    455 	EventAdd(NULL, "unrealdns_removeoldrecords", unrealdns_removeoldrecords, NULL, 15000, 0);
    456 	EventAdd(NULL, "check_pings", check_pings, NULL, 1000, 0);
    457 	EventAdd(NULL, "check_deadsockets", check_deadsockets, NULL, 1000, 0);
    458 	EventAdd(NULL, "handshake_timeout", handshake_timeout, NULL, 1000, 0);
    459 	EventAdd(NULL, "tls_check_expiry", tls_check_expiry, NULL, (86400/2)*1000, 0);
    460 	EventAdd(NULL, "unrealdb_expire_secret_cache", unrealdb_expire_secret_cache, NULL, 61000, 0);
    461 	EventAdd(NULL, "throttling_check_expire", throttling_check_expire, NULL, 1000, 0);
    462 }
    463 
    464 /** The main function. This will call SocketLoop() once the server is ready. */
    465 #ifndef _WIN32
    466 int main(int argc, char *argv[])
    467 #else
    468 int InitUnrealIRCd(int argc, char *argv[])
    469 #endif
    470 {
    471 #ifndef _WIN32
    472 	uid_t uid, euid;
    473 	gid_t gid, egid;
    474 #endif
    475 #ifdef HAVE_PSTAT
    476 	union pstun pstats;
    477 #endif
    478 #ifndef _WIN32
    479 	struct rlimit corelim;
    480 #endif
    481 
    482 	gettimeofday(&timeofday_tv, NULL);
    483 	timeofday = timeofday_tv.tv_sec;
    484 
    485 	safe_strdup(configfile, CONFIGFILE);
    486 
    487 	init_random(); /* needs to be done very early!! */
    488 	if (sodium_init() < 0)
    489 	{
    490 		fprintf(stderr, "Failed to initialize sodium library -- error accessing random device?\n");
    491 		exit(-1);
    492 	}
    493 
    494 	memset(&botmotd, '\0', sizeof(MOTDFile));
    495 	memset(&rules, '\0', sizeof(MOTDFile));
    496 	memset(&opermotd, '\0', sizeof(MOTDFile));
    497 	memset(&motd, '\0', sizeof(MOTDFile));
    498 	memset(&smotd, '\0', sizeof(MOTDFile));
    499 	memset(&svsmotd, '\0', sizeof(MOTDFile));
    500 	memset(&me, 0, sizeof(me));
    501 	me.local = safe_alloc(sizeof(LocalClient));
    502 	memset(&loop, 0, sizeof(loop));
    503 
    504 	init_hash();
    505 
    506 	SetupEvents();
    507 
    508 #ifdef _WIN32
    509 	CreateMutex(NULL, FALSE, "UnrealMutex");
    510 	SetErrorMode(SEM_FAILCRITICALERRORS);
    511 #endif
    512 #if !defined(_WIN32) && !defined(_AMIGA)
    513 	uid = getuid();
    514 	euid = geteuid();
    515 	gid = getgid();
    516 	egid = getegid();
    517 
    518 	if (euid == 0)
    519 	{
    520 		fprintf(stderr,
    521 			"** ERROR **\n"
    522 			"You attempted to run UnrealIRCd as root. This is VERY DANGEROUS\n"
    523 			"as any compromise of your UnrealIRCd will result in full\n"
    524 			"privileges to the attacker on the entire machine.\n"
    525 			"You MUST start UnrealIRCd as a different user!\n"
    526 			"\n"
    527 			"For more information, see:\n"
    528 			"https://www.unrealircd.org/docs/Do_not_run_as_root\n"
    529 			"\n");
    530 		exit(1);
    531 	}
    532 #endif
    533 #ifndef _WIN32
    534 	myargv = argv;
    535 #else
    536 	cmdLine = GetCommandLine();
    537 #endif
    538 #ifndef _WIN32
    539 	(void)umask(077);	/* better safe than sorry --SRB */
    540 #else
    541 	init_winsock();
    542 #endif
    543 	setup_signals();
    544 
    545 	memset(&irccounts, '\0', sizeof(irccounts));
    546 	irccounts.servers = 1;
    547 
    548 	mp_pool_init();
    549 	dbuf_init();
    550 	initlists();
    551 	initlist_channels();
    552 
    553 	early_init_tls();
    554 	url_init();
    555 	tkl_init();
    556 	umode_init();
    557 	extcmode_init();
    558 	efunctions_init();
    559 	clear_scache_hash_table();
    560 #ifndef _WIN32
    561 	/* Make it so we can dump core */
    562 	corelim.rlim_cur = corelim.rlim_max = RLIM_INFINITY;
    563 	setrlimit(RLIMIT_CORE, &corelim);
    564 #endif
    565 	/*
    566 	 * ** All command line parameters have the syntax "-fstring"
    567 	 * ** or "-f string" (e.g. the space is optional). String may
    568 	 * ** be empty. Flag characters cannot be concatenated (like
    569 	 * ** "-fxyz"), it would conflict with the form "-fstring".
    570 	 */
    571 	while (--argc > 0 && (*++argv)[0] == '-') {
    572 		char *p = argv[0] + 1;
    573 		int  flag = *p++;
    574 		if (flag == '\0' || *p == '\0') {
    575 			if (argc > 1 && argv[1][0] != '-') {
    576 				p = *++argv;
    577 				argc -= 1;
    578 			} else
    579 				p = "";
    580 		}
    581 		switch (flag)
    582 		{
    583 			case 'F':
    584 				bootopt |= BOOT_NOFORK;
    585 				break;
    586 			case 'f':
    587 #ifndef _WIN32
    588 				if ((uid != euid) || (gid != egid))
    589 				{
    590 					printf("ERROR: Command line config with a setuid/setgid ircd is not allowed");
    591 					exit(1);
    592 				}
    593 #endif
    594 				safe_strdup(configfile, p);
    595 				convert_to_absolute_path(&configfile, CONFDIR);
    596 				break;
    597 #if 0
    598 		case 'S':
    599 			charsys_dump_table(p ? p : "*");
    600 			//unrealdb_test();
    601 #endif
    602 #ifndef _WIN32
    603 		  case 't':
    604 			  bootopt |= BOOT_TTY;
    605 			  break;
    606 		  case 'v':
    607 			  (void)printf("%s\n", version);
    608 #else
    609 		  case 'v':
    610 			  if (!IsService) {
    611 				  MessageBox(NULL, version,
    612 				      "UnrealIRCD/Win32 version", MB_OK);
    613 			  }
    614 #endif
    615 			  exit(0);
    616 		  case 'C':
    617 			  config_verbose = atoi(p);
    618 			  break;
    619 		  case 'c':
    620 			  loop.config_test = 1;
    621 			  break;
    622 		  case 'x':
    623 #ifdef	DEBUGMODE
    624 			  debuglevel = atoi(p);
    625 			  debugmode = *p ? p : "0";
    626 			  bootopt |= BOOT_DEBUG;
    627 			  break;
    628 #else
    629 # ifndef _WIN32
    630 			  (void)fprintf(stderr,
    631 			      "%s: DEBUGMODE must be defined for -x y\n",
    632 			      myargv[0]);
    633 # else
    634 			  if (!IsService) {
    635 				  MessageBox(NULL,
    636 				      "DEBUGMODE must be defined for -x option",
    637 				      "UnrealIRCD/32", MB_OK);
    638 			  }
    639 # endif
    640 			  exit(0);
    641 #endif
    642 		  case 'K':
    643 			  {
    644 			  	char *p = NULL;
    645 			  	if (chdir(TMPDIR) < 0)
    646 			  	{
    647 			  		fprintf(stderr, "Could not change to directory '%s'\n", TMPDIR);
    648 			  		exit(1);
    649 			  	}
    650 			  	fprintf(stderr, "Starting crash test!\n");
    651 			  	*p = 'a';
    652 			  	fprintf(stderr, "It is impossible to get here\n");
    653 			  	exit(0);
    654 			  }
    655 		  case 'R':
    656 		      report_crash();
    657 		      exit(0);
    658 #ifndef _WIN32
    659 		  case 'm':
    660 		      modulemanager(argc, argv);
    661 		      exit(0);
    662 #endif
    663 		  case '8':
    664 		      utf8_test();
    665 		      exit(0);
    666 		  case 'L':
    667 		      loop.boot_function = link_generator;
    668 		      break;
    669 		  default:
    670 #ifndef _WIN32
    671 			  return bad_command(myargv[0]);
    672 #else
    673 			  return bad_command(NULL);
    674 #endif
    675 			  break;
    676 		}
    677 	}
    678 
    679 #if !defined(_WIN32)
    680 #ifndef _WIN32
    681 	mkdir(TMPDIR, S_IRUSR|S_IWUSR|S_IXUSR); /* Create the tmp dir, if it doesn't exist */
    682  	mkdir(CACHEDIR, S_IRUSR|S_IWUSR|S_IXUSR); /* Create the cache dir, if it doesn't exist */
    683 #else
    684 	mkdir(TMPDIR);
    685 	mkdir(CACHEDIR);
    686 #endif
    687 	if (chdir(TMPDIR)) {
    688 # ifndef _WIN32
    689 		perror("chdir");
    690 		fprintf(stderr, "ERROR: Unable to change to directory '%s'\n", TMPDIR);
    691 # else
    692 		if (!IsService) {
    693 			MessageBox(NULL, strerror(GetLastError()),
    694 			    "UnrealIRCD/32: chdir()", MB_OK);
    695 		}
    696 # endif
    697 		exit(-1);
    698 	}
    699 #endif
    700 #ifndef _WIN32
    701 	/*
    702 	 * didn't set debuglevel
    703 	 */
    704 	/*
    705 	 * but asked for debugging output to tty
    706 	 */
    707 	if ((debuglevel < 0) && (bootopt & BOOT_TTY)) {
    708 		(void)fprintf(stderr,
    709 		    "you specified -t without -x. use -x <n>\n");
    710 		exit(-1);
    711 	}
    712 #endif
    713 
    714 	/* HACK! This ifndef should be removed when the restart-on-w32-brings-up-dialog bug
    715 	 * is fixed. This is just an ugly "ignore the invalid parameter" thing ;). -- Syzop
    716 	 */
    717 #ifndef _WIN32
    718 	if (argc > 0)
    719 		return bad_command(myargv[0]);	/* This should exit out */
    720 #endif
    721 #ifndef _WIN32
    722 	fprintf(stderr, "%s", unreallogo);
    723 	fprintf(stderr, "                           v%s\n\n", VERSIONONLY);
    724 	fprintf(stderr, "UnrealIRCd is brought to you by Bram Matthys (Syzop),\n"
    725 	                "Krzysztof Beresztant (k4be), Gottem and i\n\n");
    726 
    727 	fprintf(stderr, "Using the following libraries:\n");
    728 	fprintf(stderr, "* %s\n", SSLeay_version(SSLEAY_VERSION));
    729 	fprintf(stderr, "* libsodium %s\n", sodium_version_string());
    730 #ifdef USE_LIBCURL
    731 	fprintf(stderr, "* %s\n", curl_version());
    732 #endif
    733 	fprintf(stderr, "* c-ares %s\n", ares_version(NULL));
    734 	fprintf(stderr, "* %s\n", pcre2_version());
    735 #endif
    736 #if JANSSON_VERSION_HEX >= 0x020D00
    737 	fprintf(stderr, "* jansson %s\n", jansson_version_str());
    738 #endif
    739 	check_user_limit();
    740 #ifndef _WIN32
    741 	fprintf(stderr, "\n");
    742 	fprintf(stderr, "This server can handle %d concurrent sockets (%d clients + %d reserve)\n\n",
    743 		maxclients+CLIENTS_RESERVE, maxclients, CLIENTS_RESERVE);
    744 #endif
    745 	init_CommandHash();
    746 	initwhowas();
    747 	initstats();
    748 	if (!loop.config_test)
    749 		DeleteTempModules();
    750 #if !defined(_WIN32) && !defined(_AMIGA) && !defined(OSXTIGER) && DEFAULT_PERMISSIONS != 0
    751 	/* Hack to stop people from being able to read the config file */
    752 	(void)chmod(CPATH, DEFAULT_PERMISSIONS);
    753 #endif
    754 	init_dynconf();
    755 	init_sys();
    756 	clicap_init();
    757 	/*
    758 	 * Add default class
    759 	 */
    760 	default_class = safe_alloc(sizeof(ConfigItem_class));
    761 	default_class->flag.permanent = 1;
    762 	default_class->pingfreq = 120;
    763 	default_class->maxclients = 100;
    764 	default_class->sendq = DEFAULT_RECVQ;
    765 	default_class->name = "default";
    766 	AddListItem(default_class, conf_class);
    767 	if (config_read_start() < 0)
    768 		exit(-1);
    769 	while (!is_config_read_finished())
    770 	{
    771 		gettimeofday(&timeofday_tv, NULL);
    772 		timeofday = timeofday_tv.tv_sec;
    773 		url_socket_timeout(NULL);
    774 		unrealdns_timeout(NULL);
    775 		fd_select(500);
    776 	}
    777 	if (config_test() < 0)
    778 		exit(-1);
    779 	load_tunefile();
    780 	make_umodestr();
    781 	SetListening(&me);
    782 	me.local->fd = -1;
    783 	SetMe(&me);
    784 	make_server(&me);
    785 	umodes_check_for_changes();
    786 	charsys_check_for_changes();
    787 	if (!find_command_simple("PRIVMSG"))
    788 	{
    789 		config_error("Someone forgot to load modules with proper commands in them. READ THE DOCUMENTATION");
    790 		exit(-4);
    791 	}
    792 
    793 	if (!init_tls())
    794 	{
    795 		config_error("Failed to load TLS (see errors above). UnrealIRCd can not start.");
    796 #ifdef _WIN32
    797 		win_error(); /* display error dialog box */
    798 #endif
    799 		exit(9);
    800 	}
    801 	unreal_log(ULOG_INFO, "config", "CONFIG_PASSED", NULL, "Configuration test passed OK");
    802 	if (loop.config_test)
    803 	{
    804 		fflush(stderr);
    805 		exit(0);
    806 	}
    807 	if (loop.boot_function)
    808 		loop.boot_function();
    809 	open_debugfile();
    810 	me.local->port = 6667; /* pointless? */
    811 	applymeblock();
    812 #ifdef HAVE_SYSLOG
    813 	openlog("ircd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
    814 #endif
    815 	config_run();
    816 	unreal_log(ULOG_INFO, "main", "UNREALIRCD_START", NULL, "UnrealIRCd started.");
    817 
    818 	read_motd(conf_files->botmotd_file, &botmotd);
    819 	read_motd(conf_files->rules_file, &rules);
    820 	read_motd(conf_files->opermotd_file, &opermotd);
    821 	read_motd(conf_files->motd_file, &motd);
    822 	read_motd(conf_files->smotd_file, &smotd);
    823 	read_motd(conf_files->svsmotd_file, &svsmotd);
    824 
    825 	me.hopcount = 0;
    826 	me.local->authfd = -1;
    827 	me.user = NULL;
    828 	me.direction = &me;
    829 
    830 	/*
    831 	 * This listener will never go away
    832 	 */
    833 	me_hash = find_or_add(me.name);
    834 	timeofday = time(NULL);
    835 	me.local->last_msg_received = me.local->fake_lag = me.local->creationtime = me.server->boottime = TStime();
    836 	me.server->features.protocol = UnrealProtocol;
    837 	safe_strdup(me.server->features.software, version);
    838 	add_to_client_hash_table(me.name, &me);
    839 	add_to_id_hash_table(me.id, &me);
    840 	list_add(&me.client_node, &global_server_list);
    841 #if !defined(_AMIGA) && !defined(_WIN32) && !defined(NO_FORKING)
    842 	if (!(bootopt & BOOT_NOFORK))
    843 	{
    844 		pid_t p;
    845 		p = fork();
    846 		if (p < 0)
    847 		{
    848 			fprintf(stderr, "Could not create background job. Call to fork() failed: %s\n",
    849 				strerror(errno));
    850 			exit(-1);
    851 		}
    852 		if (p > 0)
    853 		{
    854 			/* Background job created and we are the parent. We can terminate. */
    855 			exit(0);
    856 		}
    857 		/* Background process (child) continues below... */
    858 		close_std_descriptors();
    859 		fd_fork();
    860 		loop.forked = 1;
    861 	}
    862 #endif
    863 #ifdef _WIN32
    864 	loop.forked = 1;
    865 #endif
    866 
    867 	fix_timers();
    868 	write_pidfile();
    869 	loop.booted = 1;
    870 #if defined(HAVE_SETPROCTITLE)
    871 	setproctitle("%s", me.name);
    872 #elif defined(HAVE_PSTAT)
    873 	pstats.pst_command = me.name;
    874 	pstat(PSTAT_SETCMD, pstats, strlen(me.name), 0, 0);
    875 #elif defined(HAVE_PSSTRINGS)
    876 	PS_STRINGS->ps_nargvstr = 1;
    877 	PS_STRINGS->ps_argvstr = me.name;
    878 #endif
    879 	module_loadall();
    880 	loop.config_status = CONFIG_STATUS_COMPLETE;
    881 
    882 #ifndef _WIN32
    883 	SocketLoop(NULL);
    884 #endif
    885 	return 1;
    886 }
    887 
    888 /** The main loop that the server will run all the time.
    889  * On Windows this is a thread, on *NIX we simply jump here from main()
    890  * when the server is ready.
    891  */
    892 void SocketLoop(void *dummy)
    893 {
    894 	struct timeval doevents_tv, process_clients_tv;
    895 
    896 	memset(&doevents_tv, 0, sizeof(doevents_tv));
    897 	memset(&process_clients_tv, 0, sizeof(process_clients_tv));
    898 
    899 	while (1)
    900 	{
    901 		gettimeofday(&timeofday_tv, NULL);
    902 		timeofday = timeofday_tv.tv_sec;
    903 
    904 		detect_timeshift_and_warn();
    905 
    906 		if (minimum_msec_since_last_run(&doevents_tv, 250))
    907 			DoEvents();
    908 
    909 		/* Update statistics */
    910 		if (irccounts.clients > irccounts.global_max)
    911 			irccounts.global_max = irccounts.clients;
    912 		if (irccounts.me_clients > irccounts.me_max)
    913 			irccounts.me_max = irccounts.me_clients;
    914 
    915 		/* Process I/O */
    916 		fd_select(SOCKETLOOP_MAX_DELAY);
    917 
    918 		if (minimum_msec_since_last_run(&process_clients_tv, 200))
    919 			process_clients();
    920 
    921 		/* Check if there are pending "actions".
    922 		 * These are actions that should be done outside of
    923 		 * process_clients() and fd_select() when we are not
    924 		 * processing any clients.
    925 		 */
    926 		if (dorehash)
    927 		{
    928 			request_rehash(NULL);
    929 			dorehash = 0;
    930 		}
    931 		if (dorestart)
    932 		{
    933 			server_reboot("SIGINT");
    934 		}
    935 		if (doreloadcert)
    936 		{
    937 			unreal_log(ULOG_INFO, "config", "CONFIG_RELOAD_TLS", NULL, "Reloading all TLS related data (./unrealircd reloadtls)");
    938 			reinit_tls();
    939 			doreloadcert = 0;
    940 		}
    941 		/* If rehashing, check if we are done. */
    942 		if (loop.rehashing && is_config_read_finished())
    943 			rehash_internal(loop.rehash_save_client);
    944 	}
    945 }
    946 
    947 /*
    948  * open_debugfile
    949  *
    950  * If the -t option is not given on the command line when the server is
    951  * started, all debugging output is sent to the file set by LPATH in config.h
    952  * If the debuglevel is not set from the command line by -x, use /dev/null
    953  * as the dummy logfile as long as DEBUGMODE has been defined, else don't
    954  * waste the fd.
    955  */
    956 static void open_debugfile(void)
    957 {
    958 #ifdef	DEBUGMODE
    959 	int  fd;
    960 	Client *client;
    961 	if (debuglevel >= 0) {
    962 		client = make_client(NULL, NULL);
    963 		client->local->fd = 2;
    964 		SetLog(client);
    965 		client->local->port = debuglevel;
    966 		client->flags = 0;
    967 
    968 		strlcpy(client->local->sockhost, me.local->sockhost, sizeof client->local->sockhost);
    969 # ifndef _WIN32
    970 		/*(void)printf("isatty = %d ttyname = %#x\n",
    971 		    isatty(2), (u_int)ttyname(2)); */
    972 		if (!(bootopt & BOOT_TTY)) {	/* leave debugging output on fd 2 */
    973 			if (truncate(LOGFILE, 0) < 0)
    974 				fprintf(stderr, "WARNING: could not truncate log file '%s'\n", LOGFILE);
    975 			if ((fd = open(LOGFILE, O_WRONLY | O_CREAT, 0600)) < 0)
    976 				if ((fd = open("/dev/null", O_WRONLY)) < 0)
    977 					exit(-1);
    978 
    979 #if 1
    980 			client->local->fd = fd;
    981 			debugfd = fd;
    982 #else
    983 			/* if (fd != 2) {
    984 				(void)dup2(fd, 2);
    985 				(void)close(fd);
    986 			} -- hands off stderr! */
    987 #endif
    988 			strlcpy(client->name, LOGFILE, sizeof(client->name));
    989 		} else if (isatty(2) && ttyname(2))
    990 			strlcpy(client->name, ttyname(2), sizeof(client->name));
    991 		else
    992 # endif
    993 			strlcpy(client->name, "FD2-Pipe", sizeof(client->name));
    994 	}
    995 #endif
    996 }
    997 
    998 static void setup_signals()
    999 {
   1000 #ifndef _WIN32
   1001 	struct sigaction act;
   1002 	act.sa_handler = SIG_IGN;
   1003 	act.sa_flags = 0;
   1004 	(void)sigemptyset(&act.sa_mask);
   1005 	(void)sigaddset(&act.sa_mask, SIGPIPE);
   1006 	(void)sigaddset(&act.sa_mask, SIGALRM);
   1007 #ifdef SIGWINCH
   1008 	(void)sigaddset(&act.sa_mask, SIGWINCH);
   1009 	(void)sigaction(SIGWINCH, &act, NULL);
   1010 #endif
   1011 	(void)sigaction(SIGPIPE, &act, NULL);
   1012 	act.sa_handler = ignore_this_signal;
   1013 	(void)sigaction(SIGALRM, &act, NULL);
   1014 	act.sa_handler = s_rehash;
   1015 	(void)sigemptyset(&act.sa_mask);
   1016 	(void)sigaddset(&act.sa_mask, SIGHUP);
   1017 	(void)sigaction(SIGHUP, &act, NULL);
   1018 	act.sa_handler = s_restart;
   1019 	(void)sigaddset(&act.sa_mask, SIGINT);
   1020 	(void)sigaction(SIGINT, &act, NULL);
   1021 	act.sa_handler = s_die;
   1022 	(void)sigaddset(&act.sa_mask, SIGTERM);
   1023 	(void)sigaction(SIGTERM, &act, NULL);
   1024 	act.sa_handler = s_reloadcert;
   1025 	(void)sigemptyset(&act.sa_mask);
   1026 	(void)sigaddset(&act.sa_mask, SIGUSR1);
   1027 	(void)sigaction(SIGUSR1, &act, NULL);
   1028 #endif
   1029 }