]> arthur.barton.de Git - ngircd-alex.git/blobdiff - src/ngircd/conn.c
Add Doxygen @file documentation to each source and header file
[ngircd-alex.git] / src / ngircd / conn.c
index 6994c8be8d7fb3876bc88961c95cac258a16f5d3..e73dd3060e9d51887f21a610418c34b408e4c7f6 100644 (file)
@@ -7,8 +7,6 @@
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  * Please read the file COPYING, README and AUTHORS for more information.
- *
- * Connection management
  */
 
 
 #include "conf-ssl.h"
 #include "io.h"
 
+/**
+ * @file
+ * Connection management
+ */
+
 #include "imp.h"
 #include <assert.h>
 #ifdef PROTOTYPES
-#      include <stdarg.h>
+# include <stdarg.h>
 #else
-#      include <varargs.h>
+# include <varargs.h>
 #endif
 #include <stdio.h>
 #include <stdlib.h>
 
 #include "array.h"
 #include "defines.h"
-#include "resolve.h"
 
 #include "exp.h"
 #include "conn.h"
 
 #include "imp.h"
 #include "ngircd.h"
+#include "array.h"
 #include "client.h"
 #include "conf.h"
 #include "conn-ssl.h"
 #include "conn-zip.h"
 #include "conn-func.h"
 #include "log.h"
+#include "ng_ipaddr.h"
 #include "parse.h"
+#include "resolve.h"
 #include "tool.h"
 
-#ifdef ZEROCONF
-# include "rendezvous.h"
-#endif
-
 #include "exp.h"
 
 
@@ -80,6 +81,7 @@
 
 #define MAX_COMMANDS 3
 #define MAX_COMMANDS_SERVER 10
+#define MAX_COMMANDS_SERVICE MAX_COMMANDS_SERVER
 
 
 static bool Handle_Write PARAMS(( CONN_ID Idx ));
