]> arthur.barton.de Git - ngircd-alex.git/blobdiff - src/ngircd/conn.c
Mac OS X: fix test for packagemaker(1) tool in Makefile
[ngircd-alex.git] / src / ngircd / conn.c
index b29ad7e353dbb5042d94db35f97798b0ef26bb07..d62be359c5435000418d75956e9b5fa2b41e086e 100644 (file)
 
 #define SERVER_WAIT (NONE - 1)
 
+#define MAX_COMMANDS 3
+#define MAX_COMMANDS_SERVER 10
+
 
 static bool Handle_Write PARAMS(( CONN_ID Idx ));
 static bool Conn_Write PARAMS(( CONN_ID Idx, char *Data, size_t Len ));
 static int New_Connection PARAMS(( int Sock ));
 static CONN_ID Socket2Index PARAMS(( int Sock ));
 static void Read_Request PARAMS(( CONN_ID Idx ));
-static void Handle_Buffer PARAMS(( CONN_ID Idx ));
+static unsigned int Handle_Buffer PARAMS(( CONN_ID Idx ));
 static void Check_Connections PARAMS(( void ));
 static void Check_Servers PARAMS(( void ));
 static void Init_Conn_Struct PARAMS(( CONN_ID Idx ));
@@ -234,12 +237,12 @@ cb_connserver_login_ssl(int sock, short unused)
        case 0: LogDebug("ConnSSL_Connect: not ready");
                return;
        case -1:
-               Log(LOG_INFO, "SSL connection on socket %d failed", sock);
+               Log(LOG_ERR, "SSL connection on socket %d failed!", sock);
                Conn_Close(idx, "Can't connect!", NULL, false);
                return;
        }
 
