]> arthur.barton.de Git - ngircd.git/blobdiff - src/ngircd/conn.c
- "back-ports" aus CVS-HEAD fuer Version 0.5.3
[ngircd.git] / src / ngircd / conn.c
index 6acfb083fbb13bbc3483aa713098c8fb9340e1e0..a300af4de1f3f8caa9e5ce9d3774ad8dc860fa5e 100644 (file)
@@ -9,7 +9,7 @@
  * Naehere Informationen entnehmen Sie bitter der Datei COPYING. Eine Liste
  * der an ngIRCd beteiligten Autoren finden Sie in der Datei AUTHORS.
  *
- * $Id: conn.c,v 1.66 2002/05/30 16:52:21 alex Exp $
+ * $Id: conn.c,v 1.72.2.4 2002/11/04 19:18:39 alex Exp $
  *
  * connect.h: Verwaltung aller Netz-Verbindungen ("connections")
  */
@@ -74,6 +74,7 @@ typedef struct _Connection
        time_t lastdata;                /* Letzte Aktivitaet */
        time_t lastping;                /* Letzter PING */
        time_t lastprivmsg;             /* Letzte PRIVMSG */
+       time_t delaytime;               /* Nicht beachten bis ("penalty") */
 } CONNECTION;
 
 
@@ -147,7 +148,11 @@ Conn_Exit( VOID )
                                close( i );
                                Log( LOG_DEBUG, "Connection %d closed during creation (socket %d).", idx, i );
                        }
-                       else if( idx < MAX_CONNECTIONS ) Conn_Close( idx, NULL, "Server going down", TRUE );
+                       else if( idx < MAX_CONNECTIONS )
+                       {
+                               if( NGIRCd_Restart ) Conn_Close( idx, NULL, "Server going down (restarting)", TRUE );
+                               else Conn_Close( idx, NULL, "Server going down", TRUE );
+                       }
                        else
                        {
                                Log( LOG_WARNING, "Closing unknown connection %d ...", i );
@@ -213,11 +218,12 @@ Conn_NewListener( CONST UINT Port )
 
 
 GLOBAL VOID
-Conn_Handler( INT Timeout )
+Conn_Handler( VOID )
 {
-       /* Aktive Verbindungen ueberwachen. Mindestens alle "Timeout"
-        * Sekunden wird die Funktion verlassen. Folgende Aktionen
-        * werden durchgefuehrt:
+       /* "Hauptschleife": Aktive Verbindungen ueberwachen. Folgende Aktionen
+        * werden dabei durchgefuehrt, bis der Server terminieren oder neu
+        * starten soll:
+        *
         *  - neue Verbindungen annehmen,
         *  - Server-Verbindungen aufbauen,
         *  - geschlossene Verbindungen loeschen,
@@ -228,20 +234,16 @@ Conn_Handler( INT Timeout )
 
        fd_set read_sockets, write_sockets;
        struct timeval tv;
-       time_t start;
-       INT i;
+       time_t start, t;
+       INT i, idx;
 
        start = time( NULL );
-       while(( time( NULL ) - start < Timeout ) && ( ! NGIRCd_Quit ))
+       while(( ! NGIRCd_Quit ) && ( ! NGIRCd_Restart ))
        {
                Check_Servers( );
 
                Check_Connections( );
 
-               /* Timeout initialisieren */
-               tv.tv_sec = 0;
-               tv.tv_usec = 50000;
-
                /* noch volle Lese-Buffer suchen */
                for( i = 0; i < MAX_CONNECTIONS; i++ )
                {
@@ -269,6 +271,7 @@ Conn_Handler( INT Timeout )
                }
 
                /* von welchen Sockets koennte gelesen werden? */
+               t = time( NULL );
                read_sockets = My_Sockets;
                for( i = 0; i < MAX_CONNECTIONS; i++ )
                {
@@ -282,6 +285,12 @@ Conn_Handler( INT Timeout )
                                /* Hier laeuft noch ein asyncrones connect() */
                                FD_CLR( My_Connections[i].sock, &read_sockets );
                        }
+                       if( My_Connections[i].delaytime > t )
+                       {
+                               /* Fuer die Verbindung ist eine "Penalty-Zeit" gesetzt */
+                               FD_CLR( My_Connections[i].sock, &read_sockets );
+                               FD_CLR( My_Connections[i].sock, &write_sockets );
+                       }
                }
                for( i = 0; i < Conn_MaxFD + 1; i++ )
                {
@@ -292,12 +301,23 @@ Conn_Handler( INT Timeout )
                        }
                }
 
