mirror of
git://git.acid.vegas/unrealircd.git
synced 2024-11-15 04:26:41 +00:00
1526 lines
40 KiB
C
1526 lines
40 KiB
C
|
/************************************************************************
|
||
|
* Unreal Internet Relay Chat Daemon, src/ircd.c
|
||
|
* Copyright (C) 1989-1990 Jarkko Oikarinen and
|
||
|
* University of Oulu, Computing Center
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 1, or (at your option)
|
||
|
* any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
|
*/
|
||
|
|
||
|
#include "unrealircd.h"
|
||
|
|
||
|
#ifdef __FreeBSD__
|
||
|
char *malloc_options = "h" MALLOC_FLAGS_EXTRA;
|
||
|
#endif
|
||
|
|
||
|
#ifndef _WIN32
|
||
|
extern char unreallogo[];
|
||
|
#endif
|
||
|
int SVSNOOP = 0;
|
||
|
extern MODVAR char *buildid;
|
||
|
time_t timeofday = 0;
|
||
|
struct timeval timeofday_tv;
|
||
|
int tainted = 0;
|
||
|
LoopStruct loop;
|
||
|
MODVAR MemoryInfo StatsZ;
|
||
|
#ifndef _WIN32
|
||
|
uid_t irc_uid = 0;
|
||
|
gid_t irc_gid = 0;
|
||
|
#endif
|
||
|
|
||
|
MODVAR IRCCounts irccounts;
|
||
|
MODVAR Client me; /* That's me */
|
||
|
MODVAR char *me_hash;
|
||
|
extern char backupbuf[8192];
|
||
|
#ifdef _WIN32
|
||
|
extern SERVICE_STATUS_HANDLE IRCDStatusHandle;
|
||
|
extern SERVICE_STATUS IRCDStatus;
|
||
|
#endif
|
||
|
|
||
|
MODVAR unsigned char conf_debuglevel = 0;
|
||
|
|
||
|
#ifdef USE_LIBCURL
|
||
|
extern void url_init(void);
|
||
|
#endif
|
||
|
|
||
|
void server_reboot(char *);
|
||
|
void restart(char *);
|
||
|
static void open_debugfile(), setup_signals();
|
||
|
extern void init_glines(void);
|
||
|
extern void tkl_init(void);
|
||
|
extern void process_clients(void);
|
||
|
|
||
|
#ifndef _WIN32
|
||
|
MODVAR char **myargv;
|
||
|
#else
|
||
|
LPCSTR cmdLine;
|
||
|
#endif
|
||
|
char *configfile = NULL; /* Server configuration file */
|
||
|
int debuglevel = 0; /* Server debug level */
|
||
|
int bootopt = 0; /* Server boot option flags */
|
||
|
char *debugmode = ""; /* -"- -"- -"- */
|
||
|
char *sbrk0; /* initial sbrk(0) */
|
||
|
static int dorehash = 0, dorestart = 0, doreloadcert = 0;
|
||
|
MODVAR int booted = FALSE;
|
||
|
|
||
|
void s_die()
|
||
|
{
|
||
|
#ifdef _WIN32
|
||
|
Client *client;
|
||
|
if (!IsService)
|
||
|
{
|
||
|
unload_all_modules();
|
||
|
|
||
|
list_for_each_entry(client, &lclient_list, lclient_node)
|
||
|
(void) send_queued(client);
|
||
|
|
||
|
exit(-1);
|
||
|
}
|
||
|
else {
|
||
|
SERVICE_STATUS status;
|
||
|
SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
|
||
|
SC_HANDLE hService = OpenService(hSCManager, "UnrealIRCd", SERVICE_STOP);
|
||
|
ControlService(hService, SERVICE_CONTROL_STOP, &status);
|
||
|
}
|
||
|
#else
|
||
|
unload_all_modules();
|
||
|
unlink(conf_files ? conf_files->pid_file : IRCD_PIDFILE);
|
||
|
exit(-1);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#ifndef _WIN32
|
||
|
static void s_rehash()
|
||
|
{
|
||
|
struct sigaction act;
|
||
|
dorehash = 1;
|
||
|
act.sa_handler = s_rehash;
|
||
|
act.sa_flags = 0;
|
||
|
(void)sigemptyset(&act.sa_mask);
|
||
|
(void)sigaddset(&act.sa_mask, SIGHUP);
|
||
|
(void)sigaction(SIGHUP, &act, NULL);
|
||
|
}
|
||
|
|
||
|
static void s_reloadcert()
|
||
|
{
|
||
|
struct sigaction act;
|
||
|
doreloadcert = 1;
|
||
|
act.sa_handler = s_reloadcert;
|
||
|
act.sa_flags = 0;
|
||
|
(void)sigemptyset(&act.sa_mask);
|
||
|
(void)sigaddset(&act.sa_mask, SIGUSR1);
|
||
|
(void)sigaction(SIGUSR1, &act, NULL);
|
||
|
}
|
||
|
#endif // #ifndef _WIN32
|
||
|
|
||
|
void restart(char *mesg)
|
||
|
{
|
||
|
server_reboot(mesg);
|
||
|
}
|
||
|
|
||
|
void s_restart()
|
||
|
{
|
||
|
dorestart = 1;
|
||
|
#if 0
|
||
|
static int restarting = 0;
|
||
|
|
||
|
if (restarting == 0) {
|
||
|
/*
|
||
|
* Send (or attempt to) a dying scream to oper if present
|
||
|
*/
|
||
|
|
||
|
restarting = 1;
|
||
|
server_reboot("SIGINT");
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifndef _WIN32
|
||
|
/** Signal handler for signals which we ignore,
|
||
|
* like SIGPIPE ("Broken pipe") and SIGWINCH (terminal window changed) etc.
|
||
|
*/
|
||
|
void ignore_this_signal()
|
||
|
{
|
||
|
struct sigaction act;
|
||
|
|
||
|
act.sa_handler = ignore_this_signal;
|
||
|
act.sa_flags = 0;
|
||
|
(void)sigemptyset(&act.sa_mask);
|
||
|
(void)sigaddset(&act.sa_mask, SIGALRM);
|
||
|
(void)sigaddset(&act.sa_mask, SIGPIPE);
|
||
|
(void)sigaction(SIGALRM, &act, (struct sigaction *)NULL);
|
||
|
(void)sigaction(SIGPIPE, &act, (struct sigaction *)NULL);
|
||
|
#ifdef SIGWINCH
|
||
|
(void)sigaddset(&act.sa_mask, SIGWINCH);
|
||
|
(void)sigaction(SIGWINCH, &act, (struct sigaction *)NULL);
|
||
|
#endif
|
||
|
}
|
||
|
#endif /* #ifndef _WIN32 */
|
||
|
|
||
|
|
||
|
void server_reboot(char *mesg)
|
||
|
{
|
||
|
int i;
|
||
|
Client *client;
|
||
|
sendto_realops("Aieeeee!!! Restarting server... %s", mesg);
|
||
|
Debug((DEBUG_NOTICE, "Restarting server... %s", mesg));
|
||
|
|
||
|
list_for_each_entry(client, &lclient_list, lclient_node)
|
||
|
(void) send_queued(client);
|
||
|
|
||
|
/*
|
||
|
* ** fd 0 must be 'preserved' if either the -d or -i options have
|
||
|
* ** been passed to us before restarting.
|
||
|
*/
|
||
|
#ifdef HAVE_SYSLOG
|
||
|
(void)closelog();
|
||
|
#endif
|
||
|
#ifndef _WIN32
|
||
|
for (i = 3; i < MAXCONNECTIONS; i++)
|
||
|
(void)close(i);
|
||
|
if (!(bootopt & (BOOT_TTY | BOOT_DEBUG)))
|
||
|
(void)close(2);
|
||
|
(void)close(1);
|
||
|
(void)close(0);
|
||
|
(void)execv(MYNAME, myargv);
|
||
|
#else
|
||
|
close_connections();
|
||
|
if (!IsService)
|
||
|
{
|
||
|
CleanUp();
|
||
|
WinExec(cmdLine, SW_SHOWDEFAULT);
|
||
|
}
|
||
|
#endif
|
||
|
#ifndef _WIN32
|
||
|
Debug((DEBUG_FATAL, "Couldn't restart server: %s", strerror(errno)));
|
||
|
#else
|
||
|
Debug((DEBUG_FATAL, "Couldn't restart server: %s",
|
||
|
strerror(GetLastError())));
|
||
|
#endif
|
||
|
unload_all_modules();
|
||
|
#ifdef _WIN32
|
||
|
if (IsService)
|
||
|
{
|
||
|
SERVICE_STATUS status;
|
||
|
PROCESS_INFORMATION pi;
|
||
|
STARTUPINFO si;
|
||
|
char fname[MAX_PATH];
|
||
|
memset(&status, 0, sizeof(status));
|
||
|
memset(&si, 0, sizeof(si));
|
||
|
IRCDStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
||
|
SetServiceStatus(IRCDStatusHandle, &IRCDStatus);
|
||
|
GetModuleFileName(GetModuleHandle(NULL), fname, MAX_PATH);
|
||
|
CreateProcess(fname, "restartsvc", NULL, NULL, FALSE,
|
||
|
0, NULL, NULL, &si, &pi);
|
||
|
IRCDStatus.dwCurrentState = SERVICE_STOPPED;
|
||
|
SetServiceStatus(IRCDStatusHandle, &IRCDStatus);
|
||
|
ExitProcess(0);
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
exit(-1);
|
||
|
}
|
||
|
|
||
|
MODVAR char *areason;
|
||
|
|
||
|
EVENT(loop_event)
|
||
|
{
|
||
|
if (loop.do_garbage_collect == 1) {
|
||
|
garbage_collect(NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
EVENT(garbage_collect)
|
||
|
{
|
||
|
extern int freelinks;
|
||
|
extern Link *freelink;
|
||
|
Link p;
|
||
|
int ii;
|
||
|
|
||
|
if (loop.do_garbage_collect == 1)
|
||
|
sendto_realops("Doing garbage collection ..");
|
||
|
if (freelinks > HOW_MANY_FREELINKS_ALLOWED) {
|
||
|
ii = freelinks;
|
||
|
while (freelink && (freelinks > HOW_MANY_FREELINKS_ALLOWED)) {
|
||
|
freelinks--;
|
||
|
p.next = freelink;
|
||
|
freelink = freelink->next;
|
||
|
safe_free(p.next);
|
||
|
}
|
||
|
if (loop.do_garbage_collect == 1) {
|
||
|
loop.do_garbage_collect = 0;
|
||
|
sendto_realops
|
||
|
("Cleaned up %i garbage blocks", (ii - freelinks));
|
||
|
}
|
||
|
}
|
||
|
if (loop.do_garbage_collect == 1)
|
||
|
loop.do_garbage_collect = 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** try_connections
|
||
|
**
|
||
|
** Scan through configuration and try new connections.
|
||
|
** Returns the calendar time when the next call to this
|
||
|
** function should be made latest. (No harm done if this
|
||
|
** is called earlier or later...)
|
||
|
*/
|
||
|
EVENT(try_connections)
|
||
|
{
|
||
|
ConfigItem_link *aconf;
|
||
|
ConfigItem_deny_link *deny;
|
||
|
Client *client;
|
||
|
int confrq;
|
||
|
ConfigItem_class *class;
|
||
|
|
||
|
for (aconf = conf_link; aconf; aconf = aconf->next)
|
||
|
{
|
||
|
/* We're only interested in autoconnect blocks that are valid (and ignore temporary link blocks) */
|
||
|
if (!(aconf->outgoing.options & CONNECT_AUTO) || !aconf->outgoing.hostname || (aconf->flag.temporary == 1))
|
||
|
continue;
|
||
|
|
||
|
class = aconf->class;
|
||
|
|
||
|
/* Only do one connection attempt per <connfreq> seconds (for the same server) */
|
||
|
if ((aconf->hold > TStime()))
|
||
|
continue;
|
||
|
confrq = class->connfreq;
|
||
|
aconf->hold = TStime() + confrq;
|
||
|
|
||
|
client = find_client(aconf->servername, NULL);
|
||
|
if (client)
|
||
|
continue; /* Server already connected (or connecting) */
|
||
|
|
||
|
if (class->clients >= class->maxclients)
|
||
|
continue; /* Class is full */
|
||
|
|
||
|
/* Check connect rules to see if we're allowed to try the link */
|
||
|
for (deny = conf_deny_link; deny; deny = deny->next)
|
||
|
if (match_simple(deny->mask, aconf->servername) && crule_eval(deny->rule))
|
||
|
break;
|
||
|
|
||
|
if (!deny && connect_server(aconf, NULL, NULL) == 0)
|
||
|
sendto_realops("Connection to %s[%s] activated.",
|
||
|
aconf->servername, aconf->outgoing.hostname);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Does this user match any TKL's? */
|
||
|
int match_tkls(Client *client)
|
||
|
{
|
||
|
ConfigItem_ban *bconf = NULL;
|
||
|
char banbuf[1024];
|
||
|
|
||
|
char killflag = 0;
|
||
|
|
||
|
/* Process dynamic *LINES */
|
||
|
if (find_tkline_match(client, 0))
|
||
|
return 1; /* user killed */
|
||
|
|
||
|
find_shun(client); /* check for shunned and take action, if so */
|
||
|
|
||
|
if (IsUser(client))
|
||
|
{
|
||
|
/* Check ban realname { } */
|
||
|
if (!ValidatePermissionsForPath("immune",client,NULL,NULL,NULL) && (bconf = find_ban(NULL, client->info, CONF_BAN_REALNAME)))
|
||
|
killflag++;
|
||
|
}
|
||
|
|
||
|
/* If user is meant to be killed, take action: */
|
||
|
if (killflag)
|
||
|
{
|
||
|
if (IsUser(client))
|
||
|
sendto_realops("Ban active for %s (%s)",
|
||
|
get_client_name(client, FALSE),
|
||
|
bconf->reason ? bconf->reason : "no reason");
|
||
|
|
||
|
if (IsServer(client))
|
||
|
sendto_realops("Ban active for server %s (%s)",
|
||
|
get_client_name(client, FALSE),
|
||
|
bconf->reason ? bconf->reason : "no reason");
|
||
|
|
||
|
if (bconf->reason) {
|
||
|
if (IsUser(client))
|
||
|
snprintf(banbuf, sizeof(banbuf), "User has been banned (%s)", bconf->reason);
|
||
|
else
|
||
|
snprintf(banbuf, sizeof(banbuf), "Banned (%s)", bconf->reason);
|
||
|
exit_client(client, NULL, banbuf);
|
||
|
} else {
|
||
|
if (IsUser(client))
|
||
|
exit_client(client, NULL, "User has been banned");
|
||
|
else
|
||
|
exit_client(client, NULL, "Banned");
|
||
|
}
|
||
|
return 1; /* stop processing this user, as (s)he is dead now. */
|
||
|
}
|
||
|
|
||
|
if (loop.do_bancheck_spamf_user && IsUser(client) && find_spamfilter_user(client, SPAMFLAG_NOWARN))
|
||
|
return 1;
|
||
|
|
||
|
if (loop.do_bancheck_spamf_away && IsUser(client) &&
|
||
|
client->user->away != NULL &&
|
||
|
match_spamfilter(client, client->user->away, SPAMF_AWAY, NULL, SPAMFLAG_NOWARN, NULL))
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/** Time out connections that are still in handshake.
|
||
|
*/
|
||
|
EVENT(handshake_timeout)
|
||
|
{
|
||
|
Client *client, *next;
|
||
|
|
||
|
list_for_each_entry_safe(client, next, &unknown_list, lclient_node)
|
||
|
{
|
||
|
if (client->local->firsttime && ((TStime() - client->local->firsttime) > iConf.handshake_timeout))
|
||
|
{
|
||
|
if (client->serv && *client->serv->by)
|
||
|
{
|
||
|
/* If this is a handshake timeout to an outgoing server then notify ops & log it */
|
||
|
sendto_ops_and_log("Connection handshake timeout while connecting to server '%s' (%s)",
|
||
|
client->name, client->ip?client->ip:"<unknown ip>");
|
||
|
}
|
||
|
|
||
|
exit_client(client, NULL, "Registration Timeout");
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Ping individual user, and check for ping timeout */
|
||
|
void check_ping(Client *client)
|
||
|
{
|
||
|
char scratch[64];
|
||
|
int ping = 0;
|
||
|
|
||
|
ping = client->local->class ? client->local->class->pingfreq : iConf.handshake_timeout;
|
||
|
Debug((DEBUG_DEBUG, "c(%s)=%d p %d a %lld", client->name,
|
||
|
client->status, ping,
|
||
|
(long long)(TStime() - client->local->lasttime)));
|
||
|
|
||
|
/* If ping is less than or equal to the last time we received a command from them */
|
||
|
if (ping > (TStime() - client->local->lasttime))
|
||
|
return; /* some recent command was executed */
|
||
|
|
||
|
if (
|
||
|
/* If we have sent a ping */
|
||
|
(IsPingSent(client)
|
||
|
/* And they had 2x ping frequency to respond */
|
||
|
&& ((TStime() - client->local->lasttime) >= (2 * ping)))
|
||
|
||
|
||
|
/* Or isn't registered and time spent is larger than ping (CONNECTTIMEOUT).. */
|
||
|
(!IsRegistered(client) && (TStime() - client->local->since >= ping))
|
||
|
)
|
||
|
{
|
||
|
if (IsServer(client) || IsConnecting(client) ||
|
||
|
IsHandshake(client) || IsTLSConnectHandshake(client))
|
||
|
{
|
||
|
sendto_umode_global(UMODE_OPER, "No response from %s, closing link",
|
||
|
get_client_name(client, FALSE));
|
||
|
ircd_log(LOG_ERROR, "No response from %s, closing link",
|
||
|
get_client_name(client, FALSE));
|
||
|
}
|
||
|
if (IsTLSAcceptHandshake(client))
|
||
|
Debug((DEBUG_DEBUG, "ssl accept handshake timeout: %s (%lld-%lld > %lld)", client->local->sockhost,
|
||
|
(long long)TStime(), (long long)client->local->since, (long long)ping));
|
||
|
ircsnprintf(scratch, sizeof(scratch), "Ping timeout: %lld seconds",
|
||
|
(long long) (TStime() - client->local->lasttime));
|
||
|
exit_client(client, NULL, scratch);
|
||
|
return;
|
||
|
}
|
||
|
else if (IsRegistered(client) && !IsPingSent(client))
|
||
|
{
|
||
|
/* Time to send a PING */
|
||
|
SetPingSent(client);
|
||
|
ClearPingWarning(client);
|
||
|
/* not nice but does the job */
|
||
|
client->local->lasttime = TStime() - ping;
|
||
|
sendto_one(client, NULL, "PING :%s", me.name);
|
||
|
}
|
||
|
else if (!IsPingWarning(client) && PINGWARNING > 0 &&
|
||
|
(IsServer(client) || IsHandshake(client) || IsConnecting(client) ||
|
||
|
IsTLSConnectHandshake(client)) &&
|
||
|
(TStime() - client->local->lasttime) >= (ping + PINGWARNING))
|
||
|
{
|
||
|
SetPingWarning(client);
|
||
|
sendto_realops("Warning, no response from %s for %d seconds",
|
||
|
get_client_name(client, FALSE), PINGWARNING);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Check registered connections for PING timeout.
|
||
|
* XXX: also does some other stuff still, need to sort this. --nenolod
|
||
|
* Perhaps it would be wise to ping servers as well mr nenolod, just an idea -- Syzop
|
||
|
*/
|
||
|
EVENT(check_pings)
|
||
|
{
|
||
|
Client *client, *next;
|
||
|
|
||
|
list_for_each_entry_safe(client, next, &lclient_list, lclient_node)
|
||
|
{
|
||
|
/* Check TKLs for this user */
|
||
|
if (loop.do_bancheck && match_tkls(client))
|
||
|
continue;
|
||
|
check_ping(client);
|
||
|
/* don't touch 'client' after this as it may have been killed */
|
||
|
}
|
||
|
|
||
|
list_for_each_entry_safe(client, next, &server_list, special_node)
|
||
|
{
|
||
|
check_ping(client);
|
||
|
}
|
||
|
|
||
|
loop.do_bancheck = loop.do_bancheck_spamf_user = loop.do_bancheck_spamf_away = 0;
|
||
|
/* done */
|
||
|
}
|
||
|
|
||
|
EVENT(check_deadsockets)
|
||
|
{
|
||
|
Client *client, *next;
|
||
|
|
||
|
list_for_each_entry_safe(client, next, &unknown_list, lclient_node)
|
||
|
{
|
||
|
/* No need to notify opers here. It's already done when dead socket is set */
|
||
|
if (IsDeadSocket(client))
|
||
|
{
|
||
|
#ifdef DEBUGMODE
|
||
|
ircd_log(LOG_ERROR, "Closing deadsock: %d/%s", client->local->fd, client->name);
|
||
|
#endif
|
||
|
ClearDeadSocket(client); /* CPR. So we send the error. */
|
||
|
exit_client(client, NULL, client->local->error_str ? client->local->error_str : "Dead socket");
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
list_for_each_entry_safe(client, next, &lclient_list, lclient_node)
|
||
|
{
|
||
|
/* No need to notify opers here. It's already done when dead socket is set */
|
||
|
if (IsDeadSocket(client))
|
||
|
{
|
||
|
#ifdef DEBUGMODE
|
||
|
ircd_log(LOG_ERROR, "Closing deadsock: %d/%s", client->local->fd, client->name);
|
||
|
#endif
|
||
|
ClearDeadSocket(client); /* CPR. So we send the error. */
|
||
|
exit_client(client, NULL, client->local->error_str ? client->local->error_str : "Dead socket");
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Next is for clients that are already exited (unlike the above).
|
||
|
* The client is already out of all lists (channels, invites, etc etc)
|
||
|
* and 90% has been freed. Here we actually free the remaining parts.
|
||
|
* We don't have to send anything anymore.
|
||
|
*/
|
||
|
list_for_each_entry_safe(client, next, &dead_list, client_node)
|
||
|
{
|
||
|
if (!IsDead(client))
|
||
|
abort(); /* impossible */
|
||
|
#ifdef DEBUGMODE
|
||
|
ircd_log(LOG_ERROR, "Closing deadsock2: %s", client->name);
|
||
|
#endif
|
||
|
list_del(&client->client_node);
|
||
|
free_client(client);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** bad_command
|
||
|
** This is called when the commandline is not acceptable.
|
||
|
** Give error message and exit without starting anything.
|
||
|
*/
|
||
|
static int bad_command(const char *argv0)
|
||
|
{
|
||
|
#ifndef _WIN32
|
||
|
if (!argv0)
|
||
|
argv0 = "unrealircd";
|
||
|
|
||
|
(void)printf
|
||
|
("Usage: %s [-f <config>] [-F]\n"
|
||
|
"\n"
|
||
|
"UnrealIRCd\n"
|
||
|
" -f <config> Load configuration from <config> instead of the default\n"
|
||
|
" (%s).\n"
|
||
|
" -F Don't fork() when starting up. Use this when running\n"
|
||
|
" UnrealIRCd under gdb or when playing around with settings\n"
|
||
|
" on a non-production setup.\n"
|
||
|
"\n",
|
||
|
argv0, CONFIGFILE);
|
||
|
(void)printf("Server not started\n\n");
|
||
|
#else
|
||
|
if (!IsService) {
|
||
|
MessageBox(NULL,
|
||
|
"Usage: UnrealIRCd [-f configfile]\n",
|
||
|
"UnrealIRCD/32", MB_OK);
|
||
|
}
|
||
|
#endif
|
||
|
return (-1);
|
||
|
}
|
||
|
|
||
|
char chess[] = {
|
||
|
85, 110, 114, 101, 97, 108, 0
|
||
|
};
|
||
|
|
||
|
static void version_check_logerror(char *fmt, ...)
|
||
|
{
|
||
|
va_list va;
|
||
|
char buf[1024];
|
||
|
|
||
|
va_start(va, fmt);
|
||
|
vsnprintf(buf, sizeof(buf), fmt, va);
|
||
|
va_end(va);
|
||
|
#ifndef _WIN32
|
||
|
fprintf(stderr, "[!!!] %s\n", buf);
|
||
|
#else
|
||
|
win_log("[!!!] %s", buf);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/** Ugly version checker that ensures ssl/curl runtime libraries match the
|
||
|
* version we compiled for.
|
||
|
*/
|
||
|
static void do_version_check()
|
||
|
{
|
||
|
const char *compiledfor, *runtime;
|
||
|
int error = 0;
|
||
|
char *p;
|
||
|
|
||
|
/* OPENSSL:
|
||
|
* Nowadays (since openssl 1.0.0) they retain binary compatibility
|
||
|
* when the first two version numbers are the same: eg 1.0.0 and 1.0.2
|
||
|
*/
|
||
|
compiledfor = OPENSSL_VERSION_TEXT;
|
||
|
runtime = SSLeay_version(SSLEAY_VERSION);
|
||
|
p = strchr(compiledfor, '.');
|
||
|
if (p)
|
||
|
{
|
||
|
p = strchr(p+1, '.');
|
||
|
if (p)
|
||
|
{
|
||
|
int versionlen = p - compiledfor + 1;
|
||
|
|
||
|
if (strncasecmp(compiledfor, runtime, versionlen))
|
||
|
{
|
||
|
version_check_logerror("OpenSSL version mismatch: compiled for '%s', library is '%s'",
|
||
|
compiledfor, runtime);
|
||
|
error=1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef USE_LIBCURL
|
||
|
/* Perhaps someone should tell them to do this a bit more easy ;)
|
||
|
* problem is runtime output is like: 'libcurl/7.11.1 c-ares/1.2.0'
|
||
|
* while header output is like: '7.11.1'.
|
||
|
*/
|
||
|
{
|
||
|
char buf[128], *p;
|
||
|
|
||
|
runtime = curl_version();
|
||
|
compiledfor = LIBCURL_VERSION;
|
||
|
if (!strncmp(runtime, "libcurl/", 8))
|
||
|
{
|
||
|
strlcpy(buf, runtime+8, sizeof(buf));
|
||
|
p = strchr(buf, ' ');
|
||
|
if (p)
|
||
|
{
|
||
|
*p = '\0';
|
||
|
if (strcmp(compiledfor, buf))
|
||
|
{
|
||
|
version_check_logerror("Curl version mismatch: compiled for '%s', library is '%s'",
|
||
|
compiledfor, buf);
|
||
|
error = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (error)
|
||
|
{
|
||
|
#ifndef _WIN32
|
||
|
version_check_logerror("Header<->library mismatches can make UnrealIRCd *CRASH*! "
|
||
|
"Make sure you don't have multiple versions of openssl installed (eg: "
|
||
|
"one in /usr and one in /usr/local). And, if you recently upgraded them, "
|
||
|
"be sure to recompile UnrealIRCd.");
|
||
|
#else
|
||
|
version_check_logerror("Header<->library mismatches can make UnrealIRCd *CRASH*! "
|
||
|
"This should never happen with official Windows builds... unless "
|
||
|
"you overwrote any .dll files with newer/older ones or something.");
|
||
|
win_error();
|
||
|
#endif
|
||
|
tainted = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
extern void applymeblock(void);
|
||
|
|
||
|
extern MODVAR Event *events;
|
||
|
|
||
|
/** This functions resets a couple of timers and does other things that
|
||
|
* are absolutely cruicial when the clock is adjusted - particularly
|
||
|
* when the clock goes backwards. -- Syzop
|
||
|
*/
|
||
|
void fix_timers(void)
|
||
|
{
|
||
|
int i, cnt;
|
||
|
Client *client;
|
||
|
Event *e;
|
||
|
struct ThrottlingBucket *thr;
|
||
|
ConfigItem_link *lnk;
|
||
|
|
||
|
list_for_each_entry(client, &lclient_list, lclient_node)
|
||
|
{
|
||
|
if (client->local->since > TStime())
|
||
|
{
|
||
|
Debug((DEBUG_DEBUG, "fix_timers(): %s: client->local->since %ld -> %ld",
|
||
|
client->name, client->local->since, TStime()));
|
||
|
client->local->since = TStime();
|
||
|
}
|
||
|
if (client->local->lasttime > TStime())
|
||
|
{
|
||
|
Debug((DEBUG_DEBUG, "fix_timers(): %s: client->local->lasttime %ld -> %ld",
|
||
|
client->name, client->local->lasttime, TStime()));
|
||
|
client->local->lasttime = TStime();
|
||
|
}
|
||
|
if (client->local->last > TStime())
|
||
|
{
|
||
|
Debug((DEBUG_DEBUG, "fix_timers(): %s: client->local->last %ld -> %ld",
|
||
|
client->name, client->local->last, TStime()));
|
||
|
client->local->last = TStime();
|
||
|
}
|
||
|
|
||
|
/* users */
|
||
|
if (MyUser(client))
|
||
|
{
|
||
|
if (client->local->nextnick > TStime())
|
||
|
{
|
||
|
Debug((DEBUG_DEBUG, "fix_timers(): %s: client->local->nextnick %ld -> %ld",
|
||
|
client->name, client->local->nextnick, TStime()));
|
||
|
client->local->nextnick = TStime();
|
||
|
}
|
||
|
if (client->local->nexttarget > TStime())
|
||
|
{
|
||
|
Debug((DEBUG_DEBUG, "fix_timers(): %s: client->local->nexttarget %ld -> %ld",
|
||
|
client->name, client->local->nexttarget, TStime()));
|
||
|
client->local->nexttarget = TStime();
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Reset all event timers */
|
||
|
for (e = events; e; e = e->next)
|
||
|
{
|
||
|
if (e->last_run.tv_sec > TStime())
|
||
|
{
|
||
|
e->last_run.tv_sec = TStime()-1;
|
||
|
e->last_run.tv_usec = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* For throttling we only have to deal with time jumping backward, which
|
||
|
* is a real problem as if the jump was, say, 900 seconds, then it would
|
||
|
* (potentially) throttle for 900 seconds.
|
||
|
* Time going forward is "no problem", it just means we expire our entries
|
||
|
* sonner than we should.
|
||
|
*/
|
||
|
cnt = 0;
|
||
|
for (i = 0; i < THROTTLING_HASH_TABLE_SIZE; i++)
|
||
|
{
|
||
|
for (thr = ThrottlingHash[i]; thr; thr = thr->next)
|
||
|
{
|
||
|
if (thr->since > TStime())
|
||
|
thr->since = TStime();
|
||
|
}
|
||
|
}
|
||
|
Debug((DEBUG_DEBUG, "fix_timers(): removed %d throttling item(s)", cnt));
|
||
|
|
||
|
/* Make sure autoconnect for servers still works (lnk->hold) */
|
||
|
for (lnk = conf_link; lnk; lnk = lnk->next)
|
||
|
{
|
||
|
int t = lnk->class ? lnk->class->connfreq : 90;
|
||
|
|
||
|
if (lnk->hold > TStime() + t)
|
||
|
{
|
||
|
lnk->hold = TStime() + (t / 2); /* compromise */
|
||
|
Debug((DEBUG_DEBUG, "fix_timers(): link '%s' hold-time adjusted to %ld", lnk->servername, lnk->hold));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifndef _WIN32
|
||
|
static void generate_cloakkeys()
|
||
|
{
|
||
|
/* Generate 3 cloak keys */
|
||
|
#define GENERATE_CLOAKKEY_MINLEN 50
|
||
|
#define GENERATE_CLOAKKEY_MAXLEN 60 /* Length of cloak keys to generate. */
|
||
|
char keyBuf[GENERATE_CLOAKKEY_MAXLEN + 1];
|
||
|
int keyNum;
|
||
|
int keyLen;
|
||
|
int charIndex;
|
||
|
|
||
|
short has_upper;
|
||
|
short has_lower;
|
||
|
short has_num;
|
||
|
|
||
|
fprintf(stderr, "Here are 3 random cloak keys:\n");
|
||
|
|
||
|
for (keyNum = 0; keyNum < 3; ++keyNum)
|
||
|
{
|
||
|
has_upper = 0;
|
||
|
has_lower = 0;
|
||
|
has_num = 0;
|
||
|
|
||
|
keyLen = (getrandom8() % (GENERATE_CLOAKKEY_MAXLEN - GENERATE_CLOAKKEY_MINLEN + 1)) + GENERATE_CLOAKKEY_MINLEN;
|
||
|
for (charIndex = 0; charIndex < keyLen; ++charIndex)
|
||
|
{
|
||
|
switch (getrandom8() % 3)
|
||
|
{
|
||
|
case 0: /* Uppercase. */
|
||
|
keyBuf[charIndex] = (char)('A' + (getrandom8() % ('Z' - 'A')));
|
||
|
has_upper = 1;
|
||
|
break;
|
||
|
case 1: /* Lowercase. */
|
||
|
keyBuf[charIndex] = (char)('a' + (getrandom8() % ('z' - 'a')));
|
||
|
has_lower = 1;
|
||
|
break;
|
||
|
case 2: /* Digit. */
|
||
|
keyBuf[charIndex] = (char)('0' + (getrandom8() % ('9' - '0')));
|
||
|
has_num = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
keyBuf[keyLen] = '\0';
|
||
|
|
||
|
if (has_upper && has_lower && has_num)
|
||
|
(void)fprintf(stderr, "%s\n", keyBuf);
|
||
|
else
|
||
|
/* Try again. For this reason, keyNum must be signed. */
|
||
|
keyNum--;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* MY tdiff... because 'double' sucks.
|
||
|
* This should work until 2038, and very likely after that as well
|
||
|
* because 'long' should be 64 bit on all systems by then... -- Syzop
|
||
|
*/
|
||
|
#define mytdiff(a, b) ((long)a - (long)b)
|
||
|
|
||
|
#define NEGATIVE_SHIFT_WARN -15
|
||
|
#define POSITIVE_SHIFT_WARN 20
|
||
|
|
||
|
void detect_timeshift_and_warn(void)
|
||
|
{
|
||
|
static time_t highesttimeofday=0, oldtimeofday=0, lasthighwarn=0;
|
||
|
|
||
|
if (oldtimeofday == 0)
|
||
|
oldtimeofday = timeofday; /* pretend everything is ok the first time.. */
|
||
|
|
||
|
if (mytdiff(timeofday, oldtimeofday) < NEGATIVE_SHIFT_WARN) {
|
||
|
/* tdiff = # of seconds of time set backwards (positive number! eg: 60) */
|
||
|
time_t tdiff = oldtimeofday - timeofday;
|
||
|
ircd_log(LOG_ERROR, "WARNING: Time running backwards! Clock set back ~%lld seconds (%lld -> %lld)",
|
||
|
(long long)tdiff, (long long)oldtimeofday, (long long)timeofday);
|
||
|
ircd_log(LOG_ERROR, "[TimeShift] Resetting a few timers to prevent IRCd freeze!");
|
||
|
sendto_realops("WARNING: Time running backwards! Clock set back ~%lld seconds (%lld -> %lld)",
|
||
|
(long long)tdiff, (long long)oldtimeofday, (long long)timeofday);
|
||
|
sendto_realops("Incorrect time for IRC servers is a serious problem. "
|
||
|
"Time being set backwards (system clock changed) is "
|
||
|
"even more serious and can cause clients to freeze, channels to be "
|
||
|
"taken over, and other issues.");
|
||
|
sendto_realops("Please be sure your clock is always synchronized before "
|
||
|
"the IRCd is started!");
|
||
|
sendto_realops("[TimeShift] Resetting a few timers to prevent IRCd freeze!");
|
||
|
fix_timers();
|
||
|
} else
|
||
|
if (mytdiff(timeofday, oldtimeofday) > POSITIVE_SHIFT_WARN) /* do not set too low or you get false positives */
|
||
|
{
|
||
|
/* tdiff = # of seconds of time set forward (eg: 60) */
|
||
|
time_t tdiff = timeofday - oldtimeofday;
|
||
|
ircd_log(LOG_ERROR, "WARNING: Time jumped ~%lld seconds ahead! (%lld -> %lld)",
|
||
|
(long long)tdiff, (long long)oldtimeofday, (long long)timeofday);
|
||
|
ircd_log(LOG_ERROR, "[TimeShift] Resetting some timers!");
|
||
|
sendto_realops("WARNING: Time jumped ~%lld seconds ahead! (%lld -> %lld)",
|
||
|
(long long)tdiff, (long long)oldtimeofday, (long long)timeofday);
|
||
|
sendto_realops("Incorrect time for IRC servers is a serious problem. "
|
||
|
"Time being adjusted (by changing the system clock) "
|
||
|
"more than a few seconds forward/backward can lead to serious issues.");
|
||
|
sendto_realops("Please be sure your clock is always synchronized before "
|
||
|
"the IRCd is started!");
|
||
|
sendto_realops("[TimeShift] Resetting some timers!");
|
||
|
fix_timers();
|
||
|
}
|
||
|
|
||
|
if (highesttimeofday+NEGATIVE_SHIFT_WARN > timeofday)
|
||
|
{
|
||
|
if (lasthighwarn > timeofday)
|
||
|
lasthighwarn = timeofday;
|
||
|
if (timeofday - lasthighwarn > 300)
|
||
|
{
|
||
|
ircd_log(LOG_ERROR, "[TimeShift] The (IRCd) clock was set backwards. "
|
||
|
"Waiting for time to be OK again. This will be in %lld seconds",
|
||
|
(long long)(highesttimeofday - timeofday));
|
||
|
sendto_realops("[TimeShift] The (IRCd) clock was set backwards. Timers, nick- "
|
||
|
"and channel-timestamps are possibly incorrect. This message will "
|
||
|
"repeat itself until we catch up with the original time, which will be "
|
||
|
"in %lld seconds", (long long)(highesttimeofday - timeofday));
|
||
|
lasthighwarn = timeofday;
|
||
|
}
|
||
|
} else {
|
||
|
highesttimeofday = timeofday;
|
||
|
}
|
||
|
|
||
|
oldtimeofday = timeofday;
|
||
|
}
|
||
|
|
||
|
/** Check if at least 'minimum' seconds passed by since last run.
|
||
|
* @param tv_old Pointer to a timeval struct to keep track of things.
|
||
|
* @param minimum The time specified in milliseconds (eg: 1000 for 1 second)
|
||
|
* @returns When 'minimum' msec passed 1 is returned and the time is reset, otherwise 0 is returned.
|
||
|
*/
|
||
|
int minimum_msec_since_last_run(struct timeval *tv_old, long minimum)
|
||
|
{
|
||
|
long v;
|
||
|
|
||
|
if (tv_old->tv_sec == 0)
|
||
|
{
|
||
|
/* First call ever */
|
||
|
tv_old->tv_sec = timeofday_tv.tv_sec;
|
||
|
tv_old->tv_usec = timeofday_tv.tv_usec;
|
||
|
return 0;
|
||
|
}
|
||
|
v = ((timeofday_tv.tv_sec - tv_old->tv_sec) * 1000) + ((timeofday_tv.tv_usec - tv_old->tv_usec)/1000);
|
||
|
if (v >= minimum)
|
||
|
{
|
||
|
tv_old->tv_sec = timeofday_tv.tv_sec;
|
||
|
tv_old->tv_usec = timeofday_tv.tv_usec;
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/** The main function. This will call SocketLoop() once the server is ready. */
|
||
|
#ifndef _WIN32
|
||
|
int main(int argc, char *argv[])
|
||
|
#else
|
||
|
int InitUnrealIRCd(int argc, char *argv[])
|
||
|
#endif
|
||
|
{
|
||
|
#ifdef _WIN32
|
||
|
WORD wVersionRequested = MAKEWORD(1, 1);
|
||
|
WSADATA wsaData;
|
||
|
#else
|
||
|
uid_t uid, euid;
|
||
|
gid_t gid, egid;
|
||
|
#endif
|
||
|
#ifdef HAVE_PSTAT
|
||
|
union pstun pstats;
|
||
|
#endif
|
||
|
#ifndef _WIN32
|
||
|
struct rlimit corelim;
|
||
|
#endif
|
||
|
|
||
|
gettimeofday(&timeofday_tv, NULL);
|
||
|
timeofday = timeofday_tv.tv_sec;
|
||
|
|
||
|
safe_strdup(configfile, CONFIGFILE);
|
||
|
|
||
|
init_random(); /* needs to be done very early!! */
|
||
|
|
||
|
memset(&botmotd, '\0', sizeof(MOTDFile));
|
||
|
memset(&rules, '\0', sizeof(MOTDFile));
|
||
|
memset(&opermotd, '\0', sizeof(MOTDFile));
|
||
|
memset(&motd, '\0', sizeof(MOTDFile));
|
||
|
memset(&smotd, '\0', sizeof(MOTDFile));
|
||
|
memset(&svsmotd, '\0', sizeof(MOTDFile));
|
||
|
memset(&me, 0, sizeof(me));
|
||
|
me.local = safe_alloc(sizeof(LocalClient));
|
||
|
memset(&loop, 0, sizeof(loop));
|
||
|
|
||
|
init_hash();
|
||
|
|
||
|
SetupEvents();
|
||
|
|
||
|
#ifdef _WIN32
|
||
|
CreateMutex(NULL, FALSE, "UnrealMutex");
|
||
|
SetErrorMode(SEM_FAILCRITICALERRORS);
|
||
|
#endif
|
||
|
#if !defined(_WIN32) && !defined(_AMIGA)
|
||
|
sbrk0 = (char *)sbrk((size_t)0);
|
||
|
uid = getuid();
|
||
|
euid = geteuid();
|
||
|
gid = getgid();
|
||
|
egid = getegid();
|
||
|
|
||
|
if (euid == 0)
|
||
|
{
|
||
|
fprintf(stderr,
|
||
|
"** ERROR **\n"
|
||
|
"You attempted to run UnrealIRCd as root. This is VERY DANGEROUS\n"
|
||
|
"as any compromise of your UnrealIRCd will result in full\n"
|
||
|
"privileges to the attacker on the entire machine.\n"
|
||
|
"You MUST start UnrealIRCd as a different user!\n"
|
||
|
"\n"
|
||
|
"For more information, see:\n"
|
||
|
"https://www.unrealircd.org/docs/Do_not_run_as_root\n"
|
||
|
"\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
#endif
|
||
|
#ifndef _WIN32
|
||
|
myargv = argv;
|
||
|
#else
|
||
|
cmdLine = GetCommandLine();
|
||
|
#endif
|
||
|
#ifndef _WIN32
|
||
|
(void)umask(077); /* better safe than sorry --SRB */
|
||
|
#else
|
||
|
WSAStartup(wVersionRequested, &wsaData);
|
||
|
#endif
|
||
|
memset(&StatsZ, 0, sizeof(StatsZ));
|
||
|
setup_signals();
|
||
|
|
||
|
memset(&irccounts, '\0', sizeof(irccounts));
|
||
|
irccounts.servers = 1;
|
||
|
|
||
|
mp_pool_init();
|
||
|
dbuf_init();
|
||
|
initlists();
|
||
|
|
||
|
#ifdef USE_LIBCURL
|
||
|
url_init();
|
||
|
#endif
|
||
|
tkl_init();
|
||
|
umode_init();
|
||
|
extcmode_init();
|
||
|
efunctions_init();
|
||
|
clear_scache_hash_table();
|
||
|
#ifndef _WIN32
|
||
|
/* Make it so we can dump core */
|
||
|
corelim.rlim_cur = corelim.rlim_max = RLIM_INFINITY;
|
||
|
setrlimit(RLIMIT_CORE, &corelim);
|
||
|
#endif
|
||
|
/*
|
||
|
* ** All command line parameters have the syntax "-fstring"
|
||
|
* ** or "-f string" (e.g. the space is optional). String may
|
||
|
* ** be empty. Flag characters cannot be concatenated (like
|
||
|
* ** "-fxyz"), it would conflict with the form "-fstring".
|
||
|
*/
|
||
|
while (--argc > 0 && (*++argv)[0] == '-') {
|
||
|
char *p = argv[0] + 1;
|
||
|
int flag = *p++;
|
||
|
if (flag == '\0' || *p == '\0') {
|
||
|
if (argc > 1 && argv[1][0] != '-') {
|
||
|
p = *++argv;
|
||
|
argc -= 1;
|
||
|
} else
|
||
|
p = "";
|
||
|
}
|
||
|
switch (flag)
|
||
|
{
|
||
|
case 'F':
|
||
|
bootopt |= BOOT_NOFORK;
|
||
|
break;
|
||
|
case 'f':
|
||
|
#ifndef _WIN32
|
||
|
if ((uid != euid) || (gid != egid))
|
||
|
{
|
||
|
printf("ERROR: Command line config with a setuid/setgid ircd is not allowed");
|
||
|
exit(1);
|
||
|
}
|
||
|
#endif
|
||
|
safe_strdup(configfile, p);
|
||
|
convert_to_absolute_path(&configfile, CONFDIR);
|
||
|
break;
|
||
|
#ifndef _WIN32
|
||
|
case 'P':{
|
||
|
short type;
|
||
|
char *result;
|
||
|
srandom(TStime());
|
||
|
type = Auth_FindType(NULL, p);
|
||
|
if (type == -1)
|
||
|
{
|
||
|
type = AUTHTYPE_ARGON2;
|
||
|
} else {
|
||
|
p = *++argv;
|
||
|
argc--;
|
||
|
}
|
||
|
if (BadPtr(p))
|
||
|
{
|
||
|
#ifndef _WIN32
|
||
|
p = getpass("Enter password to hash: ");
|
||
|
#else
|
||
|
printf("ERROR: You should specify a password to hash");
|
||
|
exit(1);
|
||
|
#endif
|
||
|
}
|
||
|
if ((type == AUTHTYPE_UNIXCRYPT) && (strlen(p) > 8))
|
||
|
{
|
||
|
/* Hmmm.. is this warning really still true (and always) ?? */
|
||
|
printf("WARNING: Password truncated to 8 characters due to 'crypt' algorithm. "
|
||
|
"You are suggested to use the 'argon2' algorithm instead.");
|
||
|
p[8] = '\0';
|
||
|
}
|
||
|
if (!(result = Auth_Hash(type, p))) {
|
||
|
printf("Failed to generate password. Deprecated method? Try 'argon2' instead.\n");
|
||
|
exit(0);
|
||
|
}
|
||
|
printf("Encrypted password is: %s\n", result);
|
||
|
exit(0);
|
||
|
}
|
||
|
#endif
|
||
|
#if 0
|
||
|
case 'S':
|
||
|
charsys_dump_table(p ? p : "*");
|
||
|
exit(0);
|
||
|
#endif
|
||
|
#ifndef _WIN32
|
||
|
case 't':
|
||
|
bootopt |= BOOT_TTY;
|
||
|
break;
|
||
|
case 'v':
|
||
|
(void)printf("%s build %s\n", version, buildid);
|
||
|
#else
|
||
|
case 'v':
|
||
|
if (!IsService) {
|
||
|
MessageBox(NULL, version,
|
||
|
"UnrealIRCD/Win32 version", MB_OK);
|
||
|
}
|
||
|
#endif
|
||
|
exit(0);
|
||
|
case 'C':
|
||
|
config_verbose = atoi(p);
|
||
|
break;
|
||
|
case 'c':
|
||
|
loop.config_test = 1;
|
||
|
break;
|
||
|
case 'x':
|
||
|
#ifdef DEBUGMODE
|
||
|
debuglevel = atoi(p);
|
||
|
debugmode = *p ? p : "0";
|
||
|
bootopt |= BOOT_DEBUG;
|
||
|
break;
|
||
|
#else
|
||
|
# ifndef _WIN32
|
||
|
(void)fprintf(stderr,
|
||
|
"%s: DEBUGMODE must be defined for -x y\n",
|
||
|
myargv[0]);
|
||
|
# else
|
||
|
if (!IsService) {
|
||
|
MessageBox(NULL,
|
||
|
"DEBUGMODE must be defined for -x option",
|
||
|
"UnrealIRCD/32", MB_OK);
|
||
|
}
|
||
|
# endif
|
||
|
exit(0);
|
||
|
#endif
|
||
|
#ifndef _WIN32
|
||
|
case 'k':
|
||
|
generate_cloakkeys();
|
||
|
exit(0);
|
||
|
#endif
|
||
|
case 'U':
|
||
|
if (chdir(CONFDIR) < 0)
|
||
|
{
|
||
|
fprintf(stderr, "Unable to change to '%s' directory\n", CONFDIR);
|
||
|
exit(1);
|
||
|
}
|
||
|
update_conf();
|
||
|
exit(0);
|
||
|
case 'R':
|
||
|
report_crash();
|
||
|
exit(0);
|
||
|
#ifndef _WIN32
|
||
|
case 'm':
|
||
|
modulemanager(argc, argv);
|
||
|
exit(0);
|
||
|
#endif
|
||
|
case '8':
|
||
|
utf8_test();
|
||
|
exit(0);
|
||
|
case 'L':
|
||
|
loop.boot_function = link_generator;
|
||
|
break;
|
||
|
default:
|
||
|
#ifndef _WIN32
|
||
|
return bad_command(myargv[0]);
|
||
|
#else
|
||
|
return bad_command(NULL);
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
do_version_check();
|
||
|
|
||
|
#if !defined(_WIN32)
|
||
|
#ifndef _WIN32
|
||
|
mkdir(TMPDIR, S_IRUSR|S_IWUSR|S_IXUSR); /* Create the tmp dir, if it doesn't exist */
|
||
|
mkdir(CACHEDIR, S_IRUSR|S_IWUSR|S_IXUSR); /* Create the cache dir, if it doesn't exist */
|
||
|
#else
|
||
|
mkdir(TMPDIR);
|
||
|
mkdir(CACHEDIR);
|
||
|
#endif
|
||
|
if (chdir(TMPDIR)) {
|
||
|
# ifndef _WIN32
|
||
|
perror("chdir");
|
||
|
fprintf(stderr, "ERROR: Unable to change to directory '%s'\n", TMPDIR);
|
||
|
# else
|
||
|
if (!IsService) {
|
||
|
MessageBox(NULL, strerror(GetLastError()),
|
||
|
"UnrealIRCD/32: chdir()", MB_OK);
|
||
|
}
|
||
|
# endif
|
||
|
exit(-1);
|
||
|
}
|
||
|
#endif
|
||
|
#ifndef _WIN32
|
||
|
/*
|
||
|
* didn't set debuglevel
|
||
|
*/
|
||
|
/*
|
||
|
* but asked for debugging output to tty
|
||
|
*/
|
||
|
if ((debuglevel < 0) && (bootopt & BOOT_TTY)) {
|
||
|
(void)fprintf(stderr,
|
||
|
"you specified -t without -x. use -x <n>\n");
|
||
|
exit(-1);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* HACK! This ifndef should be removed when the restart-on-w32-brings-up-dialog bug
|
||
|
* is fixed. This is just an ugly "ignore the invalid parameter" thing ;). -- Syzop
|
||
|
*/
|
||
|
#ifndef _WIN32
|
||
|
if (argc > 0)
|
||
|
return bad_command(myargv[0]); /* This should exit out */
|
||
|
#endif
|
||
|
#ifndef _WIN32
|
||
|
fprintf(stderr, "%s", unreallogo);
|
||
|
fprintf(stderr, " v%s\n\n", VERSIONONLY);
|
||
|
fprintf(stderr, "UnrealIRCd is brought to you by Bram Matthys (Syzop), Gottem and i\n\n");
|
||
|
|
||
|
fprintf(stderr, "Using the following libraries:\n");
|
||
|
fprintf(stderr, "* %s\n", pcre2_version());
|
||
|
fprintf(stderr, "* %s\n", SSLeay_version(SSLEAY_VERSION));
|
||
|
#ifdef USE_LIBCURL
|
||
|
fprintf(stderr, "* %s\n", curl_version());
|
||
|
#endif
|
||
|
#endif
|
||
|
check_user_limit();
|
||
|
#ifndef _WIN32
|
||
|
fprintf(stderr, "\n");
|
||
|
fprintf(stderr, "This server can handle %d concurrent sockets (%d clients + %d reserve)\n\n",
|
||
|
maxclients+CLIENTS_RESERVE, maxclients, CLIENTS_RESERVE);
|
||
|
#endif
|
||
|
init_CommandHash();
|
||
|
initwhowas();
|
||
|
initstats();
|
||
|
if (!loop.config_test)
|
||
|
DeleteTempModules();
|
||
|
booted = FALSE;
|
||
|
#if !defined(_WIN32) && !defined(_AMIGA) && !defined(OSXTIGER) && DEFAULT_PERMISSIONS != 0
|
||
|
/* Hack to stop people from being able to read the config file */
|
||
|
(void)chmod(CPATH, DEFAULT_PERMISSIONS);
|
||
|
#endif
|
||
|
init_dynconf();
|
||
|
early_init_ssl();
|
||
|
/*
|
||
|
* Add default class
|
||
|
*/
|
||
|
default_class = safe_alloc(sizeof(ConfigItem_class));
|
||
|
default_class->flag.permanent = 1;
|
||
|
default_class->pingfreq = 120;
|
||
|
default_class->maxclients = 100;
|
||
|
default_class->sendq = DEFAULT_RECVQ;
|
||
|
default_class->name = "default";
|
||
|
AddListItem(default_class, conf_class);
|
||
|
if (init_conf(configfile, 0) < 0)
|
||
|
{
|
||
|
exit(-1);
|
||
|
}
|
||
|
booted = TRUE;
|
||
|
load_tunefile();
|
||
|
make_umodestr();
|
||
|
SetListening(&me);
|
||
|
me.local->fd = -1;
|
||
|
SetMe(&me);
|
||
|
make_server(&me);
|
||
|
extcmodes_check_for_changes();
|
||
|
umodes_check_for_changes();
|
||
|
charsys_check_for_changes();
|
||
|
clicap_init();
|
||
|
if (!find_command_simple("PRIVMSG"))
|
||
|
{
|
||
|
config_error("Someone forgot to load modules with proper commands in them. READ THE DOCUMENTATION");
|
||
|
exit(-4);
|
||
|
}
|
||
|
|
||
|
#ifndef _WIN32
|
||
|
fprintf(stderr, "Initializing TLS..\n");
|
||
|
#endif
|
||
|
if (!init_ssl())
|
||
|
{
|
||
|
config_error("Failed to load SSL/TLS (see errors above). UnrealIRCd can not start.");
|
||
|
#ifdef _WIN32
|
||
|
win_error(); /* display error dialog box */
|
||
|
#endif
|
||
|
exit(9);
|
||
|
}
|
||
|
if (loop.config_test)
|
||
|
{
|
||
|
ircd_log(LOG_ERROR, "Configuration test passed OK");
|
||
|
fflush(stderr);
|
||
|
exit(0);
|
||
|
}
|
||
|
if (loop.boot_function)
|
||
|
loop.boot_function();
|
||
|
#ifndef _WIN32
|
||
|
fprintf(stderr, "Dynamic configuration initialized.. booting IRCd.\n");
|
||
|
#endif
|
||
|
open_debugfile();
|
||
|
me.local->port = 6667; /* pointless? */
|
||
|
init_sys();
|
||
|
applymeblock();
|
||
|
#ifdef HAVE_SYSLOG
|
||
|
openlog("ircd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
|
||
|
#endif
|
||
|
run_configuration();
|
||
|
ircd_log(LOG_ERROR, "UnrealIRCd started.");
|
||
|
|
||
|
read_motd(conf_files->botmotd_file, &botmotd);
|
||
|
read_motd(conf_files->rules_file, &rules);
|
||
|
read_motd(conf_files->opermotd_file, &opermotd);
|
||
|
read_motd(conf_files->motd_file, &motd);
|
||
|
read_motd(conf_files->smotd_file, &smotd);
|
||
|
read_motd(conf_files->svsmotd_file, &svsmotd);
|
||
|
|
||
|
me.hopcount = 0;
|
||
|
me.local->authfd = -1;
|
||
|
me.user = NULL;
|
||
|
me.direction = &me;
|
||
|
|
||
|
/*
|
||
|
* This listener will never go away
|
||
|
*/
|
||
|
me_hash = find_or_add(me.name);
|
||
|
me.serv->up = me_hash;
|
||
|
timeofday = time(NULL);
|
||
|
me.local->lasttime = me.local->since = me.local->firsttime = me.serv->boottime = TStime();
|
||
|
me.serv->features.protocol = UnrealProtocol;
|
||
|
safe_strdup(me.serv->features.software, version);
|
||
|
add_to_client_hash_table(me.name, &me);
|
||
|
add_to_id_hash_table(me.id, &me);
|
||
|
list_add(&me.client_node, &global_server_list);
|
||
|
#if !defined(_AMIGA) && !defined(_WIN32) && !defined(NO_FORKING)
|
||
|
if (!(bootopt & BOOT_NOFORK))
|
||
|
{
|
||
|
pid_t p;
|
||
|
p = fork();
|
||
|
if (p < 0)
|
||
|
{
|
||
|
fprintf(stderr, "Could not create background job. Call to fork() failed: %s\n",
|
||
|
strerror(errno));
|
||
|
exit(-1);
|
||
|
}
|
||
|
if (p > 0)
|
||
|
{
|
||
|
/* Background job created and we are the parent. We can terminate. */
|
||
|
exit(0);
|
||
|
}
|
||
|
/* Background process (child) continues below... */
|
||
|
close_std_descriptors();
|
||
|
fd_fork();
|
||
|
loop.ircd_forked = 1;
|
||
|
}
|
||
|
#endif
|
||
|
#ifdef _WIN32
|
||
|
loop.ircd_forked = 1;
|
||
|
#endif
|
||
|
|
||
|
fix_timers();
|
||
|
write_pidfile();
|
||
|
Debug((DEBUG_NOTICE, "Server ready..."));
|
||
|
init_throttling();
|
||
|
loop.ircd_booted = 1;
|
||
|
#if defined(HAVE_SETPROCTITLE)
|
||
|
setproctitle("%s", me.name);
|
||
|
#elif defined(HAVE_PSTAT)
|
||
|
pstats.pst_command = me.name;
|
||
|
pstat(PSTAT_SETCMD, pstats, strlen(me.name), 0, 0);
|
||
|
#elif defined(HAVE_PSSTRINGS)
|
||
|
PS_STRINGS->ps_nargvstr = 1;
|
||
|
PS_STRINGS->ps_argvstr = me.name;
|
||
|
#endif
|
||
|
module_loadall();
|
||
|
|
||
|
#ifndef _WIN32
|
||
|
SocketLoop(NULL);
|
||
|
#endif
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/** The main loop that the server will run all the time.
|
||
|
* On Windows this is a thread, on *NIX we simply jump here from main()
|
||
|
* when the server is ready.
|
||
|
*/
|
||
|
void SocketLoop(void *dummy)
|
||
|
{
|
||
|
struct timeval doevents_tv, process_clients_tv;
|
||
|
|
||
|
memset(&doevents_tv, 0, sizeof(doevents_tv));
|
||
|
memset(&process_clients_tv, 0, sizeof(process_clients_tv));
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
gettimeofday(&timeofday_tv, NULL);
|
||
|
timeofday = timeofday_tv.tv_sec;
|
||
|
|
||
|
detect_timeshift_and_warn();
|
||
|
|
||
|
if (minimum_msec_since_last_run(&doevents_tv, 250))
|
||
|
DoEvents();
|
||
|
|
||
|
/* Update statistics */
|
||
|
if (irccounts.clients > irccounts.global_max)
|
||
|
irccounts.global_max = irccounts.clients;
|
||
|
if (irccounts.me_clients > irccounts.me_max)
|
||
|
irccounts.me_max = irccounts.me_clients;
|
||
|
|
||
|
/* Process I/O */
|
||
|
fd_select(SOCKETLOOP_MAX_DELAY);
|
||
|
|
||
|
if (minimum_msec_since_last_run(&process_clients_tv, 200))
|
||
|
process_clients();
|
||
|
|
||
|
/* Check if there are pending "actions".
|
||
|
* These are actions that should be done outside of
|
||
|
* process_clients() and fd_select() when we are not
|
||
|
* processing any clients.
|
||
|
*/
|
||
|
if (dorehash)
|
||
|
{
|
||
|
(void)rehash(&me, 1);
|
||
|
dorehash = 0;
|
||
|
}
|
||
|
if (dorestart)
|
||
|
{
|
||
|
server_reboot("SIGINT");
|
||
|
}
|
||
|
if (doreloadcert)
|
||
|
{
|
||
|
reinit_ssl(NULL);
|
||
|
doreloadcert = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* open_debugfile
|
||
|
*
|
||
|
* If the -t option is not given on the command line when the server is
|
||
|
* started, all debugging output is sent to the file set by LPATH in config.h
|
||
|
* If the debuglevel is not set from the command line by -x, use /dev/null
|
||
|
* as the dummy logfile as long as DEBUGMODE has been defined, else don't
|
||
|
* waste the fd.
|
||
|
*/
|
||
|
static void open_debugfile(void)
|
||
|
{
|
||
|
#ifdef DEBUGMODE
|
||
|
int fd;
|
||
|
Client *client;
|
||
|
if (debuglevel >= 0) {
|
||
|
client = make_client(NULL, NULL);
|
||
|
client->local->fd = 2;
|
||
|
SetLog(client);
|
||
|
client->local->port = debuglevel;
|
||
|
client->flags = 0;
|
||
|
|
||
|
strlcpy(client->local->sockhost, me.local->sockhost, sizeof client->local->sockhost);
|
||
|
# ifndef _WIN32
|
||
|
/*(void)printf("isatty = %d ttyname = %#x\n",
|
||
|
isatty(2), (u_int)ttyname(2)); */
|
||
|
if (!(bootopt & BOOT_TTY)) { /* leave debugging output on fd 2 */
|
||
|
if (truncate(LOGFILE, 0) < 0)
|
||
|
fprintf(stderr, "WARNING: could not truncate log file '%s'\n", LOGFILE);
|
||
|
if ((fd = open(LOGFILE, O_WRONLY | O_CREAT, 0600)) < 0)
|
||
|
if ((fd = open("/dev/null", O_WRONLY)) < 0)
|
||
|
exit(-1);
|
||
|
|
||
|
#if 1
|
||
|
client->local->fd = fd;
|
||
|
debugfd = fd;
|
||
|
#else
|
||
|
/* if (fd != 2) {
|
||
|
(void)dup2(fd, 2);
|
||
|
(void)close(fd);
|
||
|
} -- hands off stderr! */
|
||
|
#endif
|
||
|
strlcpy(client->name, LOGFILE, sizeof(client->name));
|
||
|
} else if (isatty(2) && ttyname(2))
|
||
|
strlcpy(client->name, ttyname(2), sizeof(client->name));
|
||
|
else
|
||
|
# endif
|
||
|
strlcpy(client->name, "FD2-Pipe", sizeof(client->name));
|
||
|
Debug((DEBUG_FATAL,
|
||
|
"Debug: File <%s> Level: %d at %s", client->name,
|
||
|
client->local->port, myctime(time(NULL))));
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void setup_signals()
|
||
|
{
|
||
|
#ifndef _WIN32
|
||
|
struct sigaction act;
|
||
|
act.sa_handler = SIG_IGN;
|
||
|
act.sa_flags = 0;
|
||
|
(void)sigemptyset(&act.sa_mask);
|
||
|
(void)sigaddset(&act.sa_mask, SIGPIPE);
|
||
|
(void)sigaddset(&act.sa_mask, SIGALRM);
|
||
|
#ifdef SIGWINCH
|
||
|
(void)sigaddset(&act.sa_mask, SIGWINCH);
|
||
|
(void)sigaction(SIGWINCH, &act, NULL);
|
||
|
#endif
|
||
|
(void)sigaction(SIGPIPE, &act, NULL);
|
||
|
act.sa_handler = ignore_this_signal;
|
||
|
(void)sigaction(SIGALRM, &act, NULL);
|
||
|
act.sa_handler = s_rehash;
|
||
|
(void)sigemptyset(&act.sa_mask);
|
||
|
(void)sigaddset(&act.sa_mask, SIGHUP);
|
||
|
(void)sigaction(SIGHUP, &act, NULL);
|
||
|
act.sa_handler = s_restart;
|
||
|
(void)sigaddset(&act.sa_mask, SIGINT);
|
||
|
(void)sigaction(SIGINT, &act, NULL);
|
||
|
act.sa_handler = s_die;
|
||
|
(void)sigaddset(&act.sa_mask, SIGTERM);
|
||
|
(void)sigaction(SIGTERM, &act, NULL);
|
||
|
act.sa_handler = s_reloadcert;
|
||
|
(void)sigemptyset(&act.sa_mask);
|
||
|
(void)sigaddset(&act.sa_mask, SIGUSR1);
|
||
|
(void)sigaction(SIGUSR1, &act, NULL);
|
||
|
#endif
|
||
|
}
|