]> arthur.barton.de Git - ngircd-alex.git/blobdiff - src/ngircd/conn.c
- Copyright-Texte aktualisiert.
[ngircd-alex.git] / src / ngircd / conn.c
index bbd43abbd077bdef075848fd338c3081e19e4392..95e878853e6afc0a8ef437874d7981daabd8da1e 100644 (file)
@@ -7,13 +7,34 @@
  * herausgegeben, weitergeben und/oder modifizieren, entweder unter Version 2
  * der Lizenz oder (wenn Sie es wuenschen) jeder spaeteren Version.
  * Naehere Informationen entnehmen Sie bitter der Datei COPYING. Eine Liste
- * der an comBase beteiligten Autoren finden Sie in der Datei AUTHORS.
+ * der an ngIRCd beteiligten Autoren finden Sie in der Datei AUTHORS.
  *
- * $Id: conn.c,v 1.18 2001/12/29 20:17:25 alex Exp $
+ * $Id: conn.c,v 1.24 2002/01/01 18:25:44 alex Exp $
  *
  * connect.h: Verwaltung aller Netz-Verbindungen ("connections")
  *
  * $Log: conn.c,v $
+ * Revision 1.24  2002/01/01 18:25:44  alex
+ * - #include's fuer stdlib.h ergaenzt.
+ *
+ * Revision 1.23  2001/12/31 02:18:51  alex
+ * - viele neue Befehle (WHOIS, ISON, OPER, DIE, RESTART),
+ * - neuen Header "defines.h" mit (fast) allen Konstanten.
+ * - Code Cleanups und viele "kleine" Aenderungen & Bugfixes.
+ *
+ * Revision 1.22  2001/12/30 19:26:11  alex
+ * - Unterstuetzung fuer die Konfigurationsdatei eingebaut.
+ *
+ * Revision 1.21  2001/12/29 22:33:36  alex
+ * - bessere Dokumentation des Modules bzw. der Funktionen.
+ *
+ * Revision 1.20  2001/12/29 22:09:43  alex
+ * - kleinere Aenderungen ("clean-ups") bei Logging (Resolver).
+ *
+ * Revision 1.19  2001/12/29 21:53:57  alex
+ * - Da hatte ich mich wohl ein wenig verrannt; jetzt sollte der Resolver
+ *   aber tatsaechlich funktionieren.
+ *
  * Revision 1.18  2001/12/29 20:17:25  alex
  * - asyncronen Resolver (IP->Name) implementiert, dadurch div. Aenderungen.
  *
 #include <assert.h>
 #include <stdarg.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <unistd.h>
 #include <errno.h>
 #include <fcntl.h>
 #include "conn.h"
 
 
-#define MAX_CONNECTIONS 100            /* max. Anzahl von Verbindungen an diesem Server */
-
-#define MAX_CMDLEN 512                 /* max. Laenge eines Befehls, vgl. RFC 2812, 3.2 */
-
-#define READBUFFER_LEN 2 * MAX_CMDLEN  /* Laenge des Lesepuffers je Verbindung (Bytes) */
-#define WRITEBUFFER_LEN 4096           /* Laenge des Schreibpuffers je Verbindung (Bytes) */
-
-#define HOST_LEN 256                   /* max. Laenge eines Hostnamen */
-
-
 typedef struct _Res_Stat
 {
        INT pid;                        /* PID des Child-Prozess */
-       INT out_pipe[2];                /* Pipe fuer IPC: zum Client */
-       INT in_pipe[2];                 /* Pipe fuer IPC: vom Client */
+       INT pipe[2];                    /* Pipe fuer IPC */
 } RES_STAT;
 
 
@@ -137,12 +148,13 @@ typedef struct _Connection
        struct sockaddr_in addr;        /* Adresse des Client */
        RES_STAT *res_stat;             /* "Resolver-Status", s.o. */
        CHAR host[HOST_LEN];            /* Hostname */
-       CHAR rbuf[READBUFFER_LEN + 1];  /* Lesepuffer */
+       CHAR rbuf[READBUFFER_LEN];      /* Lesepuffer */
        INT rdatalen;                   /* Laenge der Daten im Lesepuffer */
-       CHAR wbuf[WRITEBUFFER_LEN + 1]; /* Schreibpuffer */
+       CHAR wbuf[WRITEBUFFER_LEN];     /* Schreibpuffer */
        INT wdatalen;                   /* Laenge der Daten im Schreibpuffer */
        time_t lastdata;                /* Letzte Aktivitaet */
        time_t lastping;                /* Letzter PING */
