]> arthur.barton.de Git - ngircd.git/blobdiff - src/ngircd/conn.c
Fix handling of MaxConnections option
[ngircd.git] / src / ngircd / conn.c
index 1696d5c5f62968d8d495576b4e1bc67947db66ee..43f521cb6ccbf0ae72b6b19ce711127cf1e90f77 100644 (file)
@@ -88,10 +88,11 @@ static void Init_Conn_Struct PARAMS(( CONN_ID Idx ));
 static bool Init_Socket PARAMS(( int Sock ));
 static void New_Server PARAMS(( int Server, ng_ipaddr_t *dest ));
 static void Simple_Message PARAMS(( int Sock, const char *Msg ));
-static int NewListener PARAMS(( int af, const UINT16 Port ));
+static int NewListener PARAMS(( const char *listen_addr, UINT16 Port ));
 
 static array My_Listeners;
 static array My_ConnArray;
+static size_t NumConnections;
 
 #ifdef TCPWRAP
 int allow_severity = LOG_INFO;
@@ -108,7 +109,8 @@ static void
 cb_listen(int sock, short irrelevant)
 {
        (void) irrelevant;
-       New_Connection( sock );
+       if (New_Connection( sock ) >= 0)
+               NumConnections++;
 }
 
 
@@ -214,12 +216,10 @@ Conn_Init( void )
 
        /* Speicher fuer Verbindungs-Pool anfordern */
        Pool_Size = CONNECTION_POOL;
