]> arthur.barton.de Git - ngircd-alex.git/blobdiff - src/ngircd/conn.c
Conn_Handler(): cleanup code, add/translate comments.
[ngircd-alex.git] / src / ngircd / conn.c
index 9e31e4eec576e7c3a747e0c97d65ed790911b5da..33c374c318d64686e545bf00b5d8436da6b405f6 100644 (file)
@@ -88,7 +88,7 @@ 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;
@@ -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,20 +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_NoListenIpv6)
-               created = ports_initlisteners(&Conf_ListenPorts, AF_INET6, cb_listen);
-#endif
-       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 */
 
@@ -349,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;
 }
@@ -393,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;
        }
 
@@ -435,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 */
@@ -476,104 +480,114 @@ NewListener(int af, const UINT16 Port)
 } /* NewListener */
 
 
+/**
+ * "Main Loop": Loop until shutdown or restart is signalled.
+ * This function loops until a shutdown or restart of ngIRCd is signalled and
+ * calls io_dispatch() to check for readable and writable sockets every second.
+ * It checks for status changes on pending connections (e. g. when a hostname
+ * has been resolved), checks for "penalties" and timeouts, and handles the
+ * input buffers.
+ */
 GLOBAL void
-Conn_Handler( void )
+Conn_Handler(void)
 {
-       /* "Main Loop.": Loop until a signal (for shutdown or restart) arrives.
-        * Call io_dispatch() to check for read/writeable sockets every second
-        * Wait for status change on pending connections (e.g: when the hostname has been resolved)
-        * check for penalty/timeouts
-        * handle input buffers
-        */
        int i;
        unsigned int wdatalen;
        struct timeval tv;
        time_t t;
-       bool timeout;
 
-       while(( ! NGIRCd_SignalQuit ) && ( ! NGIRCd_SignalRestart )) {
-               timeout = true;
+       while (!NGIRCd_SignalQuit && !NGIRCd_SignalRestart) {
+               t = time(NULL);
 
 #ifdef ZEROCONF
-               Rendezvous_Handler( );
+               Rendezvous_Handler();
 #endif
 
                /* Should the configuration be reloaded? */
-               if (NGIRCd_SignalRehash) {
-                       NGIRCd_Rehash( );
-               }
+               if (NGIRCd_SignalRehash)
+                       NGIRCd_Rehash();
 
                /* Check configured servers and established links */
-               Check_Servers( );
-               Check_Connections( );
-
-               t = time( NULL );
-
-               /* noch volle Lese-Buffer suchen */
-               for( i = 0; i < Pool_Size; i++ ) {
-                       if(( My_Connections[i].sock > NONE ) && ( array_bytes(&My_Connections[i].rbuf) > 0 ) &&
-                        ( My_Connections[i].delaytime < t ))
-                       {
-                               /* Kann aus dem Buffer noch ein Befehl extrahiert werden? */
-                               if (Handle_Buffer( i )) timeout = false;
+               Check_Servers();
+               Check_Connections();
+
+               /* Look for non-empty read buffers ... */
+               for (i = 0; i < Pool_Size; i++) {
+                       if ((My_Connections[i].sock > NONE)
+                           && (array_bytes(&My_Connections[i].rbuf) > 0)
+                           && (My_Connections[i].delaytime < t)) {
+                               /* ... and try to handle the received data */
+                               Handle_Buffer(i);
                        }
                }
 
-               /* noch volle Schreib-Puffer suchen */
-               for( i = 0; i < Pool_Size; i++ ) {
-                       if ( My_Connections[i].sock <= NONE )
+               /* Look for non-empty write buffers ... */
+               for (i = 0; i < Pool_Size; i++) {
+                       if (My_Connections[i].sock <= NONE)
                                continue;
 
                        wdatalen = (unsigned int)array_bytes(&My_Connections[i].wbuf);
-
 #ifdef ZLIB
-                       if (( wdatalen > 0 ) || ( array_bytes(&My_Connections[i].zip.wbuf)> 0 ))
+                       if (wdatalen > 0 ||
+                           array_bytes(&My_Connections[i].zip.wbuf) > 0)
 #else
-                       if ( wdatalen > 0 )
+                       if (wdatalen > 0)
 #endif
                        {
-                               /* Socket der Verbindung in Set aufnehmen */
-                               io_event_add( My_Connections[i].sock, IO_WANTWRITE );
+                               /* Set the "WANTWRITE" flag on this socket */
+                               io_event_add(My_Connections[i].sock,
+                                            IO_WANTWRITE);
                        }
                }
 
-               /* von welchen Sockets koennte gelesen werden? */
-               for (i = 0; i < Pool_Size; i++ ) {
-                       if ( My_Connections[i].sock <= NONE )
+               /* Check from which sockets we possibly could read ... */
+               for (i = 0; i < Pool_Size; i++) {
+                       if (My_Connections[i].sock <= NONE)
                                continue;
 
                        if (Resolve_INPROGRESS(&My_Connections[i].res_stat)) {
-                               /* wait for completion of Resolver Sub-Process */
-                               io_event_del( My_Connections[i].sock, IO_WANTREAD );
+                               /* Wait for completion of resolver sub-process ... */
+                               io_event_del(My_Connections[i].sock,
+                                            IO_WANTREAD);
                                continue;
                        }
 
-                       if ( Conn_OPTION_ISSET( &My_Connections[i], CONN_ISCONNECTING ))
-                               continue;       /* wait for completion of connect() */
+                       if (Conn_OPTION_ISSET(&My_Connections[i], CONN_ISCONNECTING))
+                               /* Wait for completion of connect() ... */
+                               continue;
 
-                       if( My_Connections[i].delaytime > t ) {
-                               /* Fuer die Verbindung ist eine "Penalty-Zeit" gesetzt */
-                               io_event_del( My_Connections[i].sock, IO_WANTREAD );
+                       if (My_Connections[i].delaytime > t) {
+                               /* There is a "penalty time" set: ignore socket! */
+                               io_event_del(My_Connections[i].sock,
+                                            IO_WANTREAD);
                                continue;
                        }
-                       io_event_add( My_Connections[i].sock, IO_WANTREAD );
+                       io_event_add(My_Connections[i].sock, IO_WANTREAD);
                }
 
-               /* (re-)set timeout - tv_sec/usec are undefined after io_dispatch() returns */
+               /* Set the timeout for reading from the network to 1 second,
+                * which is the granularity with witch we handle "penalty
+                * times" for example.
+                * Note: tv_sec/usec are undefined(!) after io_dispatch()
+                * returns, so we have to set it beforce each call to it! */
                tv.tv_usec = 0;
-               tv.tv_sec = timeout ? 1 : 0;
-
-               /* wait for activity */
-               i = io_dispatch( &tv );
-               if (i == -1 && errno != EINTR ) {
-                       Log(LOG_EMERG, "Conn_Handler(): io_dispatch(): %s!", strerror(errno));
-                       Log(LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME);
-                       exit( 1 );
+               tv.tv_sec = 1;
+
+               /* Wait for activity ... */
+               i = io_dispatch(&tv);
+               if (i == -1 && errno != EINTR) {
+                       Log(LOG_EMERG, "Conn_Handler(): io_dispatch(): %s!",
+                           strerror(errno));
+                       Log(LOG_ALERT, "%s exiting due to fatal errors!",
+                           PACKAGE_NAME);
+                       exit(1);
                }
        }
 
-       if( NGIRCd_SignalQuit ) Log( LOG_NOTICE|LOG_snotice, "Server going down NOW!" );
-       else if( NGIRCd_SignalRestart ) Log( LOG_NOTICE|LOG_snotice, "Server restarting NOW!" );
+       if (NGIRCd_SignalQuit)
+               Log(LOG_NOTICE | LOG_snotice, "Server going down NOW!");
+       else if (NGIRCd_SignalRestart)
+               Log(LOG_NOTICE | LOG_snotice, "Server restarting NOW!");
 } /* Conn_Handler */
 
 
@@ -1217,11 +1231,13 @@ Read_Request( CONN_ID Idx )
 } /* Read_Request */
 
 
+/**
+ * Handle data in connection read-buffer.
+ * @return true if a reuqest was handled, false otherwise (and on errors).
+ */
 static bool
-Handle_Buffer( CONN_ID Idx )
+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;
 #endif
@@ -1237,86 +1253,104 @@ Handle_Buffer( CONN_ID Idx )
        result = false;
        for (;;) {
                /* Check penalty */
-               if( My_Connections[Idx].delaytime > starttime) return result;
+               if (My_Connections[Idx].delaytime > starttime)
+                       return result;
 #ifdef ZLIB
-               /* unpack compressed data */
-               if ( Conn_OPTION_ISSET( &My_Connections[Idx], CONN_ZIP ))
-                       if( ! Unzip_Buffer( Idx )) return false;
+               /* Unpack compressed data, if compression is in use */
+               if (Conn_OPTION_ISSET(&My_Connections[Idx], CONN_ZIP)) {
+                       if (!Unzip_Buffer(Idx))
+                               return false;
+               }
 #endif
 
                if (0 == array_bytes(&My_Connections[Idx].rbuf))
                        break;
 
-               if (!array_cat0_temporary(&My_Connections[Idx].rbuf)) /* make sure buf is NULL terminated */
+               /* Make sure that the buffer is NULL terminated */
+               if (!array_cat0_temporary(&My_Connections[Idx].rbuf))
                        return false;
 
-               /* A Complete Request end with CR+LF, see RFC 2812. */
-               ptr = strstr( array_start(&My_Connections[Idx].rbuf), "\r\n" );
+               /* RFC 2812, section "2.3 Messages", 5th paragraph:
+                * "IRC messages are always lines of characters terminated
+                * with a CR-LF (Carriage Return - Line Feed) pair [...]". */
+               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' );
+               if (!ptr) {
+                       /* Check for non-RFC-compliant request (only CR or
+                        * LF)? Unfortunately, there are quite a few clients
+                        * out there that do this -- incl. "mIRC" :-( */
                        delta = 1;
-                       if( ptr1 && ptr2 ) ptr = ptr1 > ptr2 ? ptr2 : ptr1;
-                       else if( ptr1 ) ptr = ptr1;
-                       else if( ptr2 ) ptr = ptr2;
+                       ptr1 = strchr(array_start(&My_Connections[Idx].rbuf), '\r');
+                       ptr2 = strchr(array_start(&My_Connections[Idx].rbuf), '\n');
+                       if (ptr1 && ptr2)
+                               ptr = ptr1 > ptr2 ? ptr2 : ptr1;
+                       else if (ptr1)
+                               ptr = ptr1;
+                       else if (ptr2)
+                               ptr = ptr2;
                }
 #endif
 
-               if( ! ptr )
+               if (!ptr)
                        break;
 
-               /* End of request found */
+               /* Complete (=line terminated) request found, handle it! */
                *ptr = '\0';
 
-               len = ( ptr - (char*) array_start(&My_Connections[Idx].rbuf)) + delta;
+               len = ptr - (char *)array_start(&My_Connections[Idx].rbuf) + delta;
 
-               if( len > ( COMMAND_LEN - 1 )) {
-                       /* Request must not exceed 512 chars (incl. CR+LF!), see
-                        * RFC 2812. Disconnect Client if this happens. */
-                       Log( LOG_ERR, "Request too long (connection %d): %d bytes (max. %d expected)!",
-                                               Idx, array_bytes(&My_Connections[Idx].rbuf), COMMAND_LEN - 1 );
-                       Conn_Close( Idx, NULL, "Request too long", true );
+               if (len > (COMMAND_LEN - 1)) {
+                       /* Request must not exceed 512 chars (incl. CR+LF!),
+                        * see RFC 2812. Disconnect Client if this happens. */
+                       Log(LOG_ERR,
+                           "Request too long (connection %d): %d bytes (max. %d expected)!",
+                           Idx, array_bytes(&My_Connections[Idx].rbuf),
+                           COMMAND_LEN - 1);
+                       Conn_Close(Idx, NULL, "Request too long", true);
                        return false;
                }
 
-               if (len <= 2) { /* request was empty (only '\r\n') */
-                       array_moveleft(&My_Connections[Idx].rbuf, 1, delta); /* delta is either 1 or 2 */
+               if (len <= delta) {
+                       /* Request is empty (only '\r\n', '\r' or '\n');
+                        * delta is 2 ('\r\n') or 1 ('\r' or '\n'), see above */
+                       array_moveleft(&My_Connections[Idx].rbuf, 1, len);
                        break;
                }
+
 #ifdef ZLIB
                /* remember if stream is already compressed */
                old_z = My_Connections[Idx].options & CONN_ZIP;
 #endif
 
                My_Connections[Idx].msg_in++;
-               if (!Parse_Request(Idx, (char*)array_start(&My_Connections[Idx].rbuf) ))
+               if (!Parse_Request
+                   (Idx, (char *)array_start(&My_Connections[Idx].rbuf)))
                        return false;
 
                result = true;
 
                array_moveleft(&My_Connections[Idx].rbuf, 1, len);
                LogDebug("Connection %d: %d bytes left in read buffer.",
-                   Idx, array_bytes(&My_Connections[Idx].rbuf));
+                        Idx, array_bytes(&My_Connections[Idx].rbuf));
 #ifdef ZLIB
-               if(( ! old_z ) && ( My_Connections[Idx].options & CONN_ZIP ) &&
-                               ( array_bytes(&My_Connections[Idx].rbuf) > 0 ))
-               {
-                       /* The last Command activated Socket-Compression.
-                        * Data that was read after that needs to be copied to Unzip-buf
-                        * for decompression */
-                       if (!array_copy( &My_Connections[Idx].zip.rbuf, &My_Connections[Idx].rbuf ))
+               if ((!old_z) && (My_Connections[Idx].options & CONN_ZIP) &&
+                   (array_bytes(&My_Connections[Idx].rbuf) > 0)) {
+                       /* The last command activated socket compression.
+                        * Data that was read after that needs to be copied
+                        * to the unzip buffer for decompression: */
+                       if (!array_copy
+                           (&My_Connections[Idx].zip.rbuf,
+                            &My_Connections[Idx].rbuf))
                                return false;
 
                        array_trunc(&My_Connections[Idx].rbuf);
-                       LogDebug("Moved already received data (%u bytes) to uncompression buffer.",
-                                                               array_bytes(&My_Connections[Idx].zip.rbuf));
+                       LogDebug
+                           ("Moved already received data (%u bytes) to uncompression buffer.",
+                            array_bytes(&My_Connections[Idx].zip.rbuf));
                }
-#endif /* ZLIB */
+#endif
        }
        return result;
 } /* Handle_Buffer */
@@ -1438,7 +1472,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;
        }