@@ -205,7 +207,7 @@ cb_connserver(int sock, UNUSED short what)
 
                if (ng_ipaddr_af(&Conf_Server[server].dst_addr[0])) {
                        /* more addresses to try... */
-                       New_Server(res, &Conf_Server[server].dst_addr[0]);
+                       New_Server(server, &Conf_Server[server].dst_addr[0]);
                        /* connection to dst_addr[0] is now in progress, so
                         * remove this address... */
                        Conf_Server[server].dst_addr[0] =
@@ -239,8 +241,10 @@ cb_connserver(int sock, UNUSED short what)
 static void
 server_login(CONN_ID idx)
 {
-       Log( LOG_INFO, "Connection %d with \"%s:%d\" established. Now logging in ...", idx,
-                       My_Connections[idx].host, Conf_Server[Conf_GetServer( idx )].port );
+       Log(LOG_INFO,
+           "Connection %d (socket %d) with \"%s:%d\" established. Now logging in ...",
+           idx, My_Connections[idx].sock, My_Connections[idx].host,
+           Conf_Server[Conf_GetServer(idx)].port);
 
        io_event_setcb( My_Connections[idx].sock, cb_clientserver);
        io_event_add( My_Connections[idx].sock, IO_WANTREAD|IO_WANTWRITE);
@@ -417,6 +421,23 @@ Conn_Exit( void )
 } /* Conn_Exit */
 
 
+/**
+ * Close all sockets (file descriptors) of open connections.
+ * This is useful in forked child processes, for example, to make sure that
+ * they don't hold connections open that the main process wants to close.
+ */
+GLOBAL void
+Conn_CloseAllSockets(void)
+{
+       CONN_ID idx;
+
+       for(idx = 0; idx < Pool_Size; idx++) {
+               if(My_Connections[idx].sock > NONE)
+                       close(My_Connections[idx].sock);
+       }
+}
+
+
 static unsigned int
 ports_initlisteners(array *a, const char *listen_addr, void (*func)(int,short))
 {
@@ -458,11 +479,6 @@ Conn_InitListeners( void )
        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;
-       }
-
        assert(Conf_ListenAddress);
 
        /* can't use Conf_ListenAddress directly, see below */
@@ -501,9 +517,6 @@ Conn_ExitListeners( void )
        /* Close down all listening sockets */
        int *fd;
        size_t arraylen;
-#ifdef ZEROCONF
-       Rendezvous_UnregisterListeners( );
-#endif
 
        arraylen = array_length(&My_Listeners, sizeof (int));
        Log(LOG_INFO,
@@ -560,9 +573,7 @@ NewListener(const char *listen_addr, UINT16 Port)
        /* Create new listening socket on specified port */
        ng_ipaddr_t addr;
        int sock, af;
-#ifdef ZEROCONF
-       char name[CLIENT_ID_LEN], *info;
-#endif
+
        if (!InitSinaddrListenAddr(&addr, listen_addr, Port))
                return -1;
 
@@ -598,38 +609,8 @@ NewListener(const char *listen_addr, UINT16 Port)
                return -1;
        }
 
-       Log(LOG_INFO, "Now listening on [%s]:%d (socket %d).", ng_ipaddr_tostr(&addr), Port, sock);
-
-#ifdef ZEROCONF
-       /* Get best server description text */
-       if( ! Conf_ServerInfo[0] ) info = Conf_ServerName;
-       else
-       {
-               /* Use server info string */
-               info = NULL;
-               if( Conf_ServerInfo[0] == '[' )
-               {
-                       /* Cut off leading hostname part in "[]" */
-                       info = strchr( Conf_ServerInfo, ']' );
-                       if( info )
-                       {
-                               info++;
-                               while( *info == ' ' ) info++;
-                       }
-               }
-               if( ! info ) info = Conf_ServerInfo;
-       }
-
-       /* Add port number to description if non-standard */
-       if (Port != 6667)
-               snprintf(name, sizeof name, "%s (port %u)", info,
-                        (unsigned int)Port);
-       else
-               strlcpy(name, info, sizeof name);
-
-       /* Register service */
-       Rendezvous_Register( name, MDNS_TYPE, Port );
-#endif
+       Log(LOG_INFO, "Now listening on [%s]:%d (socket %d).",
+           ng_ipaddr_tostr(&addr), Port, sock);
        return sock;
 } /* NewListener */
 
@@ -667,9 +648,11 @@ SSL_WantWrite(const CONNECTION *c)
 }
 #else
 static inline bool
-SSL_WantRead(UNUSED const CONNECTION *c) { return false; }
+SSL_WantRead(UNUSED const CONNECTION *c)
+{ return false; }
 static inline bool
-SSL_WantWrite(UNUSED const CONNECTION *c) { return false; }
+SSL_WantWrite(UNUSED const CONNECTION *c)
+{ return false; }
 #endif
 
 
@@ -692,14 +675,6 @@ Conn_Handler(void)
        while (!NGIRCd_SignalQuit && !NGIRCd_SignalRestart) {
                t = time(NULL);
 
-#ifdef ZEROCONF
-               Rendezvous_Handler();
-#endif
-
-               /* Should the configuration be reloaded? */
-               if (NGIRCd_SignalRehash)
-                       NGIRCd_Rehash();
-
                /* Check configured servers and established links */
                Check_Servers();
                Check_Connections();
@@ -752,8 +727,9 @@ Conn_Handler(void)
                        if (SSL_WantWrite(&My_Connections[i]))
                                continue; /* TLS/SSL layer needs to write data; deal with this first */
 #endif
-                       if (Resolve_INPROGRESS(&My_Connections[i].res_stat)) {
-                               /* Wait for completion of resolver sub-process ... */
+                       if (Proc_InProgress(&My_Connections[i].proc_stat)) {
+                               /* Wait for completion of forked subprocess
+                                * and ignore the socket in the meantime ... */
                                io_event_del(My_Connections[i].sock,
                                             IO_WANTREAD);
                                continue;
@@ -769,6 +745,7 @@ Conn_Handler(void)
                                             IO_WANTREAD);
                                continue;
                        }
+
                        io_event_add(My_Connections[i].sock, IO_WANTREAD);
                }
 
