/*
* ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2014 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
*/
#define CONN_MODULE
+#define CONN_MODULE_GLOBAL_INIT
#include "portab.h"
#include "ng_ipaddr.h"
#include "parse.h"
#include "resolve.h"
+#include "sighandlers.h"
#define SERVER_WAIT (NONE - 1) /** "Wait for outgoing connection" flag */
CONN_ID idx = Socket2Index( sock );
if (idx <= NONE) {
- LogDebug("cb_connserver wants to write on unknown socket?!");
io_close(sock);
return;
}
{
CONN_ID idx = Socket2Index(sock);
- assert(idx >= 0);
-
- if (idx < 0) {
+ if (idx <= NONE) {
io_close(sock);
return;
}
+
#ifdef SSL_SUPPORT
if (what & IO_WANTREAD
|| (Conn_OPTION_ISSET(&My_Connections[idx], CONN_SSL_WANT_WRITE))) {
GLOBAL void
Conn_Init( void )
{
- CONN_ID i;
-
- Pool_Size = CONNECTION_POOL;
- if ((Conf_MaxConnections > 0) &&
- (Pool_Size > Conf_MaxConnections))
- Pool_Size = Conf_MaxConnections;
+ int size;
- if (!array_alloc(&My_ConnArray, sizeof(CONNECTION), (size_t)Pool_Size)) {
- Log(LOG_EMERG, "Can't allocate memory! [Conn_Init]");
+ /* Initialize the "connection pool".
+ * FIXME: My_Connetions/Pool_Size is needed by other parts of the
+ * code; remove them! */
+ Pool_Size = 0;
+ size = Conf_MaxConnections > 0 ? Conf_MaxConnections : CONNECTION_POOL;
+ if (Socket2Index(size) <= NONE) {
+ Log(LOG_EMERG, "Failed to initialize connection pool!");
exit(1);
}
- /* FIXME: My_Connetions/Pool_Size is needed by other parts of the
- * code; remove them! */
- My_Connections = (CONNECTION*) array_start(&My_ConnArray);
-
- LogDebug("Allocated connection pool for %d items (%ld bytes).",
- array_length(&My_ConnArray, sizeof(CONNECTION)),
- array_bytes(&My_ConnArray));
-
- assert(array_length(&My_ConnArray, sizeof(CONNECTION)) >= (size_t)Pool_Size);
-
+ /* Initialize "listener" array. */
array_free( &My_Listeners );
-
- for (i = 0; i < Pool_Size; i++)
- Init_Conn_Struct(i);
} /* Conn_Init */
/**
/* 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) {
if (!ret) {
assert(listen_addrstr);
Log(LOG_CRIT,
- "Can't bind to [%s]:%u: can't convert ip address \"%s\"!",
- listen_addrstr, Port, listen_addrstr);
+ "Can't listen on [%s]:%u: Failed to parse IP address!",
+ listen_addrstr, Port);
}
return ret;
}
/**
* 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.
/**
* Initialize new listening port.
*
- * @param listen_addr Local address to bind the socet to (can be 0.0.0.0).
+ * @param listen_addr Local address to bind the socket to (can be 0.0.0.0).
* @param Port Port number on which the new socket should be listening.
* @returns file descriptor of the socket or -1 on failure.
*/
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);
+ command_available = false;
/* Check configured servers and established links */
Check_Servers();
continue;
}
+ if (array_bytes(&My_Connections[i].rbuf) >= COMMAND_LEN) {
+ /* There is still more data in the read buffer
+ * than a single valid command can get long:
+ * so either there is a complete command, or
+ * invalid data. Therefore don't try to read in
+ * even more data from the network but wait for
+ * this command(s) to be handled first! */
+ io_event_del(My_Connections[i].sock,
+ IO_WANTREAD);
+ command_available = true;
+ continue;
+ }
+
io_event_add(My_Connections[i].sock, IO_WANTREAD);
}
- /* Set the timeout for reading from the network to 1 second,
- * which is the granularity with witch we handle "penalty
- * times" for example.
+ /* Don't wait for data when there is still at least one command
+ * available in a read buffer which can be handled immediately;
+ * set the timeout for reading from the network to 1 second
+ * otherwise, which is the granularity with witch we handle
+ * "penalty times" for example.
* Note: tv_sec/usec are undefined(!) after io_dispatch()
* returns, so we have to set it before each call to it! */
tv.tv_usec = 0;
- tv.tv_sec = 1;
+ tv.tv_sec = command_available ? 0 : 1;
/* Wait for activity ... */
i = io_dispatch(&tv);
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",
+ (long)NumConnections, NumConnections == 1 ? "" : "s",
+ Client_MyUserCount(), Client_MyUserCount() == 1 ? "" : "s",
+ Client_MyServerCount(), Client_MyServerCount() == 1 ? "" : "s",
+ (long)NumConnectionsMax, (long)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.
*/
GLOBAL bool
Conn_WriteStr(CONN_ID Idx, const char *Format, ...)
#else
-GLOBAL bool
+GLOBAL bool
Conn_WriteStr(Idx, Format, va_alist)
CONN_ID Idx;
const char *Format;
* IRC_WriteXXX() functions when the prefix of this server had
* to be added to an already "quite long" command line which
* has been received from a regular IRC client, for example.
- *
+ *
* We are not allowed to send such "oversized" messages to
* other servers and clients, see RFC 2812 2.3 and 2813 3.3
* ("these messages SHALL NOT exceed 512 characters in length,
#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");
}
* the calculation of in_p and out_p: in_z_k and out_z_k
* are non-zero, that's guaranteed by the protocol until
* compression can be enabled. */
- if (! in_z_k)
+ if (in_z_k <= 0)
in_z_k = in_k;
- if (! out_z_k)
+ if (out_z_k <= 0)
out_z_k = out_k;
in_p = (int)(( in_k * 100 ) / in_z_k );
out_p = (int)(( out_k * 100 ) / out_z_k );
} /* 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;
- 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));
- Conn_Close(Idx, "Write error", NULL, false);
+
return false;
}
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;
/* Check global connection limit */
if ((Conf_MaxConnections > 0) &&
(NumConnections >= (size_t) Conf_MaxConnections)) {
- Log(LOG_ALERT, "Can't accept connection: limit (%d) reached!",
- Conf_MaxConnections);
+ Log(LOG_ALERT, "Can't accept new connection on socket %d: Limit (%d) reached!",
+ Sock, Conf_MaxConnections);
Simple_Message(new_sock, "ERROR :Connection limit reached");
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);
return -1;
}
- if (new_sock >= Pool_Size) {
- if (!array_alloc(&My_ConnArray, sizeof(CONNECTION),
- (size_t) new_sock)) {
- Log(LOG_EMERG,
- "Can't allocate memory! [New_Connection]");
- Simple_Message(new_sock, "ERROR: Internal error");
- close(new_sock);
- return -1;
- }
- LogDebug("Bumped connection pool to %ld items (internal: %ld items, %ld bytes)",
- new_sock, array_length(&My_ConnArray,
- sizeof(CONNECTION)), array_bytes(&My_ConnArray));
-
- /* Adjust pointer to new block */
- My_Connections = array_start(&My_ConnArray);
- while (Pool_Size <= new_sock)
- Init_Conn_Struct(Pool_Size++);
+ if (Socket2Index(new_sock) <= NONE) {
+ Simple_Message(new_sock, "ERROR: Internal error");
+ close(new_sock);
+ return -1;
}
/* register callback */
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)
ident_sock = My_Connections[Idx].sock;
#endif
- if (Conf_NoticeAuth) {
- /* Send "NOTICE AUTH" messages to the client */
+ if (Conf_NoticeBeforeRegistration) {
+ /* Send "NOTICE *" messages to the client */
#ifdef IDENTAUTH
- if (Conf_Ident)
+ 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 AUTH :*** Looking up your hostname and checking ident");
+ "NOTICE * :*** Looking up your hostname");
else
-#endif
(void)Conn_WriteStr(Idx,
- "NOTICE AUTH :*** Looking up your hostname");
+ "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);
}
/**
} /* Account_Connection */
/**
- * Translate socket handle into connection index.
+ * Translate socket handle into connection index (for historical reasons, it is
+ * a 1:1 mapping today) and enlarge the "connection pool" accordingly.
*
* @param Sock Socket handle.
- * @returns Connecion index or NONE, if no connection could be found.
+ * @returns Connection index or NONE when the pool is too small.
*/
static CONN_ID
Socket2Index( int Sock )
{
- assert( Sock >= 0 );
+ assert(Sock > 0);
+ assert(Pool_Size >= 0);
+
+ if (Sock < Pool_Size)
+ return Sock;
- if( Sock >= Pool_Size || My_Connections[Sock].sock != Sock ) {
- /* the Connection was already closed again, likely due to
- * an error. */
- LogDebug("Socket2Index: can't get connection for socket %d!", Sock);
+ /* Try to allocate more memory ... */
+ if (!array_alloc(&My_ConnArray, sizeof(CONNECTION), (size_t)Sock)) {
+ Log(LOG_EMERG,
+ "Can't allocate memory to enlarge connection pool!");
return NONE;
}
+ LogDebug("Enlarged connection pool for %ld sockets (%ld items, %ld bytes)",
+ Sock, array_length(&My_ConnArray, sizeof(CONNECTION)),
+ array_bytes(&My_ConnArray));
+
+ /* Adjust pointer to new block, update size and initialize new items. */
+ My_Connections = array_start(&My_ConnArray);
+ while (Pool_Size <= Sock)
+ Init_Conn_Struct(Pool_Size++);
+
return Sock;
-} /* Socket2Index */
+}
/**
* Read data from the network to the read buffer. If an error occurs,
* @param Idx Connection index.
*/
static void
-Read_Request( CONN_ID Idx )
+Read_Request(CONN_ID Idx)
{
ssize_t len;
static const unsigned int maxbps = COMMAND_LEN / 2;
char readbuf[READBUFFER_LEN];
time_t t;
CLIENT *c;
- assert( Idx > NONE );
- assert( My_Connections[Idx].sock > NONE );
+ assert(Idx > NONE);
+ assert(My_Connections[Idx].sock > NONE);
+
+ /* Check if the read buffer is "full". Basically this shouldn't happen
+ * here, because as long as there possibly are commands in the read
+ * buffer (buffer usage > COMMAND_LEN), the socket shouldn't be
+ * scheduled for reading in Conn_Handler() at all ... */
#ifdef ZLIB
if ((array_bytes(&My_Connections[Idx].rbuf) >= READBUFFER_LEN) ||
(array_bytes(&My_Connections[Idx].zip.rbuf) >= READBUFFER_LEN))
if (array_bytes(&My_Connections[Idx].rbuf) >= READBUFFER_LEN)
#endif
{
- /* Read buffer is full */
Log(LOG_ERR,
"Receive buffer space exhausted (connection %d): %d/%d bytes",
Idx, array_bytes(&My_Connections[Idx].rbuf), READBUFFER_LEN);
return;
}
+ /* Now read new data from the network, up to READBUFFER_LEN bytes ... */
#ifdef SSL_SUPPORT
if (Conn_OPTION_ISSET(&My_Connections[Idx], CONN_SSL))
- len = ConnSSL_Read( &My_Connections[Idx], readbuf, sizeof(readbuf));
+ len = ConnSSL_Read(&My_Connections[Idx], readbuf, sizeof(readbuf));
else
#endif
- len = read(My_Connections[Idx].sock, readbuf, sizeof(readbuf));
+ len = read(My_Connections[Idx].sock, readbuf, sizeof(readbuf));
+
if (len == 0) {
LogDebug("Client \"%s:%u\" is closing connection %d ...",
My_Connections[Idx].host,
- ng_ipaddr_tostr(&My_Connections[Idx].addr), Idx);
+ ng_ipaddr_getport(&My_Connections[Idx].addr), Idx);
Conn_Close(Idx, NULL, "Client closed connection", false);
return;
}
if (len < 0) {
- if( errno == EAGAIN ) return;
+ if (errno == EAGAIN)
+ return;
+
Log(LOG_ERR, "Read error on connection %d (socket %d): %s!",
Idx, My_Connections[Idx].sock, strerror(errno));
Conn_Close(Idx, "Read error", "Client closed connection",
false);
return;
}
+
+ /* Now append the newly received data to the connection buffer.
+ * NOTE: This can lead to connection read buffers being bigger(!) than
+ * READBUFFER_LEN bytes, as we add up to READBUFFER_LEN new bytes to a
+ * buffer possibly being "almost" READBUFFER_LEN bytes already! */
#ifdef ZLIB
if (Conn_OPTION_ISSET(&My_Connections[Idx], CONN_ZIP)) {
if (!array_catb(&My_Connections[Idx].zip.rbuf, readbuf,
CLIENT *c;
CONN_ID i;
char msg[64];
+ time_t time_now;
+
+ time_now = time(NULL);
for (i = 0; i < Pool_Size; i++) {
if (My_Connections[i].sock < 0)
My_Connections[i].lastdata) {
/* We already sent a ping */
if (My_Connections[i].lastping <
- time(NULL) - Conf_PongTimeout) {
+ time_now - Conf_PongTimeout) {
/* Timeout */
snprintf(msg, sizeof(msg),
"Ping timeout: %d seconds",
Conn_Close(i, NULL, msg, true);
}
} else if (My_Connections[i].lastdata <
- time(NULL) - Conf_PingTimeout) {
+ time_now - Conf_PingTimeout) {
/* We need to send a PING ... */
LogDebug("Connection %d: sending PING ...", i);
- Conn_UpdatePing(i);
+ Conn_UpdatePing(i, time_now);
Conn_WriteStr(i, "PING :%s",
Client_ID(Client_ThisServer()));
}
* still not registered. */
if (My_Connections[i].lastdata <
- time(NULL) - Conf_PongTimeout) {
+ time_now - Conf_PongTimeout) {
LogDebug
("Unregistered connection %d timed out ...",
i);
Conf_Server[i].lasttry = time_now;
Conf_Server[i].conn_id = SERVER_WAIT;
assert(Proc_GetPipeFd(&Conf_Server[i].res_stat) < 0);
- Resolve_Name(&Conf_Server[i].res_stat, Conf_Server[i].host,
- cb_Connect_to_Server);
+
+ /* Start resolver subprocess ... */
+ if (!Resolve_Name(&Conf_Server[i].res_stat, Conf_Server[i].host,
+ cb_Connect_to_Server))
+ Conf_Server[i].conn_id = NONE;
}
} /* Check_Servers */
return;
}
- if (!array_alloc(&My_ConnArray, sizeof(CONNECTION), (size_t)new_sock)) {
- Log(LOG_ALERT,
- "Cannot allocate memory for server connection (socket %d)",
- new_sock);
+ if (Socket2Index(new_sock) <= NONE) {
close( new_sock );
Conf_Server[Server].conn_id = NONE;
return;
return;
}
- My_Connections = array_start(&My_ConnArray);
-
assert(My_Connections[new_sock].sock <= 0);
Init_Conn_Struct(new_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_NoticeAuth)
- (void)Conn_WriteStr(i,
- "NOTICE AUTH :*** Found your hostname: %s",
+ 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) {
i, identptr);
Client_SetUser(c, identptr, true);
}
- if (Conf_NoticeAuth) {
+ if (Conf_NoticeBeforeRegistration) {
(void)Conn_WriteStr(i,
- "NOTICE AUTH :*** Got %sident response%s%s",
+ "NOTICE * :*** Got %sident response%s%s",
*ptr ? "invalid " : "",
*ptr ? "" : ": ",
*ptr ? "" : identptr);
}
} else if(Conf_Ident) {
Log(LOG_INFO, "IDENT lookup for connection %d: no result.", i);
- if (Conf_NoticeAuth)
+ if (Conf_NoticeBeforeRegistration)
(void)Conn_WriteStr(i,
- "NOTICE AUTH :*** No ident response");
+ "NOTICE * :*** No ident response");
}
#endif
- if (Conf_NoticeAuth) {
+ if (Conf_NoticeBeforeRegistration) {
/* Send buffered data to the client, but break on
* errors because Handle_Write() would have closed
* the connection again in this case! */
Class_HandleServerBans(c);
}
-#ifdef DEBUG
else
LogDebug("Resolver: discarding result for already registered connection %d.", i);
-#endif
} /* cb_Read_Resolver_Result */
/**
* @returns Pointer to CLIENT structure.
*/
GLOBAL CLIENT *
-Conn_GetClient( CONN_ID Idx )
+Conn_GetClient( CONN_ID Idx )
{
CONNECTION *c;
* @param Reason The reason, see THROTTLE_xxx constants.
* @param Idx The connection index.
* @param Client The client of this connection.
- * @param Seconds The time to delay this connection.
+ * @param Value The time to delay this connection.
*/
static void
Throttle_Connection(const CONN_ID Idx, CLIENT *Client, const int Reason,
{
CONN_ID idx = Socket2Index(sock);
- assert(idx >= 0);
-
- if (idx < 0) {
+ if (idx <= NONE) {
io_close(sock);
return;
}
/**
* 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;
- assert(idx >= 0);
- if (idx < 0) {
+ (void) unused;
+
+ if (idx <= NONE) {
io_close(sock);
return;
}
- (void) unused;
+
switch (ConnSSL_Connect( &My_Connections[idx])) {
case 1: break;
case 0: LogDebug("ConnSSL_Connect: not ready");
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- */