+               /* Timeout initialisieren */
+               tv.tv_sec = 1;
+               tv.tv_usec = 0;
+               
                /* Auf Aktivitaet warten */
-               if( select( Conn_MaxFD + 1, &read_sockets, &write_sockets, NULL, &tv ) == -1 )
+               i = select( Conn_MaxFD + 1, &read_sockets, &write_sockets, NULL, &tv );
+               if( i == 0 )
+               {
+                       /* keine Veraenderung an den Sockets */
+                       continue;
+               }
+               if( i == -1 )
                {
+                       /* Fehler (z.B. Interrupt) */
                        if( errno != EINTR )
                        {
-                               Log( LOG_EMERG, "select(): %s!", strerror( errno ));
+                               Log( LOG_EMERG, "Conn_Handler(): select(): %s!", strerror( errno ));
                                Log( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE );
                                exit( 1 );
                        }
@@ -307,7 +327,18 @@ Conn_Handler( INT Timeout )
                /* Koennen Daten geschrieben werden? */
                for( i = 0; i < Conn_MaxFD + 1; i++ )
                {
-                       if( FD_ISSET( i, &write_sockets )) Handle_Write( Socket2Index( i ));
+                       if( ! FD_ISSET( i, &write_sockets )) continue;
+
+                       /* Es kann geschrieben werden ... */
+                       idx = Socket2Index( i );
+                       if( idx == NONE ) continue;
+                       
+                       if( ! Handle_Write( idx ))
+                       {
+                               /* Fehler beim Schreiben! Diesen Socket nun
+                                * auch aus dem Read-Set entfernen: */
+                               FD_CLR( i, &read_sockets );
+                       }
                }
 
                /* Daten zum Lesen vorhanden? */
@@ -339,7 +370,6 @@ va_dcl
        va_list ap;
 
        assert( Idx >= 0 );
-       assert( My_Connections[Idx].sock > NONE );
        assert( Format != NULL );
 
 #ifdef PROTOTYPES
@@ -373,10 +403,20 @@ Conn_Write( CONN_ID Idx, CHAR *Data, INT Len )
         * der Client disconnectiert und FALSE geliefert. */
 
        assert( Idx >= 0 );
-       assert( My_Connections[Idx].sock > NONE );
        assert( Data != NULL );
        assert( Len > 0 );
 
+       /* Ist der entsprechende Socket ueberhaupt noch offen?
+        * In einem "Handler-Durchlauf" kann es passieren, dass
+        * dem nicht mehr so ist, wenn einer von mehreren
+        * Conn_Write()'s fehlgeschlagen ist. In diesem Fall
+        * wird hier einfach ein Fehler geliefert. */
+       if( My_Connections[Idx].sock <= NONE )
+       {
+               Log( LOG_DEBUG, "Skipped write on closed socket (connection %d).", Idx );
+               return FALSE;
+       }
+
        /* pruefen, ob Daten im Schreibpuffer sind. Wenn ja, zunaechst
         * pruefen, ob diese gesendet werden koennen */
        if( My_Connections[Idx].wdatalen > 0 )
@@ -427,12 +467,17 @@ Conn_Close( CONN_ID Idx, CHAR *LogMsg, CHAR *FwdMsg, BOOLEAN InformClient )
 
        if( close( My_Connections[Idx].sock ) != 0 )
        {
-               Log( LOG_ERR, "Error closing connection %d with %s:%d - %s!", Idx, inet_ntoa( My_Connections[Idx].addr.sin_addr ), ntohs( My_Connections[Idx].addr.sin_port), strerror( errno ));
+               Log( LOG_ERR, "Error closing connection %d (socket %d) with %s:%d - %s!", Idx, My_Connections[Idx].sock, inet_ntoa( My_Connections[Idx].addr.sin_addr ), ntohs( My_Connections[Idx].addr.sin_port), strerror( errno ));
        }
        else
        {
-               Log( LOG_INFO, "Connection %d with %s:%d closed.", Idx, inet_ntoa( My_Connections[Idx].addr.sin_addr ), ntohs( My_Connections[Idx].addr.sin_port ));
+               Log( LOG_INFO, "Connection %d (socket %d) with %s:%d closed.", Idx, My_Connections[Idx].sock, inet_ntoa( My_Connections[Idx].addr.sin_addr ), ntohs( My_Connections[Idx].addr.sin_port ));
        }
+       
+       /* Socket als "ungueltig" markieren */
+       FD_CLR( My_Connections[Idx].sock, &My_Sockets );
+       FD_CLR( My_Connections[Idx].sock, &My_Connects );
+       My_Connections[Idx].sock = NONE;
 
        c = Client_GetFromConn( Idx );
        if( c ) Client_Destroy( c, LogMsg, FwdMsg, TRUE );
@@ -446,18 +491,18 @@ Conn_Close( CONN_ID Idx, CHAR *LogMsg, CHAR *FwdMsg, BOOLEAN InformClient )
                free( My_Connections[Idx].res_stat );
        }
 