-       if( Conf_MaxConnections > 0 )
-       {
-               /* konfiguriertes Limit beachten */
-               if( Pool_Size > Conf_MaxConnections ) Pool_Size = Conf_MaxConnections;
-       }
-       
+       if ((Conf_MaxConnections > 0) &&
+               (Pool_Size > Conf_MaxConnections))
+                       Pool_Size = Conf_MaxConnections;
+
        if (!array_alloc(&My_ConnArray, sizeof(CONNECTION), (size_t)Pool_Size)) {
                Log( LOG_EMERG, "Can't allocate memory! [Conn_Init]" );
                exit( 1 );
@@ -272,7 +272,7 @@ Conn_Exit( void )
 
 
 static unsigned int
-ports_initlisteners(array *a, int af, void (*func)(int,short))
+ports_initlisteners(array *a, const char *listen_addr, void (*func)(int,short))
 {
        unsigned int created = 0;
        size_t len;
@@ -281,15 +281,15 @@ ports_initlisteners(array *a, int af, void (*func)(int,short))
 
        len = array_length(a, sizeof (UINT16));
        port = array_start(a);
-       while(len--) {
-               fd = NewListener(af, *port);
+       while (len--) {
+               fd = NewListener(listen_addr, *port);
                if (fd < 0) {
                        port++;
                        continue;
                }
                if (!io_event_create( fd, IO_WANTREAD, func )) {
                        Log( LOG_ERR, "io_event_create(): Could not add listening fd %d (port %u): %s!",
-                                                       fd, (unsigned int) *port, strerror(errno));
+                                               fd, (unsigned int) *port, strerror(errno));
                        close(fd);
                        port++;
                        continue;
@@ -297,7 +297,6 @@ ports_initlisteners(array *a, int af, void (*func)(int,short))
                created++;
                port++;
        }
-
        return created;
 }
 
@@ -306,21 +305,39 @@ GLOBAL unsigned int
 Conn_InitListeners( void )
 {
        /* Initialize ports on which the server should accept connections */
-
        unsigned int created = 0;
+       char *copy, *listen_addr;
 
        if (!io_library_init(CONNECTION_POOL)) {
                Log(LOG_EMERG, "Cannot initialize IO routines: %s", strerror(errno));
                return -1;
        }
 
-#ifdef WANT_IPV6
-       if (Conf_ListenIPv6)
-               created = ports_initlisteners(&Conf_ListenPorts, AF_INET6, cb_listen);
-#endif
-       if (Conf_ListenIPv4)
-               created += ports_initlisteners(&Conf_ListenPorts, AF_INET, cb_listen);
+       assert(Conf_ListenAddress);
+
+       /* can't use Conf_ListenAddress directly, see below */
+       copy = strdup(Conf_ListenAddress);
+       if (!copy) {
+               Log(LOG_CRIT, "Cannot copy %s: %s", Conf_ListenAddress, strerror(errno));
+               return 0;
+       }
+       listen_addr = strtok(copy, ",");
 
+       while (listen_addr) {
+               ngt_TrimStr(listen_addr);
+               if (*listen_addr)
+                       created += ports_initlisteners(&Conf_ListenPorts, listen_addr, cb_listen);
+
+               listen_addr = strtok(NULL, ",");
+       }
+
+       /*
+        * can't free() Conf_ListenAddress here. On /REHASH, if the config file
+        * cannot be re-loaded, we'd end up with a NULL Conf_ListenAddress.
+        * Instead, free() takes place in conf.c, before the config file
+        * is being parsed.
+        */
+       free(copy);
        return created;
 } /* Conn_InitListeners */
 
@@ -350,25 +367,15 @@ Conn_ExitListeners( void )
 
 
 static bool
-InitSinaddrListenAddr(int af, ng_ipaddr_t *addr, UINT16 Port)
+InitSinaddrListenAddr(ng_ipaddr_t *addr, const char *listen_addrstr, UINT16 Port)
 {
        bool ret;
-       const char *listen_addrstr = NULL;
-#ifdef WANT_IPV6
-       if (af == AF_INET)
-               listen_addrstr = "0.0.0.0";
-#else
-       (void)af;
-#endif
-       if (Conf_ListenAddress[0]) /* overrides V4/V6 atm */
-               listen_addrstr = Conf_ListenAddress;
 
        ret = ng_ipaddr_init(addr, listen_addrstr, Port);
        if (!ret) {
-               if (!listen_addrstr)
-                       listen_addrstr = "";
-               Log(LOG_CRIT, "Can't bind to %s:%u: can't convert ip address \"%s\"",
-                                       listen_addrstr, Port, listen_addrstr);
+               assert(listen_addrstr);
+               Log(LOG_CRIT, "Can't bind to [%s]:%u: can't convert ip address \"%s\"",
+                                               listen_addrstr, Port, listen_addrstr);
        }
        return ret;
 }
@@ -394,32 +401,33 @@ set_v6_only(int af, int sock)
 
 /* return new listening port file descriptor or -1 on failure */
 static int
-NewListener(int af, const UINT16 Port)
+NewListener(const char *listen_addr, UINT16 Port)
 {
        /* Create new listening socket on specified port */
        ng_ipaddr_t addr;
-       int sock;
+       int sock, af;
 #ifdef ZEROCONF
        char name[CLIENT_ID_LEN], *info;
 #endif
-       if (!InitSinaddrListenAddr(af, &addr, Port))
+       if (!InitSinaddrListenAddr(&addr, listen_addr, Port))
                return -1;
 
-       sock = socket(ng_ipaddr_af(&addr), SOCK_STREAM, 0);
+       af = ng_ipaddr_af(&addr);
+       sock = socket(af, SOCK_STREAM, 0);
        if( sock < 0 ) {
-               Log( LOG_CRIT, "Can't create socket: %s!", strerror( errno ));
+               Log(LOG_CRIT, "Can't create socket (af %d) : %s!", af, strerror(errno));
                return -1;
        }
 
-       af = ng_ipaddr_af(&addr);
-
        set_v6_only(af, sock);
 
-       if( ! Init_Socket( sock )) return -1;
+       if (!Init_Socket(sock))
+               return -1;
 
        if (bind(sock, (struct sockaddr *)&addr, ng_ipaddr_salen(&addr)) != 0) {
-               Log( LOG_CRIT, "Can't bind socket (port %d) : %s!", Port, strerror( errno ));
-               close( sock );
+               Log(LOG_CRIT, "Can't bind socket to address %s:%d - %s",
+                       ng_ipaddr_tostr(&addr), Port, strerror(errno));
+               close(sock);
                return -1;
        }
 
@@ -436,12 +444,7 @@ NewListener(int af, const UINT16 Port)
                return -1;
        }
 
-#ifdef WANT_IPV6
-       if (af == AF_INET6)
-               Log(LOG_INFO, "Now listening on [%s]:%d (socket %d).", ng_ipaddr_tostr(&addr), Port, sock);
-       else
-#endif
-               Log(LOG_INFO, "Now listening on %s:%d (socket %d).", ng_ipaddr_tostr(&addr), Port, sock);
+       Log(LOG_INFO, "Now listening on [%s]:%d (socket %d).", ng_ipaddr_tostr(&addr), Port, sock);
 
 #ifdef ZEROCONF
        /* Get best server description text */
@@ -876,7 +879,10 @@ Conn_Close( CONN_ID Idx, char *LogMsg, char *FwdMsg, bool InformClient )
        /* Clean up connection structure (=free it) */
        Init_Conn_Struct( Idx );
 
-       LogDebug("Shutdown of connection %d completed.", Idx );
+       assert(NumConnections > 0);
+       if (NumConnections)
+               NumConnections--;
+       LogDebug("Shutdown of connection %d completed", Idx );
 } /* Conn_Close */
 
 
@@ -999,7 +1005,7 @@ New_Connection( int Sock )
 #endif
        ng_ipaddr_t new_addr;
        char ip_str[NG_INET_ADDRSTRLEN];
-       int new_sock, new_sock_len, new_Pool_Size;
+       int new_sock, new_sock_len;
        CLIENT *c;
        long cnt;
 
@@ -1018,6 +1024,7 @@ New_Connection( int Sock )
                Log(LOG_CRIT, "fd %d: Can't convert IP address!", new_sock);
                Simple_Message(new_sock, "ERROR :Internal Server Error");
                close(new_sock);
+               return -1;
        }
 
 #ifdef TCPWRAP
