/*
* ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2014 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2019 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"
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 */
/**
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;
}
struct timeval tv;
time_t t;
+ Log(LOG_NOTICE, "Server \"%s\" (on \"%s\") ready.",
+ Client_ID(Client_ThisServer()), Client_Hostname(Client_ThisServer()));
+
while (!NGIRCd_SignalQuit && !NGIRCd_SignalRestart) {
t = time(NULL);
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,
* 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 );
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))
Log(LOG_ERR,
"Write error on connection %d (socket %d): %s!",
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;
}
/* 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;
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 */
/**
- * 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 Connecion 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,
Read_Request( CONN_ID Idx )
{
ssize_t len;
+ size_t readbuf_limit = READBUFFER_LEN;
static const unsigned int maxbps = COMMAND_LEN / 2;
- char readbuf[READBUFFER_LEN];
+ char readbuf[READBUFFER_MAX_LEN];
time_t t;
CLIENT *c;
assert( Idx > NONE );
assert( My_Connections[Idx].sock > NONE );
+ /* Make sure that there still exists a CLIENT structure associated
+ * with this connection and check if this is a server or not: */
+ c = Conn_GetClient(Idx);
+ if (c) {
+ /* Servers do get special read buffer limits, so they can
+ * process all the messages that are required while peering. */
+ if (Client_Type(c) == CLIENT_SERVER)
+ readbuf_limit = READBUFFER_SLINK_LEN;
+ } else
+ LogDebug("Read request without client (connection %d)!?", Idx);
+
#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) >= readbuf_limit) ||
+ (array_bytes(&My_Connections[Idx].zip.rbuf) >= readbuf_limit))
#else
- if (array_bytes(&My_Connections[Idx].rbuf) >= READBUFFER_LEN)
+ if (array_bytes(&My_Connections[Idx].rbuf) >= readbuf_limit)
#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);
+ Idx, array_bytes(&My_Connections[Idx].rbuf), readbuf_limit);
Conn_Close(Idx, "Receive buffer space exhausted", NULL, false);
return;
}
#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, readbuf_limit);
else
#endif
- len = read(My_Connections[Idx].sock, readbuf, sizeof(readbuf));
+ len = read(My_Connections[Idx].sock, readbuf, readbuf_limit);
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;
}
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);
* @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;
}
{
CONN_ID idx = Socket2Index(sock);
- 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");