-       /* Bei Server-Verbindungen lasttry-Zeitpunkt so setzen, dass
-        * der naechste Verbindungsversuch in RECONNECT_DELAY Sekunden
-        * gestartet wird. */
-       if(( My_Connections[Idx].our_server >= 0 ) && ( Conf_Server[My_Connections[Idx].our_server].lasttry <  time( NULL )))
+       /* Startzeit des naechsten Connect-Versuchs modifizieren? */
+       if(( My_Connections[Idx].our_server >= 0 ) && ( Conf_Server[My_Connections[Idx].our_server].lasttry <  time( NULL ) - Conf_ConnectRetry ))
        {
-               /* Okay, die Verbindung stand schon "genuegend lange" */
+               /* Okay, die Verbindung stand schon "genuegend lange":
+                * lasttry-Zeitpunkt so setzen, dass der naechste
+                * Verbindungsversuch in RECONNECT_DELAY Sekunden
+                * gestartet wird. */
                Conf_Server[My_Connections[Idx].our_server].lasttry = time( NULL ) - Conf_ConnectRetry + RECONNECT_DELAY;
        }
 
-       FD_CLR( My_Connections[Idx].sock, &My_Sockets );
-       FD_CLR( My_Connections[Idx].sock, &My_Connects );
-       My_Connections[Idx].sock = NONE;
+       /* Connection-Struktur loeschen (=freigeben) */
+       Init_Conn_Struct( Idx );
 } /* Conn_Close */
 
 
@@ -491,6 +536,24 @@ Conn_LastPing( CONN_ID Idx )
 } /* Conn_LastPing */
 
 