@@ -1046,18 +1053,16 @@ New_Connection( int Sock )
                return -1;
        }
 
-       if( new_sock >= Pool_Size ) {
-               new_Pool_Size = new_sock + 1;
-               /* No free Connection Structures, check if we may accept further connections */
-               if ((( Conf_MaxConnections > 0) && Pool_Size >= Conf_MaxConnections) ||
-                       (new_Pool_Size < Pool_Size))
-               {
-                       Log( LOG_ALERT, "Can't accept connection: limit (%d) reached!", Pool_Size );
-                       Simple_Message( new_sock, "ERROR :Connection limit reached" );
-                       close( new_sock );
-                       return -1;
-               }
+       if ((Conf_MaxConnections > 0) &&
+               (NumConnections >= (size_t) Conf_MaxConnections))
+       {
+               Log( LOG_ALERT, "Can't accept connection: limit (%d) reached!", Conf_MaxConnections);
+               Simple_Message( new_sock, "ERROR :Connection limit reached" );
+               close( new_sock );
+               return -1;
+       }
 
+       if( new_sock >= Pool_Size ) {
                if (!array_alloc(&My_ConnArray, sizeof(CONNECTION),
                                 (size_t)new_sock)) {
                        Log( LOG_EMERG, "Can't allocate memory! [New_Connection]" );
@@ -1070,7 +1075,7 @@ New_Connection( int Sock )
 
                /* Adjust pointer to new block */
                My_Connections = array_start(&My_ConnArray);
-               while (Pool_Size < new_Pool_Size)
+               while (Pool_Size <= new_sock)
                        Init_Conn_Struct(Pool_Size++);
        }
 
@@ -1224,7 +1229,7 @@ Handle_Buffer( CONN_ID Idx )
        /* Handle Data in Connections Read-Buffer.
         * Return true if a reuqest was handled, false otherwise (also returned on errors). */
 #ifndef STRICT_RFC
-       char *ptr1, *ptr2;
+       char *ptr1, *ptr2, *first_eol;
 #endif
        char *ptr;
        size_t len, delta;
@@ -1252,19 +1257,32 @@ Handle_Buffer( CONN_ID Idx )
                        return false;
 
                /* A Complete Request end with CR+LF, see RFC 2812. */
+               delta = 2;
                ptr = strstr( array_start(&My_Connections[Idx].rbuf), "\r\n" );
 
-               if( ptr ) delta = 2; /* complete request */
 #ifndef STRICT_RFC
-               else {
-                       /* Check for non-RFC-compliant request (only CR or LF)? Unfortunately,
-                        * there are quite a few clients that do this (incl. "mIRC" :-( */
-                       ptr1 = strchr( array_start(&My_Connections[Idx].rbuf), '\r' );
-                       ptr2 = strchr( array_start(&My_Connections[Idx].rbuf), '\n' );
+               /* Check for non-RFC-compliant request (only CR or LF)?
+                * Unfortunately, there are quite a few clients out there
+                * that do this -- e. g. mIRC, BitchX, and Trillian :-( */
+               ptr1 = strchr(array_start(&My_Connections[Idx].rbuf), '\r');
+               ptr2 = strchr(array_start(&My_Connections[Idx].rbuf), '\n');
+               if (ptr) {
+                       /* Check if there is a single CR or LF _before_ the
+                        * corerct CR+LF line terminator:  */
+                       first_eol = ptr1 < ptr2 ? ptr1 : ptr2;
+                       if (first_eol < ptr) {
+                               /* Single CR or LF before CR+LF found */
+                               ptr = first_eol;
+                               delta = 1;
+                       }
+               } else if (ptr1 || ptr2) {
+                       /* No CR+LF terminated command found, but single
+                        * CR or LF found ... */
+                       if (ptr1 && ptr2)
+                               ptr = ptr1 < ptr2 ? ptr1 : ptr2;
+                       else
+                               ptr = ptr1 ? ptr1 : ptr2;
                        delta = 1;
-                       if( ptr1 && ptr2 ) ptr = ptr1 > ptr2 ? ptr2 : ptr1;
-                       else if( ptr1 ) ptr = ptr1;
-                       else if( ptr2 ) ptr = ptr2;
                }
 #endif
 
@@ -1439,7 +1457,7 @@ New_Server( int Server , ng_ipaddr_t *dest)
        af_dest = ng_ipaddr_af(dest);
        new_sock = socket(af_dest, SOCK_STREAM, 0);
        if (new_sock < 0) {
-               Log( LOG_CRIT, "Can't create socket: %s!", strerror( errno ));
+               Log( LOG_CRIT, "Can't create socket (af %d) : %s!", af_dest, strerror( errno ));
                return;
        }