]> arthur.barton.de Git - ngircd.git/blobdiff - src/ngircd/conn.c
Don't connect to a server if a connection to another server within the same group
[ngircd.git] / src / ngircd / conn.c
index 30b86b1adf8700f7b2b10006b4d67d62a6b1a1c8..2cbb806c98f76bc2ac2c7d8e7a2e7afa8efca087 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2005 Alexander Barton <alex@barton.de>
+ * Copyright (c)2001-2007 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
@@ -17,7 +17,7 @@
 #include "portab.h"
 #include "io.h"
 
-static char UNUSED id[] = "$Id: conn.c,v 1.199 2006/12/17 22:55:07 fw Exp $";
+static char UNUSED id[] = "$Id: conn.c,v 1.210 2007/06/28 15:13:39 fw Exp $";
 
 #include "imp.h"
 #include <assert.h>
@@ -83,6 +83,7 @@ static char UNUSED id[] = "$Id: conn.c,v 1.199 2006/12/17 22:55:07 fw Exp $";
 
 
 static bool Handle_Write PARAMS(( CONN_ID Idx ));
+static bool Conn_Write PARAMS(( CONN_ID Idx, char *Data, size_t Len ));
 static int New_Connection PARAMS(( int Sock ));
 static CONN_ID Socket2Index PARAMS(( int Sock ));
 static void Read_Request PARAMS(( CONN_ID Idx ));
@@ -123,7 +124,6 @@ cb_connserver(int sock, UNUSED short what)
 {
        int res, err;
        socklen_t sock_len;
-       CLIENT *c;
        CONN_ID idx = Socket2Index( sock );
        if (idx <= NONE) {
                LogDebug("cb_connserver wants to write on unknown socket?!");
@@ -150,14 +150,7 @@ cb_connserver(int sock, UNUSED short what)
                            Conf_Server[Conf_GetServer(idx)].port,
                            idx, strerror(err));
 
-               /* Clean up the CLIENT structure (to avoid silly log
-                * messages) and call Conn_Close() to do the rest. */
-               c = Conn_GetClient(idx);
-               if (c)
-                       Client_DestroyNow(c);
-               Conn_Close(idx, "Can't connect!", NULL, false);
+               Conn_Close(idx, "Can't connect!", NULL, false);
                return;
        }
 
@@ -267,10 +260,10 @@ Conn_Exit( void )
 } /* Conn_Exit */
 
 
-static int
+static unsigned int
 ports_initlisteners(array *a, void (*func)(int,short))
 {
-       int created = 0;
+       unsigned int created = 0;
        size_t len;
        int fd;
        UINT16 *port;
@@ -298,12 +291,12 @@ ports_initlisteners(array *a, void (*func)(int,short))
 }
 
 
