]> arthur.barton.de Git - ngircd-alex.git/blobdiff - src/ngircd/conn.c
new io/buffer api.
[ngircd-alex.git] / src / ngircd / conn.c
index f5d4967b1eb302dcd6b58e016e2bed0fe7dd5587..6358ae1dde7983158d813ef66a492cdb5325dc5c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * 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
@@ -16,7 +16,7 @@
 
 #include "portab.h"
 
-static char UNUSED id[] = "$Id: conn.c,v 1.151 2005/06/01 21:28:50 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>
@@ -86,7 +86,6 @@ LOCAL bool Handle_Write PARAMS(( CONN_ID Idx ));
 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 ));
@@ -518,6 +517,15 @@ Conn_Handler( void )
 } /* 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, ... )
@@ -529,10 +537,6 @@ 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;
@@ -545,15 +549,38 @@ va_dcl
 #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 ));
@@ -594,7 +621,7 @@ Conn_Write( CONN_ID Idx, char *Data, int Len )
        {
                /* 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 )
@@ -645,7 +672,7 @@ Conn_Close( CONN_ID Idx, char *LogMsg, char *FwdMsg, bool InformClient )
        /* 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
@@ -683,7 +710,7 @@ Conn_Close( CONN_ID Idx, char *LogMsg, char *FwdMsg, bool InformClient )
        }
 
        /* 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 )
@@ -771,49 +798,6 @@ Conn_SyncServerStruct( void )
 } /* 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 )
 {
@@ -899,27 +883,28 @@ Handle_Write( CONN_ID Idx )
        }
 
 #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 = write( My_Connections[Idx].sock, My_Connections[Idx].wbuf, My_Connections[Idx].wdatalen );
-       if( len < 0 )
-       {
-               /* Operation haette Socket "nur" blockiert ... */
-               if( errno == EAGAIN ) return true;
+       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 );
 
@@ -1517,7 +1502,6 @@ Init_Conn_Struct( CONN_ID Idx )
 
        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 */
@@ -1644,7 +1628,7 @@ try_resolve:
 #ifdef IDENTAUTH
                                /* clean up buffer for IDENT result */
                                len = strlen( s->buffer ) + 1;
-                               assert(len <= sizeof( s->buffer ));
+                               assert((size_t)len <= sizeof( s->buffer ));
                                memmove( s->buffer, s->buffer + len, sizeof( s->buffer ) - len );
                                assert(len <= s->bufpos );
                                s->bufpos -= len;
@@ -1694,15 +1678,16 @@ try_resolve:
 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)write( Sock, Msg, strlen( Msg ) );
-       (void)write( Sock, "\r\n", 2 );
+       strlcpy( buf, Msg, sizeof buf - 2);
+       strlcat( buf, "\r\n", sizeof buf);
+       (void)write( Sock, buf, strlen( buf ) );
 } /* Simple_Error */