]> arthur.barton.de Git - ngircd-alex.git/blobdiff - src/ngircd/conn.c
2023!
[ngircd-alex.git] / src / ngircd / conn.c
index f62e96754fa9ef610ef556f246f7b6f3711648cd..e8ef68f36c7ec32642b806c78e5f70d4dc01feea 100644 (file)
@@ -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);
@@ -857,7 +875,7 @@ va_dcl
 
 #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 ));
@@ -1037,8 +1055,10 @@ Conn_Close(CONN_ID Idx, const char *LogMsg, const char *FwdMsg, bool InformClien
                }
 #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");
        }
@@ -1271,6 +1291,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 +1301,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;
        }
 
@@ -1335,13 +1358,14 @@ New_Connection(int Sock, UNUSED bool IsSSL)
        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;
@@ -1354,7 +1378,8 @@ New_Connection(int Sock, UNUSED bool IsSSL)
        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;
@@ -1379,8 +1404,8 @@ New_Connection(int Sock, UNUSED bool IsSSL)
        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);
@@ -1433,7 +1458,7 @@ New_Connection(int Sock, UNUSED bool IsSSL)
        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);
@@ -1539,16 +1564,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 +1586,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 +1593,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 +1610,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 +1870,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 +1887,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 +1896,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 +1910,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);
@@ -2304,10 +2345,8 @@ cb_Read_Resolver_Result( int r_fd, UNUSED short events )
 
                Class_HandleServerBans(c);
        }
-#ifdef DEBUG
        else
                LogDebug("Resolver: discarding result for already registered connection %d.", i);
-#endif
 } /* cb_Read_Resolver_Result */
 
 /**
@@ -2651,7 +2690,6 @@ Conn_SetCertFp(UNUSED CONN_ID Idx, UNUSED const char *fingerprint)
 
 #endif /* SSL_SUPPORT */
 
-#ifdef DEBUG
 
 /**
  * Dump internal state of the "connection module".
@@ -2661,11 +2699,11 @@ Conn_DebugDump(void)
 {
        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,
@@ -2675,6 +2713,5 @@ Conn_DebugDump(void)
        }
 } /* Conn_DumpClients */
 
-#endif /* DEBUG */
 
 /* -eof- */