X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd-alex.git;a=blobdiff_plain;f=src%2Fngircd%2Fconn.c;h=ae151b3104693e4b783361d5709e6a8d9f891878;hp=f62e96754fa9ef610ef556f246f7b6f3711648cd;hb=b0caf5984ea0f6db7d864dee05c2ccdf78907298;hpb=bb1d014abad85b6938cf9d3e88e64f4ee6757ede diff --git a/src/ngircd/conn.c b/src/ngircd/conn.c index f62e9675..ae151b31 100644 --- a/src/ngircd/conn.c +++ b/src/ngircd/conn.c @@ -10,6 +10,7 @@ */ #define CONN_MODULE +#define CONN_MODULE_GLOBAL_INIT #include "portab.h" @@ -590,7 +591,7 @@ set_v6_only(int af, int sock) /** * 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. */ @@ -659,12 +660,14 @@ Conn_Handler(void) size_t wdatalen; struct timeval tv; time_t t; + bool command_available; Log(LOG_NOTICE, "Server \"%s\" (on \"%s\") ready.", Client_ID(Client_ThisServer()), Client_Hostname(Client_ThisServer())); while (!NGIRCd_SignalQuit && !NGIRCd_SignalRestart) { t = time(NULL); + command_available = false; /* Check configured servers and established links */ Check_Servers(); @@ -733,16 +736,31 @@ Conn_Handler(void) 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); @@ -1271,6 +1289,9 @@ Handle_Write( CONN_ID Idx ) 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!", @@ -1278,7 +1299,7 @@ Handle_Write( CONN_ID Idx ) 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; } @@ -1539,16 +1560,21 @@ Socket2Index( int Sock ) * @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)) @@ -1556,7 +1582,6 @@ Read_Request( CONN_ID Idx ) 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); @@ -1564,12 +1589,14 @@ Read_Request( CONN_ID Idx ) 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, @@ -1579,13 +1606,20 @@ Read_Request( CONN_ID Idx ) } 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, @@ -1832,6 +1866,9 @@ Check_Connections(void) 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) @@ -1846,7 +1883,7 @@ Check_Connections(void) 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", @@ -1855,10 +1892,10 @@ Check_Connections(void) 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())); } @@ -1869,7 +1906,7 @@ Check_Connections(void) * still not registered. */ if (My_Connections[i].lastdata < - time(NULL) - Conf_PongTimeout) { + time_now - Conf_PongTimeout) { LogDebug ("Unregistered connection %d timed out ...", i);