* 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")
*/
time_t lastdata; /* Letzte Aktivitaet */
time_t lastping; /* Letzter PING */
time_t lastprivmsg; /* Letzte PRIVMSG */
+ time_t delaytime; /* Nicht beachten bis ("penalty") */
} CONNECTION;
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 );
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,
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++ )
{
}
/* von welchen Sockets koennte gelesen werden? */
+ t = time( NULL );
read_sockets = My_Sockets;
for( i = 0; i < MAX_CONNECTIONS; i++ )
{
/* 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++ )
{
}
}
+ /* 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 );
}
/* 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? */
va_list ap;
assert( Idx >= 0 );
- assert( My_Connections[Idx].sock > NONE );
assert( Format != NULL );
#ifdef PROTOTYPES
* 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 )
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 );
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 */
} /* 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 )
{
/* 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;
}
/* Ein Client Socket: entweder ein User oder Server */
idx = Socket2Index( Sock );
- Read_Request( idx );
+ if( idx > NONE ) Read_Request( idx );
}
} /* Handle_Read */
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 ))
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 );
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;
}
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 )
{
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 */
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 */
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;
}
{
/* 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.
/* 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;
}
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 */
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 */
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 */
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;