@@ -809,12 +786,12 @@ Conn_Handler(void)
  */
 #ifdef PROTOTYPES
 GLOBAL bool
-Conn_WriteStr( CONN_ID Idx, char *Format, ... )
+Conn_WriteStr(CONN_ID Idx, const char *Format, ...)
 #else
 GLOBAL bool 
-Conn_WriteStr( Idx, Format, va_alist )
+Conn_WriteStr(Idx, Format, va_alist)
 CONN_ID Idx;
-char *Format;
+const char *Format;
 va_dcl
 #endif
 {
@@ -884,7 +861,7 @@ static bool
 Conn_Write( CONN_ID Idx, char *Data, size_t Len )
 {
        CLIENT *c;
-       size_t writebuf_limit = WRITEBUFFER_LEN;
+       size_t writebuf_limit = WRITEBUFFER_MAX_LEN;
        assert( Idx > NONE );
        assert( Data != NULL );
        assert( Len > 0 );
@@ -920,7 +897,7 @@ Conn_Write( CONN_ID Idx, char *Data, size_t Len )
                /* Uncompressed link:
                 * Check if outbound buffer has enough space for the data. */
                if (array_bytes(&My_Connections[Idx].wbuf) + Len >=
-                   writebuf_limit) {
+                   WRITEBUFFER_FLUSH_LEN) {
                        /* Buffer is full, flush it. Handle_Write deals with
                         * low-level errors, if any. */
                        if (!Handle_Write(Idx))
@@ -932,8 +909,8 @@ Conn_Write( CONN_ID Idx, char *Data, size_t Len )
                if (array_bytes(&My_Connections[Idx].wbuf) + Len >=
                    writebuf_limit) {
                        Log(LOG_NOTICE,
-                           "Write buffer overflow (connection %d, size %lu byte)!",
-                           Idx,
+                           "Write buffer overflow (connection %d, limit is %lu bytes, %lu bytes new, %lu bytes pending)!",
+                           Idx, writebuf_limit, Len,
                            (unsigned long)array_bytes(&My_Connections[Idx].wbuf));
                        Conn_Close(Idx, "Write buffer overflow!", NULL, false);
                        return false;
@@ -1006,7 +983,7 @@ Conn_Close( CONN_ID Idx, const char *LogMsg, const char *FwdMsg, bool InformClie
                if (FwdMsg)
                        Conn_WriteStr(Idx, "ERROR :%s", FwdMsg);
                else
-                       Conn_WriteStr(Idx, "ERROR :Closing connection.");
+                       Conn_WriteStr(Idx, "ERROR :Closing connection");
        }
 
        /* Try to write out the write buffer. Note: Handle_Write() eventually
@@ -1070,10 +1047,6 @@ Conn_Close( CONN_ID Idx, const char *LogMsg, const char *FwdMsg, bool InformClie
                    in_k, out_k);
        }
 
-       /* cancel running resolver */
-       if (Resolve_INPROGRESS(&My_Connections[Idx].res_stat))
-               Resolve_Shutdown(&My_Connections[Idx].res_stat);
-
        /* Servers: Modify time of next connect attempt? */
        Conf_UnsetServer( Idx );
 
@@ -1122,32 +1095,32 @@ Conn_CountAccepted(void)
 } /* Conn_CountAccepted */
 
 
+/**
+ * Synchronize established connections and configured server structures
+ * after a configuration update and store the correct connection IDs, if any.
+ */
 GLOBAL void
-Conn_SyncServerStruct( void )
+Conn_SyncServerStruct(void)
 {
-       /* Synchronize server structures (connection IDs):
-        * connections <-> configuration */
-
        CLIENT *client;
        CONN_ID i;
        int c;
 
-       for( i = 0; i < Pool_Size; i++ ) {
-               /* Established connection? */
-               if (My_Connections[i].sock < 0)
+       for (i = 0; i < Pool_Size; i++) {
+               if (My_Connections[i].sock == NONE)
                        continue;
 
-               /* Server connection? */
-               client = Conn_GetClient( i );
-               if(( ! client ) || ( Client_Type( client ) != CLIENT_SERVER )) continue;
+               /* Server link? */
+               client = Conn_GetClient(i);
+               if (!client || Client_Type(client) != CLIENT_SERVER)
+                       continue;
 
-               for( c = 0; c < MAX_SERVERS; c++ )
-               {
+               for (c = 0; c < MAX_SERVERS; c++) {
                        /* Configured server? */
-                       if( ! Conf_Server[c].host[0] ) continue;
+                       if (!Conf_Server[c].host[0])
+                               continue;
 
-                       /* Duplicate? */
-                       if( strcmp( Conf_Server[c].name, Client_ID( client )) == 0 )
+                       if (strcasecmp(Conf_Server[c].name, Client_ID(client)) == 0)
                                Conf_Server[c].conn_id = i;
                }
        }
@@ -1306,7 +1279,7 @@ New_Connection(int Sock)
                    "Refused connection from %s: too may connections (%ld) from this IP address!",
                    ip_str, cnt);
                Simple_Message(new_sock,
-                              "ERROR :Connection refused, too many connections from your IP address!");
+                              "ERROR :Connection refused, too many connections from your IP address");
                close(new_sock);
                return -1;
        }
@@ -1370,18 +1343,13 @@ New_Connection(int Sock)
 
        identsock = new_sock;
 #ifdef IDENTAUTH
-       if (Conf_NoIdent)
+       if (!Conf_Ident)
                identsock = -1;
 #endif
-       if (!Conf_NoDNS)
-               Resolve_Addr(&My_Connections[new_sock].res_stat, &new_addr,
+       if (Conf_DNS)
+               Resolve_Addr(&My_Connections[new_sock].proc_stat, &new_addr,
                             identsock, cb_Read_Resolver_Result);
 
-       /* ngIRCd waits up to 4 seconds for the result of the asynchronous
-        * DNS and IDENT resolver subprocess using the "penalty" mechanism.
-        * If there are results earlier, the delay is aborted. */
-       Conn_SetPenalty(new_sock, 4);
-
        Account_Connection();
        return new_sock;
 } /* New_Connection */
@@ -1490,27 +1458,34 @@ Read_Request( CONN_ID Idx )
 
        /* Update connection statistics */
        My_Connections[Idx].bytes_in += len;
+       My_Connections[Idx].bps += Handle_Buffer(Idx);
+
+       /* Make sure that there is still a valid client registered */
+       c = Conn_GetClient(Idx);
+       if (!c)
+               return;
 
        /* Update timestamp of last data received if this connection is
         * registered as a user, server or service connection. Don't update
         * otherwise, so users have at least Conf_PongTimeout seconds time to
         * register with the IRC server -- see Check_Connections().
-        * Set "lastping", too, so we can handle time shifts backwards ... */
-       c = Conn_GetClient(Idx);
-       if (c && (Client_Type(c) == CLIENT_USER
-                 || Client_Type(c) == CLIENT_SERVER
-                 || Client_Type(c) == CLIENT_SERVICE)) {
+        * Update "lastping", too, if time shifted backwards ... */
+       if (Client_Type(c) == CLIENT_USER
+           || Client_Type(c) == CLIENT_SERVER
+           || Client_Type(c) == CLIENT_SERVICE) {
                t = time(NULL);
                if (My_Connections[Idx].lastdata != t)
                        My_Connections[Idx].bps = 0;
 
                My_Connections[Idx].lastdata = t;
-               My_Connections[Idx].lastping = My_Connections[Idx].lastdata;
+               if (My_Connections[Idx].lastping > t)
+                       My_Connections[Idx].lastping = t;
        }
 
        /* Look at the data in the (read-) buffer of this connection */
-       My_Connections[Idx].bps += Handle_Buffer(Idx);
        if (Client_Type(c) != CLIENT_SERVER
+           && Client_Type(c) != CLIENT_UNKNOWNSERVER
+           && Client_Type(c) != CLIENT_SERVICE
            && My_Connections[Idx].bps >= maxbps) {
                LogDebug("Throttling connection %d: BPS exceeded! (%u >= %u)",
                         Idx, My_Connections[Idx].bps, maxbps);
@@ -1522,7 +1497,7 @@ Read_Request( CONN_ID Idx )
 /**
  * Handle all data in the connection read-buffer.
  * Data is processed until no complete command is left in the read buffer,
- * or MAX_COMMANDS[_SERVER] commands were processed.
+ * or MAX_COMMANDS[_SERVER|_SERVICE] commands were processed.
  * When a fatal error occurs, the connection is shut down.
  * @param Idx Index of the connection.
  * @return number of bytes processed.
@@ -1547,8 +1522,12 @@ Handle_Buffer(CONN_ID Idx)
 
        /* Servers do get special command limits, so they can process
         * all the messages that are required while peering. */
-       if (Client_Type(c) == CLIENT_SERVER)
-               maxcmd = MAX_COMMANDS_SERVER;
+       switch (Client_Type(c)) {
+           case CLIENT_SERVER:
+               maxcmd = MAX_COMMANDS_SERVER; break;
+           case CLIENT_SERVICE:
+               maxcmd = MAX_COMMANDS_SERVICE; break;
+       }
 
        starttime = time(NULL);
        for (i=0; i < maxcmd; i++) {
@@ -1673,11 +1652,14 @@ Handle_Buffer(CONN_ID Idx)
 } /* Handle_Buffer */
 
 
+/**
+ * Check whether established connections are still alive or not.
+ * If not, play PING-PONG first; and if that doesn't help either,
+ * disconnect the respective peer.
+ */
 static void
 Check_Connections(void)
 {
-       /* check if connections are alive. if not, play PING-PONG first.
-        * if this doesn't help either, disconnect client. */
        CLIENT *c;
        CONN_ID i;
        char msg[64];
@@ -1729,42 +1711,50 @@ Check_Connections(void)
 } /* Check_Connections */
 
 
+/**
+ * Check if further server links should be established.
+ */
 static void
-Check_Servers( void )
+Check_Servers(void)
 {
-       /* Check if we can establish further server links */
-
        int i, n;
        time_t time_now;
 
+       time_now = time(NULL);
+
        /* Check all configured servers */
-       for( i = 0; i < MAX_SERVERS; i++ ) {
-               /* Valid outgoing server which isn't already connected or disabled? */
-               if(( ! Conf_Server[i].host[0] ) || ( ! Conf_Server[i].port > 0 ) ||
-                       ( Conf_Server[i].conn_id > NONE ) || ( Conf_Server[i].flags & CONF_SFLAG_DISABLED ))
-                               continue;
+       for (i = 0; i < MAX_SERVERS; i++) {
+               if (Conf_Server[i].conn_id != NONE)
+                       continue;       /* Already establishing or connected */
+               if (!Conf_Server[i].host[0] || !Conf_Server[i].port > 0)
+                       continue;       /* No host and/or port configured */
+               if (Conf_Server[i].flags & CONF_SFLAG_DISABLED)
+                       continue;       /* Disabled configuration entry */
+               if (Conf_Server[i].lasttry > (time_now - Conf_ConnectRetry))
+                       continue;       /* We have to wait a little bit ... */
 
                /* Is there already a connection in this group? */
-               if( Conf_Server[i].group > NONE ) {
+               if (Conf_Server[i].group > NONE) {
                        for (n = 0; n < MAX_SERVERS; n++) {
-                               if (n == i) continue;
+                               if (n == i)
+                                       continue;
                                if ((Conf_Server[n].conn_id != NONE) &&
-                                       (Conf_Server[n].group == Conf_Server[i].group))
-                                               break;
+                                   (Conf_Server[n].group == Conf_Server[i].group))
+                                       break;
                        }
-                       if (n < MAX_SERVERS) continue;
+                       if (n < MAX_SERVERS)
+                               continue;
                }
 
-               /* Check last connect attempt? */
-               time_now = time(NULL);
-               if( Conf_Server[i].lasttry > (time_now - Conf_ConnectRetry))
-                       continue;
-
                /* Okay, try to connect now */
+               Log(LOG_NOTICE,
+                   "Preparing to establish a new server link for \"%s\" ...",
+                   Conf_Server[i].name);
                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);
+               assert(Proc_GetPipeFd(&Conf_Server[i].res_stat) < 0);
+               Resolve_Name(&Conf_Server[i].res_stat, Conf_Server[i].host,
+                            cb_Connect_to_Server);
        }
 } /* Check_Servers */
 
@@ -1784,13 +1774,17 @@ New_Server( int Server , ng_ipaddr_t *dest)
                return;
        }
 
-       Log( LOG_INFO, "Establishing connection to \"%s\", %s, port %d ... ",
-                       Conf_Server[Server].host, ip_str, Conf_Server[Server].port );
-
        af_dest = ng_ipaddr_af(dest);
        new_sock = socket(af_dest, SOCK_STREAM, 0);
+
+       Log(LOG_INFO,
+           "Establishing connection for \"%s\" to \"%s:%d\" (%s), socket %d ...",
+           Conf_Server[Server].name, Conf_Server[Server].host,
+           Conf_Server[Server].port, ip_str, new_sock);
+
        if (new_sock < 0) {
-               Log( LOG_CRIT, "Can't create socket (af %d) : %s!", af_dest, strerror( errno ));
+               Log(LOG_CRIT, "Can't create socket (af %d): %s!",
+                   af_dest, strerror(errno));
                return;
        }
 
@@ -1886,7 +1880,7 @@ Init_Conn_Struct(CONN_ID Idx)
        My_Connections[Idx].signon = now;
        My_Connections[Idx].lastdata = now;
        My_Connections[Idx].lastprivmsg = now;
-       Resolve_Init(&My_Connections[Idx].res_stat);
+       Proc_InitStruct(&My_Connections[Idx].proc_stat);
 } /* Init_Conn_Struct */
 
 
@@ -1917,8 +1911,8 @@ Init_Socket( int Sock )
        LogDebug("Setting IP_TOS on socket %d to IPTOS_LOWDELAY.", Sock);
        if (setsockopt(Sock, IPPROTO_IP, IP_TOS, &value,
                       (socklen_t) sizeof(value))) {
-               Log(LOG_ERR, "Can't set socket option IP_TOS: %s!",
-                   strerror(errno));
+               LogDebug("Can't set socket option IP_TOS: %s!",
+                        strerror(errno));
                /* ignore this error */
        }
 #endif
@@ -1941,7 +1935,7 @@ cb_Connect_to_Server(int fd, UNUSED short events)
        LogDebug("Resolver: Got forward lookup callback on fd %d, events %d", fd, events);
 
        for (i=0; i < MAX_SERVERS; i++) {
-                 if (Resolve_Getfd(&Conf_Server[i].res_stat) == fd )
+                 if (Proc_GetPipeFd(&Conf_Server[i].res_stat) == fd )
                          break;
        }
 
@@ -1953,9 +1947,12 @@ cb_Connect_to_Server(int fd, UNUSED short events)
        }
 
        /* Read result from pipe */
-       len = Resolve_Read(&Conf_Server[i].res_stat, dest_addrs, sizeof(dest_addrs));
-       if (len == 0)
+       len = Proc_Read(&Conf_Server[i].res_stat, dest_addrs, sizeof(dest_addrs));
+       if (len == 0) {
+               /* Error resolving hostname: reset server structure */
+               Conf_Server[i].conn_id = NONE;
                return;
+       }
 
        assert((len % sizeof(ng_ipaddr_t)) == 0);
 
@@ -1986,7 +1983,7 @@ cb_Read_Resolver_Result( int r_fd, UNUSED short events )
         * IDENT user name.*/
 
        CLIENT *c;
-       int i;
+       CONN_ID i;
        size_t len;
        char *identptr;
 #ifdef IDENTAUTH
@@ -1996,14 +1993,8 @@ cb_Read_Resolver_Result( int r_fd, UNUSED short events )
 #endif
 
        LogDebug("Resolver: Got callback on fd %d, events %d", r_fd, events );
-
-       /* Search associated connection ... */
-       for( i = 0; i < Pool_Size; i++ ) {
-               if(( My_Connections[i].sock != NONE )
-                 && ( Resolve_Getfd(&My_Connections[i].res_stat) == r_fd ))
-                       break;
-       }
-       if( i >= Pool_Size ) {
+       i = Conn_GetFromProc(r_fd);
+       if (i == NONE) {
                /* Ops, none found? Probably the connection has already
                 * been closed!? We'll ignore that ... */
                io_close( r_fd );
@@ -2012,7 +2003,7 @@ cb_Read_Resolver_Result( int r_fd, UNUSED short events )
        }
 
        /* Read result from pipe */
-       len = Resolve_Read(&My_Connections[i].res_stat, readbuf, sizeof readbuf -1);
+       len = Proc_Read(&My_Connections[i].proc_stat, readbuf, sizeof readbuf -1);
        if (len == 0)
                return;
 
@@ -2055,8 +2046,6 @@ cb_Read_Resolver_Result( int r_fd, UNUSED short events )
 #ifdef DEBUG
                else Log( LOG_DEBUG, "Resolver: discarding result for already registered connection %d.", i );
 #endif
-       /* Reset penalty time */
-       Conn_ResetPenalty( i );
 } /* cb_Read_Resolver_Result */
 
 
@@ -2107,6 +2096,42 @@ Conn_GetClient( CONN_ID Idx )
        return c ? c->client : NULL;
 }
 
+/**
+ * Get PROC_STAT sub-process structure of a connection.
+ * @param Idx Connection index number
+ * @return PROC_STAT structure
+ */
+GLOBAL PROC_STAT *
+Conn_GetProcStat(CONN_ID Idx)
+{
+       CONNECTION *c;
+
+       assert(Idx >= 0);
+       c = array_get(&My_ConnArray, sizeof (CONNECTION), (size_t)Idx);
+       assert(c != NULL);
+       return &c->proc_stat;
+} /* Conn_GetProcStat */
+
+
+/**
+ * Get CONN_ID from file descriptor associated to a subprocess structure.
+ * @param fd File descriptor
+ * @return CONN_ID or NONE (-1)
+ */
+GLOBAL CONN_ID
+Conn_GetFromProc(int fd)
+{
+       int i;
+
+       assert(fd > 0);
+       for (i = 0; i < Pool_Size; i++) {
+               if ((My_Connections[i].sock != NONE)
+                   && (Proc_GetPipeFd(&My_Connections[i].proc_stat) == fd))
+                       return i;
+       }
+       return NONE;
+} /* Conn_GetFromProc */
+
 
 #ifdef SSL_SUPPORT
 
@@ -2144,4 +2169,28 @@ Conn_UsesSSL(CONN_ID Idx)
 #endif
 
 
+#ifdef DEBUG
+
+GLOBAL void
+Conn_DebugDump(void)
+{
+       int i;
+
+       Log(LOG_DEBUG, "Connection status:");
+       for (i = 0; i < Pool_Size; i++) {
+               if (My_Connections[i].sock == NONE)
+                       continue;
+               Log(LOG_DEBUG,
+                   " - %d: host=%s, lastdata=%ld, lastping=%ld, delaytime=%ld, flag=%d, options=%d, bps=%d, client=%s",
+                   My_Connections[i].sock, My_Connections[i].host,
+                   My_Connections[i].lastdata, My_Connections[i].lastping,
+                   My_Connections[i].delaytime, My_Connections[i].flag,
+                   My_Connections[i].options, My_Connections[i].bps,
+                   My_Connections[i].client ? Client_ID(My_Connections[i].client) : "-");
+       }
+} /* Conn_DumpClients */
+
+#endif
+
+
 /* -eof- */