/*
* ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2019 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2024 Alexander Barton (alex@barton.de) and Contributors.
*
* 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
#include "ng_ipaddr.h"
#include "parse.h"
#include "resolve.h"
+#include "sighandlers.h"
#define SERVER_WAIT (NONE - 1) /** "Wait for outgoing connection" flag */
/* not using systemd socket activation, initialize listening sockets: */
+#ifdef SSL_SUPPORT
+ if (!Conf_SSLOptions.KeyFile &&
+ array_length(&Conf_SSLOptions.ListenPorts, sizeof (UINT16))) {
+ Log(LOG_ERR,
+ "Ignoring SSL-enabled listening ports: No key file set!");
+ array_free(&Conf_SSLOptions.ListenPorts);
+ }
+#endif
+
/* can't use Conf_ListenAddress directly, see below */
copy = strdup(Conf_ListenAddress);
if (!copy) {
/**
* Set a socket to "IPv6 only". If the given socket doesn't belong to the
* AF_INET6 family, or the operating system doesn't support this functionality,
- * this function retruns silently.
+ * this function returns silently.
*
* @param af Address family of the socket.
* @param sock Socket handle.
int i;
size_t wdatalen;
struct timeval tv;
- time_t t;
+ time_t t, notify_t = 0;
bool command_available;
+ char status[200];
Log(LOG_NOTICE, "Server \"%s\" (on \"%s\") ready.",
Client_ID(Client_ThisServer()), Client_Hostname(Client_ThisServer()));
+ Signal_NotifySvcMgr("READY=1\n");
while (!NGIRCd_SignalQuit && !NGIRCd_SignalRestart) {
t = time(NULL);
exit(1);
}
- /* Should ngIRCd timeout when idle? */
+ t = time(NULL);
if (Conf_IdleTimeout > 0 && NumConnectionsAccepted > 0
- && idle_t > 0 && time(NULL) - idle_t >= Conf_IdleTimeout) {
+ && idle_t > 0 && t - idle_t >= Conf_IdleTimeout) {
+ /* Should ngIRCd timeout when idle? */
LogDebug("Server idle timeout reached: %d second%s. Initiating shutdown ...",
Conf_IdleTimeout,
Conf_IdleTimeout == 1 ? "" : "s");
NGIRCd_SignalQuit = true;
+ } else if (Signal_NotifySvcMgr_Possible() && t - notify_t > 3) {
+ /* Send the current status to the service manager. */
+ snprintf(status, sizeof(status),
+ "WATCHDOG=1\nSTATUS=%ld connection%s established (%ld user%s, %ld server%s), %ld maximum. %ld accepted in total.\n",
+ NumConnections, NumConnections == 1 ? "" : "s",
+ Client_MyUserCount(), Client_MyUserCount() == 1 ? "" : "s",
+ Client_MyServerCount(), Client_MyServerCount() == 1 ? "" : "s",
+ NumConnectionsMax, NumConnectionsAccepted);
+ Signal_NotifySvcMgr(status);
+ notify_t = t;
}
}
- if (NGIRCd_SignalQuit)
+ if (NGIRCd_SignalQuit) {
Log(LOG_NOTICE | LOG_snotice, "Server going down NOW!");
- else if (NGIRCd_SignalRestart)
+ Signal_NotifySvcMgr("STOPPING=1\n");
+ } else if (NGIRCd_SignalRestart) {
Log(LOG_NOTICE | LOG_snotice, "Server restarting NOW!");
+ Signal_NotifySvcMgr("RELOADING=1\n");
+ }
} /* Conn_Handler */
/**
* the result is a valid IRC message (oversized messages are shortened, for
* example). Then it calls the Conn_Write() function to do the actual sending.
*
- * @param Idx Index fo the connection.
+ * @param Idx Index of the connection.
* @param Format Format string, see printf().
* @returns true on success, false otherwise.
*/
#ifdef SNIFFER
if (NGIRCd_Sniffer)
- Log(LOG_DEBUG, " -> connection %d: '%s'.", Idx, buffer);
+ LogDebug("-> connection %d: '%s'.", Idx, buffer);
#endif
len = strlcat( buffer, "\r\n", sizeof( buffer ));
}
#endif
/* Send ERROR to client (see RFC 2812, section 3.1.7) */
- if (FwdMsg)
- Conn_WriteStr(Idx, "ERROR :%s", FwdMsg);
+ if (c)
+ Conn_WriteStr(Idx, "ERROR :Closing connection: %s[%s@%s] (%s)",
+ Client_ID(c), Client_User(c), Client_Hostname(c),
+ FwdMsg ? FwdMsg : "\"\"");
else
Conn_WriteStr(Idx, "ERROR :Closing connection");
}
} /* Conn_CountMax */
/**
- * Get number of connections accepted since the daemon startet.
+ * Get number of connections accepted since the daemon started.
*
* @returns Number of connections accepted.
*/
if (errno == EAGAIN || errno == EINTR)
return true;
- /* Log write errors but do not close the connection yet.
- * Calling Conn_Close() now could result in too many recursive calls.
- */
- if (!Conn_OPTION_ISSET(&My_Connections[Idx], CONN_ISCLOSING))
+ if (!Conn_OPTION_ISSET(&My_Connections[Idx], CONN_ISCLOSING)) {
Log(LOG_ERR,
"Write error on connection %d (socket %d): %s!",
Idx, My_Connections[Idx].sock, strerror(errno));
- else
+ Conn_Close(Idx, "Write error", NULL, false);
+ } else
LogDebug("Recursive write error on connection %d (socket %d): %s!",
Idx, My_Connections[Idx].sock, strerror(errno));
new_sock = accept(Sock, (struct sockaddr *)&new_addr,
(socklen_t *)&new_sock_len);
if (new_sock < 0) {
- Log(LOG_CRIT, "Can't accept connection: %s!", strerror(errno));
+ Log(LOG_CRIT, "Can't accept connection on socket %d: %s!",
+ Sock, strerror(errno));
return -1;
}
NumConnectionsAccepted++;
if (!ng_ipaddr_tostr_r(&new_addr, ip_str)) {
- Log(LOG_CRIT, "fd %d: Can't convert IP address!", new_sock);
+ Log(LOG_CRIT, "Can't convert peer IP address of socket %d!", new_sock);
Simple_Message(new_sock, "ERROR :Internal Server Error");
close(new_sock);
return -1;
fromhost(&req);
if (!hosts_access(&req)) {
Log(deny_severity,
- "Refused connection from %s (by TCP Wrappers)!", ip_str);
+ "Refused connection from %s on socket %d (by TCP Wrappers)!",
+ ip_str, Sock);
Simple_Message(new_sock, "ERROR :Connection refused");
close(new_sock);
return -1;
if ((Conf_MaxConnectionsIP > 0) && (cnt >= Conf_MaxConnectionsIP)) {
/* Access denied, too many connections from this IP address! */
Log(LOG_ERR,
- "Refused connection from %s: too may connections (%ld) from this IP address!",
- ip_str, cnt);
+ "Refused connection from %s on socket %d: too may connections (%ld) from this IP address!",
+ ip_str, Sock, cnt);
Simple_Message(new_sock,
"ERROR :Connection refused, too many connections from your IP address");
close(new_sock);
Account_Connection();
#ifdef SSL_SUPPORT
- /* Delay connection initalization until SSL handshake is finished */
+ /* Delay connection initialization until SSL handshake is finished */
if (!IsSSL)
#endif
Conn_StartLogin(new_sock);
assert(Idx >= 0);
- /* Nothing to do if DNS (and resolver subprocess) is disabled */
- if (!Conf_DNS)
- return;
-
#ifdef IDENTAUTH
/* Should we make an IDENT request? */
if (Conf_Ident)
if (Conf_NoticeBeforeRegistration) {
/* Send "NOTICE *" messages to the client */
#ifdef IDENTAUTH
- if (Conf_Ident)
- (void)Conn_WriteStr(Idx,
- "NOTICE * :*** Looking up your hostname and checking ident");
- else
+ if (Conf_Ident) {
+ if (Conf_DNS)
+ (void)Conn_WriteStr(Idx,
+ "NOTICE * :*** Looking up your hostname and checking ident");
+ else
+ (void)Conn_WriteStr(Idx,
+ "NOTICE * :*** Checking ident");
+ } else
#endif
+ if(Conf_DNS)
(void)Conn_WriteStr(Idx,
"NOTICE * :*** Looking up your hostname");
+ else
+ (void)Conn_WriteStr(Idx,
+ "NOTICE * :*** Processing your connection");
/* Send buffered data to the client, but break on errors
* because Handle_Write() would have closed the connection
* again in this case! */
return;
}
- Resolve_Addr(&My_Connections[Idx].proc_stat, &My_Connections[Idx].addr,
- ident_sock, cb_Read_Resolver_Result);
+ Resolve_Addr_Ident(&My_Connections[Idx].proc_stat,
+ &My_Connections[Idx].addr,
+ ident_sock, cb_Read_Resolver_Result);
}
/**
* a 1:1 mapping today) and enlarge the "connection pool" accordingly.
*
* @param Sock Socket handle.
- * @returns Connecion index or NONE when the pool is too small.
+ * @returns Connection index or NONE when the pool is too small.
*/
static CONN_ID
Socket2Index( int Sock )
* the resolver results, so we don't have to worry to override settings
* from these commands here. */
if(Client_Type(c) == CLIENT_UNKNOWN) {
- strlcpy(My_Connections[i].host, readbuf,
- sizeof(My_Connections[i].host));
- Client_SetHostname(c, readbuf);
- if (Conf_NoticeBeforeRegistration)
- (void)Conn_WriteStr(i,
+ if (readbuf[0]) {
+ /* We got a hostname */
+ strlcpy(My_Connections[i].host, readbuf,
+ sizeof(My_Connections[i].host));
+ Client_SetHostname(c, readbuf);
+ if (Conf_NoticeBeforeRegistration)
+ (void)Conn_WriteStr(i,
"NOTICE * :*** Found your hostname: %s",
My_Connections[i].host);
+ }
#ifdef IDENTAUTH
++identptr;
if (*identptr) {
Class_HandleServerBans(c);
}
-#ifdef DEBUG
else
LogDebug("Resolver: discarding result for already registered connection %d.", i);
-#endif
} /* cb_Read_Resolver_Result */
/**
/**
* IO callback for new outgoing SSL-enabled server connections.
*
+ * IMPORTANT: The SSL session has been validated before, but all errors have
+ * been ignored so far! The reason for this is that the generic SSL code has no
+ * idea if the new session actually belongs to a server, as this only becomes
+ * clear when the remote peer sends its PASS command (and we have to handle
+ * invalid client certificates!). Therefore, it is important to check the
+ * status of the SSL session first before continuing the server handshake here!
+ *
* @param sock Socket descriptor.
* @param unused (ignored IO specification)
*/
cb_connserver_login_ssl(int sock, short unused)
{
CONN_ID idx = Socket2Index(sock);
+ int serveridx;
(void) unused;
return;
}
- Log( LOG_INFO, "SSL connection %d with \"%s:%d\" established.", idx,
- My_Connections[idx].host, Conf_Server[Conf_GetServer( idx )].port );
+ serveridx = Conf_GetServer(idx);
+ assert(serveridx >= 0);
+ /* The SSL handshake is done, but validation results were ignored so
+ * far, so let's see where we are: */
+ LogDebug("SSL handshake on socket %d done.", idx);
+ if (!Conn_OPTION_ISSET(&My_Connections[idx], CONN_SSL_PEERCERT_OK)) {
+ if (Conf_Server[serveridx].SSLVerify) {
+ Log(LOG_ERR,
+ "Peer certificate check failed for \"%s\" on connection %d!",
+ My_Connections[idx].host, idx);
+ Conn_Close(idx, "Valid certificate required",
+ NULL, false);
+ return;
+ }
+ Log(LOG_WARNING,
+ "Peer certificate check failed for \"%s\" on connection %d, but \"SSLVerify\" is disabled. Continuing ...",
+ My_Connections[idx].host, idx);
+ }
+ LogDebug("Server certificate accepted, continuing server login ...");
server_login(idx);
}
#endif /* SSL_SUPPORT */
-#ifdef DEBUG
/**
* Dump internal state of the "connection module".
{
int i;
- Log(LOG_DEBUG, "Connection status:");
+ LogDebug("Connection status:");
for (i = 0; i < Pool_Size; i++) {
if (My_Connections[i].sock == NONE)
continue;
- Log(LOG_DEBUG,
+ LogDebug(
" - %d: host=%s, lastdata=%ld, lastping=%ld, delaytime=%ld, flag=%d, options=%d, bps=%d, client=%s",
My_Connections[i].sock, My_Connections[i].host,
My_Connections[i].lastdata, My_Connections[i].lastping,
}
} /* Conn_DumpClients */
-#endif /* DEBUG */
/* -eof- */