-GLOBAL int
+GLOBAL unsigned int
 Conn_InitListeners( void )
 {
        /* Initialize ports on which the server should accept connections */
 
-       int created;
+       unsigned int created;
 
        if (!io_library_init(CONNECTION_POOL)) {
                Log(LOG_EMERG, "Cannot initialize IO routines: %s", strerror(errno));
@@ -384,7 +377,7 @@ NewListener( const UINT16 Port )
        if( ! Init_Socket( sock )) return -1;
 
        if (bind(sock, (struct sockaddr *)&addr, (socklen_t)sizeof(addr)) != 0) {
-               Log( LOG_CRIT, "Can't bind socket: %s!", strerror( errno ));
+               Log( LOG_CRIT, "Can't bind socket (port %d) : %s!", Port, strerror( errno ));
                close( sock );
                return -1;
        }
@@ -615,51 +608,74 @@ va_dcl
 } /* Conn_WriteStr */
 
 
-GLOBAL bool
+/**
+ * Append Data to the outbound write buffer of a connection.
+ * @param Idx Index of the connection.
+ * @param Data pointer to the data.
+ * @param Len length of Data.
+ * @return true on success, false otherwise.
+ */
+static bool
 Conn_Write( CONN_ID Idx, char *Data, size_t Len )
 {
-       /* Daten in Socket schreiben. Bei "fatalen" Fehlern wird
-        * der Client disconnectiert und false geliefert. */
-
+       CLIENT *c;
+       size_t writebuf_limit = WRITEBUFFER_LEN;
        assert( Idx > 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. */
+       c = Conn_GetClient(Idx);
+       assert( c != NULL);
+
+       /* Servers do get special write buffer limits, so they can generate
+        * all the messages that are required while peering. */
+       if (Client_Type(c) == CLIENT_SERVER)
+               writebuf_limit = WRITEBUFFER_SLINK_LEN;
+
+       /* Is the socket still open? A previous call to Conn_Write()
+        * may have closed the connection due to a fatal error.
+        * In this case it is sufficient to return an error, as well. */
        if( My_Connections[Idx].sock <= NONE ) {
-               LogDebug("Skipped write on closed socket (connection %d).", Idx );
+               LogDebug("Skipped write on closed socket (connection %d).", Idx);
                return false;
        }
 
-       /* Pruefen, ob im Schreibpuffer genuegend Platz ist. Ziel ist es,
-        * moeglichts viel im Puffer zu haben und _nicht_ gleich alles auf den
-        * Socket zu schreiben (u.a. wg. Komprimierung). */
-       if( array_bytes(&My_Connections[Idx].wbuf) >= WRITEBUFFER_LEN) {
-               /* Der Puffer ist dummerweise voll. Jetzt versuchen, den Puffer
-                * zu schreiben, wenn das nicht klappt, haben wir ein Problem ... */
-               if( ! Handle_Write( Idx )) return false;
-
-               /* check again: if our writebuf is twice als large as the initial limit: Kill connection */
-               if( array_bytes(&My_Connections[Idx].wbuf) >= (WRITEBUFFER_LEN*2)) {
-                       Log( LOG_NOTICE, "Write buffer overflow (connection %d)!", Idx );
-                       Conn_Close( Idx, "Write buffer overflow!", NULL, false );
-                       return false;
-               }
-       }
-
 #ifdef ZLIB
        if ( Conn_OPTION_ISSET( &My_Connections[Idx], CONN_ZIP )) {
-               /* Daten komprimieren und in Puffer kopieren */
-               if( ! Zip_Buffer( Idx, Data, Len )) return false;
+               /* Compressed link:
+                * Zip_Buffer() does all the dirty work for us: it flushes
+                * the (pre-)compression buffers if required and handles
+                * all error conditions. */
+               if (!Zip_Buffer(Idx, Data, Len))
+                       return false;
        }
        else
 #endif
        {
-               /* Daten in Puffer kopieren */
-               if (!array_catb( &My_Connections[Idx].wbuf, Data, Len ))
+               /* Uncompressed link:
+                * Check if outbound buffer has enough space for the data. */
+               if (array_bytes(&My_Connections[Idx].wbuf) + Len >=
+                   writebuf_limit) {
+                       /* Buffer is full, flush it. Handle_Write deals with
+                        * low-level errors, if any. */
+                       if (!Handle_Write(Idx))
+                               return false;
+               }
+
+               /* When the write buffer is still too big after flushing it,
+                * the connection will be killed. */
+               if (array_bytes(&My_Connections[Idx].wbuf) + Len >=
+                   writebuf_limit) {
+                       Log(LOG_NOTICE,
+                           "Write buffer overflow (connection %d, size %lu byte)!",
+                           Idx,
+                           (unsigned long)array_bytes(&My_Connections[Idx].wbuf));
+                       Conn_Close(Idx, "Write buffer overflow!", NULL, false);
+                       return false;
+               }
+
+               /* Copy data to write buffer */
+               if (!array_catb(&My_Connections[Idx].wbuf, Data, Len))
                        return false;
 
                My_Connections[Idx].bytes_out += Len;
@@ -700,7 +716,7 @@ Conn_Close( CONN_ID Idx, char *LogMsg, char *FwdMsg, bool InformClient )
 
        /* Mark link as "closing" */
        Conn_OPTION_ADD( &My_Connections[Idx], CONN_ISCLOSING );
-               
+
        if (LogMsg)
                txt = LogMsg;
        else
@@ -728,7 +744,6 @@ Conn_Close( CONN_ID Idx, char *LogMsg, char *FwdMsg, bool InformClient )
                         (double)My_Connections[Idx].bytes_out / 1024);
                }
 #endif
-
                /* Send ERROR to client (see RFC!) */
                if (FwdMsg)
                        Conn_WriteStr(Idx, "ERROR :%s", FwdMsg);
@@ -872,24 +887,23 @@ Handle_Write( CONN_ID Idx )
        wdatalen = array_bytes(&My_Connections[Idx].wbuf );
 
 #ifdef ZLIB
-       if (wdatalen == 0 && !array_bytes(&My_Connections[Idx].zip.wbuf)) {
-               io_event_del(My_Connections[Idx].sock, IO_WANTWRITE );
-               return true;
+       if (wdatalen == 0) {
+               /* Write buffer is empty, so we try to flush the compression
+                * buffer and get some data to work with from there :-) */
+               if (!Zip_Flush(Idx))
+                       return false;
+
+               /* Now the write buffer most probably has changed: */
+               wdatalen = array_bytes(&My_Connections[Idx].wbuf);
        }
+#endif
 
-       /* write buffer empty, but not compression buffer?
-         * -> flush compression buffer! */
-       if (wdatalen == 0)
-               Zip_Flush(Idx);
-#else
        if (wdatalen == 0) {
+               /* Still no data, fine. */
                io_event_del(My_Connections[Idx].sock, IO_WANTWRITE );
                return true;
        }
-#endif
 
-       /* Zip_Flush() may have changed the write buffer ... */
-       wdatalen = array_bytes(&My_Connections[Idx].wbuf);
        LogDebug
            ("Handle_Write() called for connection %d, %ld bytes pending ...",
             Idx, wdatalen);
@@ -995,16 +1009,22 @@ New_Connection( int Sock )
                        Init_Conn_Struct(Pool_Size++);
        }
 
+       /* register callback */
+       if (!io_event_create( new_sock, IO_WANTREAD, cb_clientserver)) {
+               Log(LOG_ALERT, "Can't accept connection: io_event_create failed!");
+               Simple_Message(new_sock, "ERROR :Internal error");
+               close(new_sock);
+               return -1;
+       }
+
        c = Client_NewLocal( new_sock, inet_ntoa( new_addr.sin_addr ), CLIENT_UNKNOWN, false );
        if( ! c ) {
-               Log( LOG_ALERT, "Can't accept connection: can't create client structure!" );
-               goto out;
+               Log(LOG_ALERT, "Can't accept connection: can't create client structure!");
+               Simple_Message(new_sock, "ERROR :Internal error");
+               io_close(new_sock);
+               return -1;
        }
 
-       /* register callback */
-       if (!io_event_create( new_sock, IO_WANTREAD, cb_clientserver))
-               goto out;
-
        Init_Conn_Struct( new_sock );
        My_Connections[new_sock].sock = new_sock;
        My_Connections[new_sock].addr = new_addr;
@@ -1025,11 +1045,7 @@ New_Connection( int Sock )
        /* Penalty-Zeit setzen */
        Conn_SetPenalty( new_sock, 4 );
        return new_sock;
- out:
-       Simple_Message( new_sock, "ERROR :Internal error" );
-       close( new_sock );
-       return -1;
-}/* New_Connection */
+} /* New_Connection */
 
 
 static CONN_ID