+GLOBAL VOID
+Conn_SetPenalty( CONN_ID Idx, time_t Seconds )
+{
+       /* Penalty-Delay fuer eine Verbindung (in Sekunden) setzen;
+        * waehrend dieser Zeit wird der entsprechende Socket vom Server
+        * bei Lese-Operationen komplett ignoriert. Der Delay kann mit
+        * dieser Funktion nur erhoeht, nicht aber verringert werden. */
+       
+       time_t t;
+       
+       assert( Idx >= 0 );
+       assert( Seconds >= 0 );
+       
+       t = time( NULL ) + Seconds;
+       if( t > My_Connections[Idx].delaytime ) My_Connections[Idx].delaytime = t;
+} /* Conn_SetPenalty */
+
+
 LOCAL BOOLEAN
 Try_Write( CONN_ID Idx )
 {
@@ -510,7 +573,7 @@ Try_Write( CONN_ID Idx )
                /* Fehler! */
                if( errno != EINTR )
                {
-                       Log( LOG_ALERT, "select() failed: %s!", strerror( errno ));
+                       Log( LOG_ALERT, "Try_Write(): select() failed: %s (con=%d, sock=%d)!", strerror( errno ), Idx, My_Connections[Idx].sock );
                        Conn_Close( Idx, "Server error!", NULL, FALSE );
                        return FALSE;
                }
@@ -551,7 +614,7 @@ Handle_Read( INT Sock )
                /* Ein Client Socket: entweder ein User oder Server */
 
                idx = Socket2Index( Sock );
-               Read_Request( idx );
+               if( idx > NONE ) Read_Request( idx );
        }
 } /* Handle_Read */
 
@@ -563,7 +626,7 @@ Handle_Write( CONN_ID Idx )
 
        INT len, res, err;
 
-       assert( Idx >= 0 );
+       assert( Idx > NONE );
        assert( My_Connections[Idx].sock > NONE );
 
        if( FD_ISSET( My_Connections[Idx].sock, &My_Connects ))
@@ -598,10 +661,8 @@ Handle_Write( CONN_ID Idx )
                Log( LOG_DEBUG, "Connection %d with \"%s:%d\" established, now sendig PASS and SERVER ...", Idx, My_Connections[Idx].host, Conf_Server[My_Connections[Idx].our_server].port );
 
                /* PASS und SERVER verschicken */
-               Conn_WriteStr( Idx, "PASS %s %s", Conf_Server[My_Connections[Idx].our_server].pwd, PASSSERVERADD );
-               Conn_WriteStr( Idx, "SERVER %s :%s", Conf_ServerName, Conf_ServerInfo );
-
-               return TRUE;
+               Conn_WriteStr( Idx, "PASS %s %s", Conf_Server[My_Connections[Idx].our_server].pwd, NGIRCd_ProtoID );
+               return Conn_WriteStr( Idx, "SERVER %s :%s", Conf_ServerName, Conf_ServerInfo );
        }
 
        assert( My_Connections[Idx].wdatalen > 0 );
@@ -611,8 +672,8 @@ Handle_Write( CONN_ID Idx )
        if( len < 0 )
        {
                /* Oops, ein Fehler! */
-               Log( LOG_ERR, "Write error (buffer) on connection %d: %s!", Idx, strerror( errno ));
-               Conn_Close( Idx, "Write error (buffer)!", NULL, FALSE );
+               Log( LOG_ERR, "Write error on connection %d (socket %d): %s!", Idx, My_Connections[Idx].sock, strerror( errno ));
+               Conn_Close( Idx, "Write error!", NULL, FALSE );
                return FALSE;
        }
 
@@ -646,7 +707,7 @@ New_Connection( INT Sock )
                return;
        }
 
-       /* Freie Connection-Struktur suschen */
+       /* Freie Connection-Struktur suchen */
        for( idx = 0; idx < MAX_CONNECTIONS; idx++ ) if( My_Connections[idx].sock == NONE ) break;
        if( idx >= MAX_CONNECTIONS )
        {
@@ -688,6 +749,9 @@ New_Connection( INT Sock )
                strcpy( My_Connections[idx].host, inet_ntoa( new_addr.sin_addr ));
                Client_SetHostname( c, My_Connections[idx].host );
        }
+       
+       /* Penalty-Zeit setzen */
+       Conn_SetPenalty( idx, 1 );
 } /* New_Connection */
 
 
