/*
* ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2004 Alexander Barton <alex@barton.de>
+ * Copyright (c)2001-2005 Alexander Barton <alex@barton.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include "portab.h"
-static char UNUSED id[] = "$Id: conn.c,v 1.149 2005/04/23 14:28:44 fw Exp $";
+static char UNUSED id[] = "$Id: conn.c,v 1.156 2005/07/02 14:36:03 alex Exp $";
#include "imp.h"
#include <assert.h>
LOCAL void New_Connection PARAMS(( int Sock ));
LOCAL CONN_ID Socket2Index PARAMS(( int Sock ));
LOCAL void Read_Request PARAMS(( CONN_ID Idx ));
-LOCAL bool Try_Write PARAMS(( CONN_ID Idx ));
LOCAL bool Handle_Buffer PARAMS(( CONN_ID Idx ));
LOCAL void Check_Connections PARAMS(( void ));
LOCAL void Check_Servers PARAMS(( void ));
int deny_severity = LOG_ERR;
#endif
+LOCAL void
+FreeRes_stat( CONNECTION *c )
+{
+ assert( c != NULL );
+ assert( c->res_stat != NULL );
+
+ if (!c->res_stat) return;
+
+ FD_CLR( c->res_stat->pipe[0], &Resolver_FDs );
+
+ close( c->res_stat->pipe[0] );
+ close( c->res_stat->pipe[1] );
+
+ free( c->res_stat );
+ c->res_stat = NULL;
+}
+
GLOBAL void
Conn_Init( void )
for( i = 0; i < Pool_Size; i++ )
{
if ( My_Connections[i].sock > NONE ) {
- if ( My_Connections[i].host[0] == '\0' ) {
+ if ( My_Connections[i].res_stat ) {
/* wait for completion of Resolver Sub-Process */
FD_CLR( My_Connections[i].sock, &read_sockets );
continue;
} /* Conn_Handler */
+/**
+ * Write a text string into the socket of a connection.
+ * This function automatically appends CR+LF to the string and validates that
+ * the result is a valid IRC message (oversized messages are shortened, for
+ * example). Then it calls the Conn_Write() function to do the actual sending.
+ * @param Idx Index fo the connection.
+ * @param Format Format string, see printf().
+ * @return true on success, false otherwise.
+ */
#ifdef PROTOTYPES
GLOBAL bool
Conn_WriteStr( CONN_ID Idx, char *Format, ... )
va_dcl
#endif
{
- /* String in Socket schreiben. CR+LF wird von dieser Funktion
- * automatisch angehaengt. Im Fehlerfall wird dir Verbindung
- * getrennt und false geliefert. */
-
char buffer[COMMAND_LEN];
bool ok;
va_list ap;
#else
va_start( ap );
#endif
- if( vsnprintf( buffer, COMMAND_LEN - 2, Format, ap ) >= COMMAND_LEN - 2 )
- {
- Log( LOG_CRIT, "Text too long to send (connection %d)!", Idx );
- Conn_Close( Idx, "Text too long to send!", NULL, false );
- return false;
+
+ if (vsnprintf(buffer, COMMAND_LEN - 2, Format, ap) >= COMMAND_LEN - 2) {
+ /*
+ * The string that should be written to the socket is longer
+ * than the allowed size of COMMAND_LEN bytes (including both
+ * the CR and LF characters). This can be caused by the
+ * IRC_WriteXXX() functions when the prefix of this server had
+ * to be added to an already "quite long" command line which
+ * has been received from a regular IRC client, for example.
+ *
+ * We are not allowed to send such "oversized" messages to
+ * other servers and clients, see RFC 2812 2.3 and 2813 3.3
+ * ("these messages SHALL NOT exceed 512 characters in length,
+ * counting all characters including the trailing CR-LF").
+ *
+ * So we have a big problem here: we should send more bytes
+ * to the network than we are allowed to and we don't know
+ * the originator (any more). The "old" behaviour of blaming
+ * the receiver ("next hop") is a bad idea (it could be just
+ * an other server only routing the message!), so the only
+ * option left is to shorten the string and to hope that the
+ * result is still somewhat useful ...
+ * -alex-
+ */
+
+ strcpy (buffer + sizeof(buffer) - strlen(CUT_TXTSUFFIX) - 2 - 1,
+ CUT_TXTSUFFIX);
}
#ifdef SNIFFER
- if( NGIRCd_Sniffer ) Log( LOG_DEBUG, " -> connection %d: '%s'.", Idx, buffer );
+ if (NGIRCd_Sniffer)
+ Log(LOG_DEBUG, " -> connection %d: '%s'.", Idx, buffer);
#endif
strlcat( buffer, "\r\n", sizeof( buffer ));
{
/* Der Puffer ist dummerweise voll. Jetzt versuchen, den Puffer
* zu schreiben, wenn das nicht klappt, haben wir ein Problem ... */
- if( ! Try_Write( Idx )) return false;
+ if( ! Handle_Write( Idx )) return false;
/* nun neu pruefen: */
if( WRITEBUFFER_LEN - My_Connections[Idx].wdatalen - Len <= 0 )
/* Is this link already shutting down? */
if( Conn_OPTION_ISSET( &My_Connections[Idx], CONN_ISCLOSING )) {
/* Conn_Close() has been called recursively for this link;
- * probabe reason: Try_Write() failed -- see below. */
+ * probabe reason: Handle_Write() failed -- see below. */
#ifdef DEBUG
Log( LOG_DEBUG, "Recursive request to close connection: %d", Idx );
#endif
}
/* Try to write out the write buffer */
- (void)Try_Write( Idx );
+ (void)Handle_Write( Idx );
/* Shut down socket */
if( close( My_Connections[Idx].sock ) != 0 )
{
- /* Oops, we can't close the socket!? This is fatal! */
- Log( LOG_EMERG, "Error closing connection %d (socket %d) with %s:%d - %s!", Idx, My_Connections[Idx].sock, My_Connections[Idx].host, ntohs( My_Connections[Idx].addr.sin_port), strerror( errno ));
- Log( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME );
- exit( 1 );
+ /* Oops, we can't close the socket!? This is ... ugly! */
+ Log( LOG_CRIT, "Error closing connection %d (socket %d) with %s:%d - %s! (ignored)", Idx, My_Connections[Idx].sock, My_Connections[Idx].host, ntohs( My_Connections[Idx].addr.sin_port), strerror( errno ));
}
/* Mark socket as invalid: */
/* Is there a resolver sub-process running? */
if( My_Connections[Idx].res_stat )
- {
- /* Free resolver structures */
- FD_CLR( My_Connections[Idx].res_stat->pipe[0], &Resolver_FDs );
- close( My_Connections[Idx].res_stat->pipe[0] );
- close( My_Connections[Idx].res_stat->pipe[1] );
- free( My_Connections[Idx].res_stat );
- }
+ FreeRes_stat( &My_Connections[Idx] );
/* Servers: Modify time of next connect attempt? */
Conf_UnsetServer( Idx );
} /* SyncServerStruct */
-LOCAL bool
-Try_Write( CONN_ID Idx )
-{
- /* Versuchen, Daten aus dem Schreib-Puffer in den Socket zu
- * schreiben. true wird geliefert, wenn entweder keine Daten
- * zum Versenden vorhanden sind oder erfolgreich bearbeitet
- * werden konnten. Im Fehlerfall wird false geliefert und
- * die Verbindung geschlossen. */
-
- fd_set write_socket;
- struct timeval tv;
-
- assert( Idx > NONE );
- assert( My_Connections[Idx].sock > NONE );
-
- /* sind ueberhaupt Daten vorhanden? */
-#ifdef ZLIB
- if(( ! My_Connections[Idx].wdatalen > 0 ) && ( ! My_Connections[Idx].zip.wdatalen )) return true;
-#else
- if( ! My_Connections[Idx].wdatalen > 0 ) return true;
-#endif
-
- /* Timeout initialisieren: 0 Sekunden, also nicht blockieren */
- tv.tv_sec = 0; tv.tv_usec = 0;
-
- FD_ZERO( &write_socket );
- FD_SET( My_Connections[Idx].sock, &write_socket );
- if( select( My_Connections[Idx].sock + 1, NULL, &write_socket, NULL, &tv ) == -1 )
- {
- /* Fehler! */
- if( errno != EINTR )
- {
- 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;
- }
- }
-
- if( FD_ISSET( My_Connections[Idx].sock, &write_socket )) return Handle_Write( Idx );
- else return true;
-} /* Try_Write */
-
-
LOCAL void
Handle_Read( int Sock )
{
}
#ifdef ZLIB
- /* Schreibpuffer leer, aber noch Daten im Kompressionsbuffer?
- * Dann muss dieser nun geflushed werden! */
+ if(( My_Connections[Idx].wdatalen <= 0 ) && ( ! My_Connections[Idx].zip.wdatalen ))
+ return true;
+
+ /* write buffer empty, but not compression buf? -> flush compression buf. */
if( My_Connections[Idx].wdatalen == 0 ) Zip_Flush( Idx );
+#else
+ if( My_Connections[Idx].wdatalen <= 0 )
+ return true;
#endif
- assert( My_Connections[Idx].wdatalen > 0 );
-
- /* Daten schreiben */
- len = send( My_Connections[Idx].sock, My_Connections[Idx].wbuf, My_Connections[Idx].wdatalen, 0 );
- if( len < 0 )
- {
- /* Operation haette Socket "nur" blockiert ... */
- if( errno == EAGAIN ) return true;
+ len = write( My_Connections[Idx].sock, My_Connections[Idx].wbuf, My_Connections[Idx].wdatalen );
+ if( len < 0 ) {
+ if( errno == EAGAIN || errno == EINTR)
+ return true;
- /* Oops, ein Fehler! */
- Log( LOG_ERR, "Write error on connection %d (socket %d): %s!", Idx, My_Connections[Idx].sock, strerror( errno ));
+ 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;
}
- /* Puffer anpassen */
+ /* Update buffer len and move any data not yet written to beginning of buf */
My_Connections[Idx].wdatalen -= len;
memmove( My_Connections[Idx].wbuf, My_Connections[Idx].wbuf + len, My_Connections[Idx].wdatalen );
#else
s = Resolve_Addr( &new_addr );
#endif
- if( s )
- {
- /* Sub-Prozess wurde asyncron gestartet */
- My_Connections[idx].res_stat = s;
- }
+ /* resolver process has been started */
+ if( s ) My_Connections[idx].res_stat = s;
/* Penalty-Zeit setzen */
Conn_SetPenalty( idx, 4 );
strlcpy( Conf_Server[i].ip, Conf_Server[i].host, sizeof( Conf_Server[i].ip ));
strlcpy( My_Connections[idx].host, Conf_Server[i].host, sizeof( My_Connections[idx].host ));
s = Resolve_Name( Conf_Server[i].host );
- if( s )
- {
- /* Sub-Prozess wurde asyncron gestartet */
- My_Connections[idx].res_stat = s;
- }
+
+ /* resolver process running? */
+ if( s ) My_Connections[idx].res_stat = s;
}
} /* Check_Servers */
memset( &My_Connections[Idx], 0, sizeof ( CONNECTION ));
My_Connections[Idx].sock = NONE;
- My_Connections[Idx].starttime = now;
My_Connections[Idx].lastdata = now;
My_Connections[Idx].lastprivmsg = now;
} /* Init_Conn_Struct */
if( len < 0 )
{
/* Error! */
- close( r_fd );
Log( LOG_CRIT, "Resolver: Can't read result: %s!", strerror( errno ));
+ FreeRes_stat( &My_Connections[i] );
return;
}
s->bufpos += len;
/* Okay, we got a complete result: this is a host name for outgoing
* connections and a host name or IDENT user name (if enabled) for
- * incoming conneciions.*/
+ * incoming connections.*/
if( My_Connections[i].sock > NONE )
{
- /* Incoming connection */
-
- /* Search client ... */
+ /* Incoming connection. Search client ... */
c = Client_GetFromConn( i );
assert( c != NULL );
/* Only update client information of unregistered clients */
if( Client_Type( c ) == CLIENT_UNKNOWN )
{
- if( s->stage == 0 )
- {
- /* host name */
+ switch(s->stage) {
+ case 0: /* host name */
strlcpy( My_Connections[i].host, s->buffer, sizeof( My_Connections[i].host ));
Client_SetHostname( c, s->buffer );
-
#ifdef IDENTAUTH
/* clean up buffer for IDENT result */
len = strlen( s->buffer ) + 1;
+ assert((size_t)len <= sizeof( s->buffer ));
memmove( s->buffer, s->buffer + len, sizeof( s->buffer ) - len );
+ assert(len <= s->bufpos );
s->bufpos -= len;
/* Don't close pipe and clean up, but
* instead wait for IDENT result */
s->stage = 1;
goto try_resolve;
- }
- else if( s->stage == 1 )
- {
- /* IDENT user name */
+
+ case 1: /* IDENT user name */
if( s->buffer[0] )
{
Log( LOG_INFO, "IDENT lookup for connection %ld: \"%s\".", i, s->buffer );
}
else Log( LOG_INFO, "IDENT lookup for connection %ld: no result.", i );
#endif
+ break;
+ default:
+ Log( LOG_ERR, "Resolver: got result for unknown stage %d!?", s->stage );
}
- else Log( LOG_ERR, "Resolver: got result for unknown stage %d!?", s->stage );
}
#ifdef DEBUG
else Log( LOG_DEBUG, "Resolver: discarding result for already registered connection %d.", i );
}
/* Clean up ... */
- FD_CLR( r_fd, &Resolver_FDs );
- 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;
+ FreeRes_stat( &My_Connections[i] );
/* Reset penalty time */
Conn_ResetPenalty( i );
LOCAL void
Simple_Message( int Sock, char *Msg )
{
+ char buf[COMMAND_LEN];
/* Write "simple" message to socket, without using compression
* or even the connection write buffers. Used e.g. for error
* messages by New_Connection(). */
-
assert( Sock > NONE );
assert( Msg != NULL );
- (void)send( Sock, Msg, strlen( Msg ), 0 );
- (void)send( Sock, "\r\n", 2, 0 );
+ strlcpy( buf, Msg, sizeof buf - 2);
+ strlcat( buf, "\r\n", sizeof buf);
+ (void)write( Sock, buf, strlen( buf ) );
} /* Simple_Error */