-       Log( LOG_INFO, "SSL Connection %d with \"%s:%d\" established.", idx,
+       Log( LOG_INFO, "SSL connection %d with \"%s:%d\" established.", idx,
                        My_Connections[idx].host, Conf_Server[Conf_GetServer( idx )].port );
 
        server_login(idx);
@@ -304,8 +307,6 @@ cb_clientserver_ssl(int sock, short what)
 GLOBAL void
 Conn_Init( void )
 {
-       /* Modul initialisieren: statische Strukturen "ausnullen". */
-
        CONN_ID i;
 
        /* Speicher fuer Verbindungs-Pool anfordern */
@@ -341,16 +342,11 @@ Conn_Init( void )
 GLOBAL void
 Conn_Exit( void )
 {
-       /* Modul abmelden: alle noch offenen Connections
-        * schliessen und freigeben. */
-
        CONN_ID idx;
 
-       LogDebug("Shutting down all connections ..." );
-
        Conn_ExitListeners();
 
-       /* Sockets schliessen */
+       LogDebug("Shutting down all connections ..." );
        for( idx = 0; idx < Pool_Size; idx++ ) {
                if( My_Connections[idx].sock > NONE ) {
                        Conn_Close( idx, NULL, NGIRCd_SignalRestart ?
@@ -451,7 +447,8 @@ Conn_ExitListeners( void )
 #endif
 
        arraylen = array_length(&My_Listeners, sizeof (int));
-       Log( LOG_INFO, "Shutting down all listening sockets (%d total)...", arraylen );
+       Log(LOG_INFO,
+           "Shutting down all listening sockets (%d total) ...", arraylen);
        fd = array_start(&My_Listeners);
        while(arraylen--) {
                assert(fd != NULL);
@@ -628,7 +625,7 @@ GLOBAL void
 Conn_Handler(void)
 {
        int i;
-       unsigned int wdatalen;
+       unsigned int wdatalen, bytes_processed;
        struct timeval tv;
        time_t t;
 
@@ -651,9 +648,19 @@ Conn_Handler(void)
                for (i = 0; i < Pool_Size; i++) {
                        if ((My_Connections[i].sock > NONE)
                            && (array_bytes(&My_Connections[i].rbuf) > 0)
-                           && (My_Connections[i].delaytime < t)) {
+                           && (My_Connections[i].delaytime <= t)) {
                                /* ... and try to handle the received data */
-                               Handle_Buffer(i);
+                               bytes_processed = Handle_Buffer(i);
+                               /* if we processed data, and there might be
+                                * more commands in the input buffer, do not
+                                * try to read any more data now */
+                               if (bytes_processed &&
+                                   array_bytes(&My_Connections[i].rbuf) > 2) {
+                                       LogDebug
+                                           ("Throttling connection %d: command limit reached!",
+                                            i);
+                                       Conn_SetPenalty(i, 1);
+                               }
                        }
                }
 
@@ -887,13 +894,13 @@ Conn_Write( CONN_ID Idx, char *Data, size_t Len )
 
 
 GLOBAL void
-Conn_Close( CONN_ID Idx, char *LogMsg, char *FwdMsg, bool InformClient )
+Conn_Close( CONN_ID Idx, const char *LogMsg, const char *FwdMsg, bool InformClient )
 {
        /* Close connection. Open pipes of asyncronous resolver
         * sub-processes are closed down. */
 
        CLIENT *c;
-       char *txt;
+       const char *txt;
        double in_k, out_k;
        UINT16 port;
 #ifdef ZLIB
@@ -943,7 +950,7 @@ Conn_Close( CONN_ID Idx, char *LogMsg, char *FwdMsg, bool InformClient )
                         (double)My_Connections[Idx].bytes_out / 1024);
                }
 #endif
-               /* Send ERROR to client (see RFC!) */
+               /* Send ERROR to client (see RFC 2812, section 3.1.7) */
                if (FwdMsg)
                        Conn_WriteStr(Idx, "ERROR :%s", FwdMsg);
                else
@@ -960,7 +967,7 @@ Conn_Close( CONN_ID Idx, char *LogMsg, char *FwdMsg, bool InformClient )
        c = Conn_GetClient( Idx );
 #ifdef SSL_SUPPORT
        if ( Conn_OPTION_ISSET( &My_Connections[Idx], CONN_SSL )) {
-               Log( LOG_INFO, "SSL Connection %d shutting down", Idx );
+               Log(LOG_INFO, "SSL connection %d shutting down ...", Idx);
                ConnSSL_Free(&My_Connections[Idx]);
        }
 #endif
@@ -1262,14 +1269,21 @@ New_Connection( int Sock )
        My_Connections[new_sock].addr = new_addr;
        My_Connections[new_sock].client = c;
 
-       Log( LOG_INFO, "Accepted connection %d from %s:%d on socket %d.", new_sock,
-                       ip_str, ng_ipaddr_getport(&new_addr), Sock);
-
-       /* Hostnamen ermitteln */
-       strlcpy(My_Connections[new_sock].host, ip_str, sizeof(My_Connections[new_sock].host));
+       /* Set initial hostname to IP address. This becomes overwritten when
+        * the DNS lookup is enabled and succeeds, but is used otherwise. */
+       if (ng_ipaddr_af(&new_addr) != AF_INET)
+               snprintf(My_Connections[new_sock].host,
+                        sizeof(My_Connections[new_sock].host), "[%s]", ip_str);
+       else
+               strlcpy(My_Connections[new_sock].host, ip_str,
+                       sizeof(My_Connections[new_sock].host));
 
        Client_SetHostname(c, My_Connections[new_sock].host);
 
+       Log(LOG_INFO, "Accepted connection %d from %s:%d on socket %d.",
+           new_sock, My_Connections[new_sock].host,
+           ng_ipaddr_getport(&new_addr), Sock);
+
        identsock = new_sock;
 #ifdef IDENTAUTH
        if (Conf_NoIdent)
@@ -1286,13 +1300,11 @@ New_Connection( int Sock )
 static CONN_ID
 Socket2Index( int Sock )
 {
-       /* zum Socket passende Connection suchen */
-
        assert( Sock >= 0 );
 
        if( Sock >= Pool_Size || My_Connections[Sock].sock != Sock ) {
-               /* die Connection wurde vermutlich (wegen eines
-                * Fehlers) bereits wieder abgebaut ... */
+               /* the Connection was already closed again, likely due to
+                * an error. */
                LogDebug("Socket2Index: can't get connection for socket %d!", Sock);
                return NONE;
        }
@@ -1308,7 +1320,9 @@ static void
 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 );
@@ -1385,21 +1399,34 @@ Read_Request( CONN_ID Idx )
        if (c && (Client_Type(c) == CLIENT_USER
                  || Client_Type(c) == CLIENT_SERVER
                  || Client_Type(c) == CLIENT_SERVICE)) {
-               My_Connections[Idx].lastdata = time(NULL);
+               t = time(NULL);
+               if (My_Connections[Idx].lastdata != t)
+                       My_Connections[Idx].bps = 0;
+
+               My_Connections[Idx].lastdata = t;
                My_Connections[Idx].lastping = My_Connections[Idx].lastdata;
        }
 
        /* Look at the data in the (read-) buffer of this connection */
-       Handle_Buffer(Idx);
+       My_Connections[Idx].bps += Handle_Buffer(Idx);
+       if (Client_Type(c) != CLIENT_SERVER
+           && My_Connections[Idx].bps >= maxbps) {
+               LogDebug("Throttling connection %d: BPS exceeded! (%u >= %u)",
+                        Idx, My_Connections[Idx].bps, maxbps);
+               Conn_SetPenalty(Idx, 1);
+       }
 } /* Read_Request */
 
 
 /**
  * Handle all data in the connection read-buffer.
- * All data is precessed until no complete command is left. When a fatal
- * error occurs, the connection is shut down.
+ * Data is processed until no complete command is left in the read buffer,
+ * or MAX_COMMANDS[_SERVER] commands were processed.
+ * When a fatal error occurs, the connection is shut down.
+ * @param Idx Index of the connection.
+ * @return number of bytes processed.
  */
-static void
+static unsigned int
 Handle_Buffer(CONN_ID Idx)
 {
 #ifndef STRICT_RFC
@@ -1411,31 +1438,41 @@ Handle_Buffer(CONN_ID Idx)
 #ifdef ZLIB
        bool old_z;
 #endif
+       unsigned int i, maxcmd = MAX_COMMANDS, len_processed = 0;
+       CLIENT *c;
+
+       c = Conn_GetClient(Idx);
+       assert( c != NULL);
+
+       /* Servers do get special command limits, so they can process
+        * all the messages that are required while peering. */
+       if (Client_Type(c) == CLIENT_SERVER)
+               maxcmd = MAX_COMMANDS_SERVER;
 
        starttime = time(NULL);
-       for (;;) {
+       for (i=0; i < maxcmd; i++) {
                /* Check penalty */
                if (My_Connections[Idx].delaytime > starttime)
-                       return;
+                       return 0;
 #ifdef ZLIB
                /* Unpack compressed data, if compression is in use */
                if (Conn_OPTION_ISSET(&My_Connections[Idx], CONN_ZIP)) {
                        /* When unzipping fails, Unzip_Buffer() shuts
                         * down the connection itself */
                        if (!Unzip_Buffer(Idx))
-                               return;
+                               return 0;
                }
 #endif
 
                if (0 == array_bytes(&My_Connections[Idx].rbuf))
-                       return;
+                       break;
 
                /* Make sure that the buffer is NULL terminated */
                if (!array_cat0_temporary(&My_Connections[Idx].rbuf)) {
                        Conn_Close(Idx, NULL,
                                   "Can't allocate memory [Handle_Buffer]",
                                   true);
-                       return;
+                       return 0;
                }
 
                /* RFC 2812, section "2.3 Messages", 5th paragraph:
@@ -1471,7 +1508,7 @@ Handle_Buffer(CONN_ID Idx)
 #endif
 
                if (!ptr)
-                       return;
+                       break;
 
                /* Complete (=line terminated) request found, handle it! */
                *ptr = '\0';
@@ -1486,16 +1523,16 @@ Handle_Buffer(CONN_ID Idx)
                            Idx, array_bytes(&My_Connections[Idx].rbuf),
                            COMMAND_LEN - 1);
                        Conn_Close(Idx, NULL, "Request too long", true);
-                       return;
+                       return 0;
                }
 
+               len_processed += len;
                if (len <= delta) {
                        /* Request is empty (only '\r\n', '\r' or '\n');
                         * delta is 2 ('\r\n') or 1 ('\r' or '\n'), see above */
                        array_moveleft(&My_Connections[Idx].rbuf, 1, len);
-                       return;
+                       continue;
                }
-
 #ifdef ZLIB
                /* remember if stream is already compressed */
                old_z = My_Connections[Idx].options & CONN_ZIP;
@@ -1504,7 +1541,7 @@ Handle_Buffer(CONN_ID Idx)
                My_Connections[Idx].msg_in++;
                if (!Parse_Request
                    (Idx, (char *)array_start(&My_Connections[Idx].rbuf)))
-                       return;
+                       return 0; /* error -> connection has been closed */
 
                array_moveleft(&My_Connections[Idx].rbuf, 1, len);
                LogDebug("Connection %d: %d bytes left in read buffer.",
@@ -1521,7 +1558,7 @@ Handle_Buffer(CONN_ID Idx)
                                Conn_Close(Idx, NULL,
                                           "Can't allocate memory [Handle_Buffer]",
                                           true);
-                               return;
+                               return 0;
                        }
 
                        array_trunc(&My_Connections[Idx].rbuf);
@@ -1531,6 +1568,7 @@ Handle_Buffer(CONN_ID Idx)
                }
 #endif
        }
+       return len_processed;
 } /* Handle_Buffer */
 
 
@@ -1541,6 +1579,7 @@ Check_Connections(void)
         * if this doesn't help either, disconnect client. */
        CLIENT *c;
        CONN_ID i;
+       char msg[64];
 
        for (i = 0; i < Pool_Size; i++) {
                if (My_Connections[i].sock < 0)
@@ -1560,8 +1599,8 @@ Check_Connections(void)
                                        LogDebug
                                            ("Connection %d: Ping timeout: %d seconds.",
                                             i, Conf_PongTimeout);
-                                       Conn_Close(i, NULL, "Ping timeout",
-                                                  true);
+                                       snprintf(msg, sizeof(msg), "Ping timeout: %d seconds", Conf_PongTimeout);
+                                       Conn_Close(i, NULL, msg, true);
                                }
                        } else if (My_Connections[i].lastdata <
                                   time(NULL) - Conf_PingTimeout) {
@@ -1696,6 +1735,8 @@ New_Server( int Server , ng_ipaddr_t *dest)
                return;
        }
 
+       /* Conn_Close() decrements this counter again */
+       NumConnections++;
        Client_SetIntroducer( c, c );
        Client_SetToken( c, TOKEN_OUTBOUND );
 
@@ -1722,9 +1763,9 @@ New_Server( int Server , ng_ipaddr_t *dest)
                Conn_Close( new_sock, "Could not initialize SSL for outgoing connection", NULL, false );
                Init_Conn_Struct( new_sock );
                Conf_Server[Server].conn_id = NONE;
+               return;
        }
 #endif
-       NumConnections++;
        LogDebug("Registered new connection %d on socket %d (%ld in total).",
                 new_sock, My_Connections[new_sock].sock, NumConnections);
        Conn_OPTION_ADD( &My_Connections[new_sock], CONN_ISCONNECTING );
@@ -1825,8 +1866,8 @@ cb_Connect_to_Server(int fd, UNUSED short events)
                len -= sizeof(ng_ipaddr_t);
                if (len > sizeof(&Conf_Server[i].dst_addr)) {
                        len = sizeof(&Conf_Server[i].dst_addr);
-                       Log(LOG_NOTICE, "Notice: Resolver returned more IP Addresses for host than we can handle,"
-                                       " additional addresses dropped");
+                       Log(LOG_NOTICE,
+                               "Notice: Resolver returned more IP Addresses for host than we can handle, additional addresses dropped.");
                }
                memcpy(&Conf_Server[i].dst_addr, &dest_addrs[1], len);
        }
@@ -1951,6 +1992,9 @@ Conn_GetClient( CONN_ID Idx )
 GLOBAL bool
 Conn_GetCipherInfo(CONN_ID Idx, char *buf, size_t len)
 {
+       if (Idx < 0)
+               return false;
+       assert(Idx < (int) array_length(&My_ConnArray, sizeof(CONNECTION)));
        return ConnSSL_GetCipherInfo(&My_Connections[Idx], buf, len);
 }
 
@@ -1958,6 +2002,9 @@ Conn_GetCipherInfo(CONN_ID Idx, char *buf, size_t len)
 GLOBAL bool
 Conn_UsesSSL(CONN_ID Idx)
 {
+       if (Idx < 0)
+               return false;
+       assert(Idx < (int) array_length(&My_ConnArray, sizeof(CONNECTION)));
        return Conn_OPTION_ISSET(&My_Connections[Idx], CONN_SSL);
 }
 #endif