@@ -1049,47 +1065,52 @@ Socket2Index( int Sock )
 } /* Socket2Index */
 
 
+/**
+ * Read data from the network to the read buffer. If an error occures,
+ * the socket of this connection will be shut down.
+ */
 static void
 Read_Request( CONN_ID Idx )
 {
-       /* Daten von Socket einlesen und entsprechend behandeln.
-        * Tritt ein Fehler auf, so wird der Socket geschlossen. */
-
        ssize_t len;
-       char readbuf[1024];
+       char readbuf[READBUFFER_LEN];
        CLIENT *c;
-
        assert( Idx > NONE );
        assert( My_Connections[Idx].sock > NONE );
 
 #ifdef ZLIB
-       if (( array_bytes(&My_Connections[Idx].rbuf) >= READBUFFER_LEN ) ||
-               ( array_bytes(&My_Connections[Idx].zip.rbuf) >= ZREADBUFFER_LEN ))
+       if ((array_bytes(&My_Connections[Idx].rbuf) >= READBUFFER_LEN) ||
+               (array_bytes(&My_Connections[Idx].zip.rbuf) >= READBUFFER_LEN))
 #else
-       if ( array_bytes(&My_Connections[Idx].rbuf) >= READBUFFER_LEN )
+       if (array_bytes(&My_Connections[Idx].rbuf) >= READBUFFER_LEN)
 #endif
        {
-               /* Der Lesepuffer ist voll */
-               Log( LOG_ERR, "Receive buffer overflow (connection %d): %d bytes!", Idx,
-                                               array_bytes(&My_Connections[Idx].rbuf));
+               /* Read buffer is full */
+               Log(LOG_ERR,
+                   "Receive buffer overflow (connection %d): %d bytes!",
+                   Idx, array_bytes(&My_Connections[Idx].rbuf));
                Conn_Close( Idx, "Receive buffer overflow!", NULL, false );
                return;
        }
 
-       len = read( My_Connections[Idx].sock, readbuf, sizeof readbuf -1 );
-       if( len == 0 ) {
-               Log( LOG_INFO, "%s:%d (%s) is closing the connection ...",
-                       My_Connections[Idx].host, ntohs( My_Connections[Idx].addr.sin_port),
-                                       inet_ntoa( My_Connections[Idx].addr.sin_addr ));
-               Conn_Close( Idx, "Socket closed!", "Client closed connection", false );
+       len = read(My_Connections[Idx].sock, readbuf, sizeof(readbuf));
+       if (len == 0) {
+               Log(LOG_INFO, "%s:%d (%s) is closing the connection ...",
+                   My_Connections[Idx].host,
+                   ntohs(My_Connections[Idx].addr.sin_port),
+                   inet_ntoa( My_Connections[Idx].addr.sin_addr));
+               Conn_Close(Idx,
+                          "Socket closed!", "Client closed connection",
+                          false);
                return;
        }
 
-       if( len < 0 ) {
+       if (len < 0) {
                if( errno == EAGAIN ) return;
-               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 );
+               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;
        }
 #ifdef ZLIB
@@ -1106,8 +1127,7 @@ Read_Request( CONN_ID Idx )
        } else
 #endif
        {
-               readbuf[len] = 0;
-               if (!array_cats( &My_Connections[Idx].rbuf, readbuf )) {
+               if (!array_catb( &My_Connections[Idx].rbuf, readbuf, len)) {
                        Log( LOG_ERR, "Could not append recieved data to input buffer (connn %d): %d bytes!", Idx, len );
                        Conn_Close( Idx, "Receive buffer overflow!", NULL, false );
                }
@@ -1226,11 +1246,6 @@ Handle_Buffer( CONN_ID Idx )
                        /* The last Command activated Socket-Compression.
                         * Data that was read after that needs to be copied to Unzip-buf
                         * for decompression */
-                       if( array_bytes(&My_Connections[Idx].rbuf)> ZREADBUFFER_LEN ) {
-                               Log( LOG_ALERT, "Connection %d: No space left in unzip buf (need %u bytes)!",
-                                                               Idx, array_bytes(&My_Connections[Idx].rbuf ));
-                               return false;
-                       }
                        if (!array_copy( &My_Connections[Idx].zip.rbuf, &My_Connections[Idx].rbuf ))
                                return false;
 
@@ -1318,7 +1333,7 @@ Check_Servers( void )
                if( Conf_Server[i].group > NONE ) {
                        for (n = 0; n < MAX_SERVERS; n++) {
                                if (n == i) continue;
-                               if ((Conf_Server[n].conn_id > NONE) &&
+                               if ((Conf_Server[n].conn_id != NONE) &&
                                        (Conf_Server[n].group == Conf_Server[i].group))
                                                break;
                        }
@@ -1331,7 +1346,7 @@ Check_Servers( void )
                        continue;
 
                /* Okay, try to connect now */
-               Conf_Server[i].lasttry = time_now;
+               Conf_Server[i].conn_id = SERVER_WAIT;
                assert(Resolve_Getfd(&Conf_Server[i].res_stat) < 0);
                Resolve_Name(&Conf_Server[i].res_stat, Conf_Server[i].host, cb_Connect_to_Server);
        }
@@ -1638,9 +1653,9 @@ Conn_GetClient( CONN_ID Idx )
        assert( Idx >= 0 );
 
        c = array_get(&My_ConnArray, sizeof (CONNECTION), (size_t)Idx);
-       
+
        assert(c != NULL);
-       
+
        return c ? c->client : NULL;
 }