@@ -702,8 +766,14 @@ Socket2Index( INT Sock )
 
        for( idx = 0; idx < MAX_CONNECTIONS; idx++ ) if( My_Connections[idx].sock == Sock ) break;
 
-       assert( idx < MAX_CONNECTIONS );
-       return idx;
+       if( idx >= MAX_CONNECTIONS )
+       {
+               /* die Connection wurde vermutlich (wegen eines
+                * Fehlers) bereits wieder abgebaut ... */
+               Log( LOG_DEBUG, "Socket2Index: can't get connection for socket %d!", Sock );
+               return NONE;
+       }
+       else return idx;
 } /* Socket2Index */
 
 
@@ -739,7 +809,7 @@ Read_Request( CONN_ID Idx )
        if( len < 0 )
        {
                /* Fehler beim Lesen */
-               Log( LOG_ERR, "Read error on connection %d: %s!", Idx, strerror( errno ));
+               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;
        }
@@ -761,7 +831,10 @@ Handle_Buffer( CONN_ID Idx )
 {
        /* Daten im Lese-Puffer einer Verbindung verarbeiten. */
 
-       CHAR *ptr, *ptr1, *ptr2;
+#ifndef STRICT_RFC
+       CHAR *ptr1, *ptr2;
+#endif
+       CHAR *ptr;
        INT len, delta;
 
        /* Eine komplette Anfrage muss mit CR+LF enden, vgl.
@@ -793,7 +866,7 @@ Handle_Buffer( CONN_ID Idx )
                        /* Eine Anfrage darf(!) nicht laenger als 512 Zeichen
                        * (incl. CR+LF!) werden; vgl. RFC 2812. Wenn soetwas
                        * empfangen wird, wird der Client disconnectiert. */
-                       Log( LOG_ERR, "Request too long (connection %d): %d bytes!", Idx, My_Connections[Idx].rdatalen );
+                       Log( LOG_ERR, "Request too long (connection %d): %d bytes (max. %d expected)!", Idx, My_Connections[Idx].rdatalen, COMMAND_LEN - 1 );
                        Conn_Close( Idx, NULL, "Request too long", TRUE );
                        return;
                }
@@ -1022,6 +1095,8 @@ New_Server( INT Server, CONN_ID Idx )
        FD_SET( new_sock, &My_Sockets );
        FD_SET( new_sock, &My_Connects );
        if( new_sock > Conn_MaxFD ) Conn_MaxFD = new_sock;
+       
+       Log( LOG_DEBUG, "Registered new connection %d on socket %d.", Idx, My_Connections[Idx].sock );
 } /* New_Server */
 
 
@@ -1041,6 +1116,7 @@ Init_Conn_Struct( INT Idx )
        My_Connections[Idx].lastdata = time( NULL );
        My_Connections[Idx].lastping = 0;
        My_Connections[Idx].lastprivmsg = time( NULL );
+       My_Connections[Idx].delaytime = 0;
 } /* Init_Conn_Struct */
 
 
@@ -1082,7 +1158,7 @@ Read_Resolver_Result( INT r_fd )
        FD_CLR( r_fd, &Resolver_FDs );
 
        /* Anfrage vom Parent lesen */
-       len = read( r_fd, result, HOST_LEN);
+       len = read( r_fd, result, HOST_LEN - 1 );
        if( len < 0 )
        {
                /* Fehler beim Lesen aus der Pipe */
@@ -1100,7 +1176,7 @@ Read_Resolver_Result( INT r_fd )
        if( i >= MAX_CONNECTIONS )
        {
                /* Opsa! Keine passende Connection gefunden!? Vermutlich
-               * wurde sie schon wieder geschlossen. */
+                * wurde sie schon wieder geschlossen. */
                close( r_fd );
                Log( LOG_DEBUG, "Resolver: Got result for unknown connection!?" );
                return;