+       time_t lastprivmsg;             /* Letzte PRIVMSG */
 } CONNECTION;
 
 
@@ -156,9 +168,9 @@ LOCAL VOID Handle_Buffer( CONN_ID Idx );
 LOCAL VOID Check_Connections( VOID );
 LOCAL VOID Init_Conn_Struct( INT Idx );
 
-LOCAL RES_STAT *Resolve( CHAR *Host );
-LOCAL VOID Do_Resolve( INT r_fd, INT w_fd );
+LOCAL RES_STAT *Resolve( struct sockaddr_in *Addr );
 LOCAL VOID Read_Resolver_Result( INT r_fd );
+LOCAL VOID Do_Resolve( struct sockaddr_in *Addr, INT w_fd );
 
 
 LOCAL fd_set My_Listeners;
@@ -172,6 +184,8 @@ LOCAL CONNECTION My_Connections[MAX_CONNECTIONS];
 
 GLOBAL VOID Conn_Init( VOID )
 {
+       /* Modul initialisieren: statische Strukturen "ausnullen". */
+
        CONN_ID i;
 
        /* zu Beginn haben wir keine Verbindungen */
@@ -188,6 +202,9 @@ GLOBAL VOID Conn_Init( VOID )
 
 GLOBAL VOID Conn_Exit( VOID )
 {
+       /* Modul abmelden: alle noch offenen Connections
+        * schliessen und freigeben. */
+
        CONN_ID idx;
        INT i;
 
@@ -218,8 +235,9 @@ GLOBAL VOID Conn_Exit( VOID )
 
 GLOBAL BOOLEAN Conn_NewListener( CONST INT Port )
 {
-       /* Neuen Listen-Socket erzeugen: der Server wartet dann
-        * auf dem angegebenen Port auf Verbindungen. */
+       /* Neuen Listen-Socket erzeugen: der Server wartet dann auf
+        * dem angegebenen Port auf Verbindungen. Kann der Listen-
+        * Socket nicht erteugt werden, so wird NULL geliefert.*/
 
        struct sockaddr_in addr;
        INT sock, on = 1;
@@ -273,7 +291,7 @@ GLOBAL BOOLEAN Conn_NewListener( CONST INT Port )
 
        if( sock > My_Max_Fd ) My_Max_Fd = sock;
 
-       Log( LOG_INFO, "Now listening on port %d, socket %d.", Port, sock );
+       Log( LOG_INFO, "Now listening on port %d (socket %d).", Port, sock );
 
        return TRUE;
 } /* Conn_NewListener */
@@ -281,6 +299,16 @@ GLOBAL BOOLEAN Conn_NewListener( CONST INT Port )
 
 GLOBAL VOID Conn_Handler( INT Timeout )
 {
+       /* Aktive Verbindungen ueberwachen. Mindestens alle "Timeout"
+        * Sekunden wird die Funktion verlassen. Folgende Aktionen
+        * werden durchgefuehrt:
+        *  - neue Verbindungen annehmen,
+        *  - geschlossene Verbindungen loeschen,
+        *  - volle Schreibpuffer versuchen zu schreiben,
+        *  - volle Lesepuffer versuchen zu verarbeiten,
+        *  - Antworten von Resolver Sub-Prozessen annehmen.
+        */
+
        fd_set read_sockets, write_sockets;
        struct timeval tv;
        time_t start;
@@ -363,12 +391,12 @@ GLOBAL BOOLEAN Conn_WriteStr( CONN_ID Idx, CHAR *Format, ... )
         * automatisch angehaengt. Im Fehlerfall wird dir Verbindung
         * getrennt und FALSE geliefert. */
 
-       CHAR buffer[MAX_CMDLEN];
+       CHAR buffer[COMMAND_LEN];
        BOOLEAN ok;
        va_list ap;
 
        va_start( ap, Format );
-       if( vsnprintf( buffer, MAX_CMDLEN - 2, Format, ap ) == MAX_CMDLEN - 2 )
+       if( vsnprintf( buffer, COMMAND_LEN - 2, Format, ap ) == COMMAND_LEN - 2 )
        {
                Log( LOG_ALERT, "String too long to send (connection %d)!", Idx );
                Conn_Close( Idx, "Server error: String too long to send!" );
@@ -429,7 +457,8 @@ GLOBAL BOOLEAN Conn_Write( CONN_ID Idx, CHAR *Data, INT Len )
 
 GLOBAL VOID Conn_Close( CONN_ID Idx, CHAR *Msg )
 {
-       /* Verbindung schliessen */
+       /* Verbindung schliessen. Evtl. noch von Resolver
+        * Sub-Prozessen offene Pipes werden geschlossen. */
 
        assert( Idx >= 0 );
        assert( My_Connections[Idx].sock >= 0 );
@@ -451,11 +480,9 @@ GLOBAL VOID Conn_Close( CONN_ID Idx, CHAR *Msg )
        if( My_Connections[Idx].res_stat )
        {
                /* Resolver-Strukturen freigeben, wenn noch nicht geschehen */
-               FD_CLR( My_Connections[Idx].res_stat->in_pipe[0], &My_Resolvers );
-               close( My_Connections[Idx].res_stat->out_pipe[0] );
-               close( My_Connections[Idx].res_stat->out_pipe[1] );
-               close( My_Connections[Idx].res_stat->in_pipe[0] );
-               close( My_Connections[Idx].res_stat->in_pipe[1] );
+               FD_CLR( My_Connections[Idx].res_stat->pipe[0], &My_Resolvers );
+               close( My_Connections[Idx].res_stat->pipe[0] );
+               close( My_Connections[Idx].res_stat->pipe[1] );
                free( My_Connections[Idx].res_stat );
        }
        
@@ -464,6 +491,24 @@ GLOBAL VOID Conn_Close( CONN_ID Idx, CHAR *Msg )
 } /* Conn_Close */
 
 
+GLOBAL VOID Conn_UpdateIdle( CONN_ID Idx )
+{
+       /* Idle-Timer zuruecksetzen */
+
+       assert( Idx >= 0 );
+       My_Connections[Idx].lastprivmsg = time( NULL );
+}
+
+
+GLOBAL INT32 Conn_GetIdle( CONN_ID Idx )
+{
+       /* Idle-Time einer Verbindung liefern (in Sekunden) */
+
+       assert( Idx >= 0 );
+       return time( NULL ) - My_Connections[Idx].lastprivmsg;
+} /* Conn_GetIdle */
+
+
 LOCAL BOOLEAN Try_Write( CONN_ID Idx )
 {
        /* Versuchen, Daten aus dem Schreib-Puffer in den
@@ -495,7 +540,10 @@ LOCAL BOOLEAN Try_Write( CONN_ID Idx )
 
 LOCAL VOID Handle_Read( INT Sock )
 {
-       /* Aktivitaet auf einem Socket verarbeiten */
+       /* Aktivitaet auf einem Socket verarbeiten:
+        *  - neue Clients annehmen,
+        *  - Daten von Clients verarbeiten,
+        *  - Resolver-Rueckmeldungen annehmen. */
 
        CONN_ID idx;
 
@@ -601,7 +649,7 @@ LOCAL VOID New_Connection( INT Sock )
        Log( LOG_NOTICE, "Accepted connection %d from %s:%d on socket %d.", idx, inet_ntoa( new_addr.sin_addr ), ntohs( new_addr.sin_port), Sock );
 
        /* Hostnamen ermitteln */
-       s = Resolve( inet_ntoa( new_addr.sin_addr ));
+       s = Resolve( &new_addr );
        if( s )
        {
                /* Sub-Prozess wurde asyncron gestartet */
@@ -640,8 +688,8 @@ LOCAL VOID Read_Request( CONN_ID Idx )
        assert( Idx >= 0 );
        assert( My_Connections[Idx].sock >= 0 );
 
-       len = recv( My_Connections[Idx].sock, My_Connections[Idx].rbuf + My_Connections[Idx].rdatalen, READBUFFER_LEN - My_Connections[Idx].rdatalen, 0 );
-       My_Connections[Idx].rbuf[READBUFFER_LEN] = '\0';
+       len = recv( My_Connections[Idx].sock, My_Connections[Idx].rbuf + My_Connections[Idx].rdatalen, READBUFFER_LEN - My_Connections[Idx].rdatalen - 1, 0 );
+       My_Connections[Idx].rbuf[READBUFFER_LEN - 1] = '\0';
 
        if( len == 0 )
        {
@@ -664,7 +712,7 @@ LOCAL VOID Read_Request( CONN_ID Idx )
        assert( My_Connections[Idx].rdatalen <= READBUFFER_LEN );
        My_Connections[Idx].rbuf[My_Connections[Idx].rdatalen] = '\0';
 
-       if( My_Connections[Idx].rdatalen > MAX_CMDLEN )
+       if( My_Connections[Idx].rdatalen > COMMAND_LEN )
        {
                /* Eine Anfrage darf(!) nicht laenger als 512 Zeichen
                 * (incl. CR+LF!) werden; vgl. RFC 2812. Wenn soetwas
@@ -683,6 +731,8 @@ LOCAL VOID Read_Request( CONN_ID Idx )
 
 LOCAL VOID Handle_Buffer( CONN_ID Idx )
 {
+       /* Daten im Lese-Puffer einer Verbindung verarbeiten. */
+
        CHAR *ptr, *ptr1, *ptr2;
        INT len, delta;
        
@@ -723,7 +773,9 @@ LOCAL VOID Handle_Buffer( CONN_ID Idx )
 
 LOCAL VOID Check_Connections( VOID )
 {
-       /* Pruefen, ob Verbindungen noch "alive" sind */
+       /* Pruefen, ob Verbindungen noch "alive" sind. Ist dies
+        * nicht der Fall, zunaechst PING-PONG spielen und, wenn
+        * auch das nicht "hilft", Client disconnectieren. */
 
        INT i;
 
@@ -766,10 +818,11 @@ LOCAL VOID Init_Conn_Struct( INT Idx )
        My_Connections[Idx].wdatalen = 0;
        My_Connections[Idx].lastdata = time( NULL );
        My_Connections[Idx].lastping = 0;
+       My_Connections[Idx].lastprivmsg = time( NULL );
 } /* Init_Conn_Struct */
 
 
-LOCAL RES_STAT *Resolve( CHAR *Host )
+LOCAL RES_STAT *Resolve( struct sockaddr_in *Addr )
 {
        /* Hostnamen (asyncron!) aufloesen. Bei Fehler, z.B. wenn der
         * Child-Prozess nicht erzeugt werden kann, wird NULL geliefert.
@@ -778,6 +831,7 @@ LOCAL RES_STAT *Resolve( CHAR *Host )
        RES_STAT *s;
        INT pid;
 
+       /* Speicher anfordern */
        s = malloc( sizeof( RES_STAT ));
        if( ! s )
        {
@@ -785,44 +839,30 @@ LOCAL RES_STAT *Resolve( CHAR *Host )
                return NULL;
        }
 
-       if( pipe( s->out_pipe ) != 0 )
+       /* Pipe fuer Antwort initialisieren */
+       if( pipe( s->pipe ) != 0 )
        {
                free( s );
                Log( LOG_ALERT, "Resolver: Can't create output pipe: %s!", strerror( errno ));
                return NULL;
        }
 
-       if( pipe( s->in_pipe ) != 0 )
-       {
-               free( s );
-               Log( LOG_ALERT, "Resolver: Can't create input pipe: %s!", strerror( errno ));
-               return NULL;
-       }
-
+       /* Sub-Prozess erzeugen */
        pid = fork( );
        if( pid > 0 )
        {
                /* Haupt-Prozess */
-               if( write( s->out_pipe[1], Host, strlen( Host ) + 1 ) != ( strlen( Host ) + 1 ))
-               {
-                       free( s );
-                       Log( LOG_ALERT, "Resolver: Can't write to child: %s!", strerror( errno ));
-                       return NULL;
-               }
-
-               FD_SET( s->in_pipe[0], &My_Resolvers );
-               if( s->in_pipe[0] > My_Max_Fd ) My_Max_Fd = s->in_pipe[0];
-
+               Log( LOG_DEBUG, "Resolver for %s created (PID %d).", inet_ntoa( Addr->sin_addr ), pid );
+               FD_SET( s->pipe[0], &My_Resolvers );
+               if( s->pipe[0] > My_Max_Fd ) My_Max_Fd = s->pipe[0];
                s->pid = pid;
-
-               Log( LOG_DEBUG, "Resolver process for \"%s\" (PID %d) created.", Host, pid );
                return s;
        }
        else if( pid == 0 )
        {
                /* Sub-Prozess */
                Log_Init_Resolver( );
-               Do_Resolve( s->out_pipe[0], s->in_pipe[1] );
+               Do_Resolve( Addr, s->pipe[1] );
                Log_Exit_Resolver( );
                exit( 0 );
        }
@@ -856,10 +896,10 @@ LOCAL VOID Read_Resolver_Result( INT r_fd )
                return;
        }
 
+       /* zugehoerige Connection suchen */
        for( i = 0; i < MAX_CONNECTIONS; i++ )
        {
-               /* zugehoerige Connection suchen */
-               if(( My_Connections[i].sock >= 0 ) && ( My_Connections[i].res_stat ) && ( My_Connections[i].res_stat->in_pipe[0] == r_fd )) break;
+               if(( My_Connections[i].sock >= 0 ) && ( My_Connections[i].res_stat ) && ( My_Connections[i].res_stat->pipe[0] == r_fd )) break;
        }
 
        if( i >= MAX_CONNECTIONS )
@@ -871,61 +911,46 @@ LOCAL VOID Read_Resolver_Result( INT r_fd )
        }
 
        /* Aufraeumen */
-       close( My_Connections[i].res_stat->out_pipe[0] );
-       close( My_Connections[i].res_stat->out_pipe[1] );
-       close( My_Connections[i].res_stat->in_pipe[0] );
-       close( My_Connections[i].res_stat->in_pipe[1] );
+       close( My_Connections[i].res_stat->pipe[0] );
+       close( My_Connections[i].res_stat->pipe[1] );
        free( My_Connections[i].res_stat );
        My_Connections[i].res_stat = NULL;
        
        /* Hostnamen setzen */
        strcpy( My_Connections[i].host, hostname );
        c = Client_GetFromConn( i );
-       if( c )
-       {
-               Log( LOG_DEBUG, "Set hostname: \"%s\".", hostname );
-               Client_SetHostname( c, hostname );
-       }
+       if( c ) Client_SetHostname( c, hostname );
 } /* Read_Resolver_Result */
 
 
-LOCAL VOID Do_Resolve( INT r_fd, INT w_fd )
+LOCAL VOID Do_Resolve( struct sockaddr_in *Addr, INT w_fd )
 {
        /* Resolver Sub-Prozess: aufzuloesenden Namen aus
         * der Pipe lesen, Ergebnis in Pipe schreiben. */
 
-       CHAR host[HOST_LEN], res_host[HOST_LEN];
+       CHAR hostname[HOST_LEN];
        struct hostent *h;
 
-       /* Anfrage vom Parent lesen */
-       if( read( r_fd, host, HOST_LEN) < 0 )
-       {
-               Log_Resolver( LOG_ALERT, "Resolver: Can't read from parent: %s!", strerror( errno ));
-               close( r_fd ); close( w_fd );
-               return;
-       }
-       host[HOST_LEN] = '\0';
-
-       Log_Resolver( LOG_DEBUG, "Now resolving \"%s\" ...", host );
+       Log_Resolver( LOG_DEBUG, "Now resolving %s ...", inet_ntoa( Addr->sin_addr ));
 
        /* Namen aufloesen */
-       h = gethostbyname( host );
-       if( h ) strcpy( res_host, h->h_name );
+       h = gethostbyaddr( (CHAR *)&Addr->sin_addr, sizeof( Addr->sin_addr ), AF_INET );
+       if( h ) strcpy( hostname, h->h_name );
        else
        {
-               Log_Resolver( LOG_WARNING, "Can't resolce host name (code %d)!", h_errno );
-               strcpy( res_host, host );
+               Log_Resolver( LOG_WARNING, "Resolver: Can't resolve host name (code %d)!", h_errno );
+               strcpy( hostname, inet_ntoa( Addr->sin_addr ));
        }
 
        /* Antwort an Parent schreiben */
-       if( write( w_fd, res_host, strlen( res_host ) + 1 ) != ( strlen( res_host ) + 1 ))
+       if( write( w_fd, hostname, strlen( hostname ) + 1 ) != ( strlen( hostname ) + 1 ))
        {
                Log_Resolver( LOG_ALERT, "Resolver: Can't write to parent: %s!", strerror( errno ));
-               close( r_fd ); close( w_fd );
+               close( w_fd );
                return;
        }
 
-       Log_Resolver( LOG_DEBUG, "Ok, translated \"%s\" to \"%s\".", host, res_host );
+       Log_Resolver( LOG_DEBUG, "Ok, translated %s to \"%s\".", inet_ntoa( Addr->sin_addr ), hostname );
 } /* Do_Resolve */