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 }