X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd.git;a=blobdiff_plain;f=src%2Fngircd%2Fconn.c;h=c6095a31c613bc5ca127d55b8723e15b836f1cca;hp=1696d5c5f62968d8d495576b4e1bc67947db66ee;hb=627b0b713c52406e50c84bb9459e7794262920a2;hpb=bb6ee46892e94a8c7aae9eaa89981343fbd6863d diff --git a/src/ngircd/conn.c b/src/ngircd/conn.c index 1696d5c5..c6095a31 100644 --- a/src/ngircd/conn.c +++ b/src/ngircd/conn.c @@ -15,10 +15,9 @@ #define CONN_MODULE #include "portab.h" +#include "conf-ssl.h" #include "io.h" -static char UNUSED id[] = "$Id: conn.c,v 1.221 2008/02/26 22:04:17 fw Exp $"; - #include "imp.h" #include #ifdef PROTOTYPES @@ -60,6 +59,7 @@ static char UNUSED id[] = "$Id: conn.c,v 1.221 2008/02/26 22:04:17 fw Exp $"; #include "ngircd.h" #include "client.h" #include "conf.h" +#include "conn-ssl.h" #include "conn-zip.h" #include "conn-func.h" #include "log.h" @@ -81,17 +81,18 @@ 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 )); -static bool Handle_Buffer PARAMS(( CONN_ID Idx )); +static void Handle_Buffer PARAMS(( CONN_ID Idx )); static void Check_Connections PARAMS(( void )); static void Check_Servers PARAMS(( void )); 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; @@ -100,6 +101,11 @@ int deny_severity = LOG_ERR; static void server_login PARAMS((CONN_ID idx)); +#ifdef SSL_SUPPORT +extern struct SSLOptions Conf_SSLOptions; +static void cb_connserver_login_ssl PARAMS((int sock, short what)); +static void cb_clientserver_ssl PARAMS((int sock, short what)); +#endif static void cb_Read_Resolver_Result PARAMS(( int sock, UNUSED short what)); static void cb_Connect_to_Server PARAMS(( int sock, UNUSED short what)); static void cb_clientserver PARAMS((int sock, short what)); @@ -108,8 +114,27 @@ static void cb_listen(int sock, short irrelevant) { (void) irrelevant; - New_Connection( sock ); + if (New_Connection( sock ) >= 0) + NumConnections++; + LogDebug("Total number of connections now %ld.", NumConnections); +} + + +#ifdef SSL_SUPPORT +static void +cb_listen_ssl(int sock, short irrelevant) +{ + int fd; + (void) irrelevant; + fd = New_Connection(sock); + if (fd < 0) + return; + + NumConnections++; + LogDebug("Total number of connections now %ld.", NumConnections); + io_event_setcb(My_Connections[fd].sock, cb_clientserver_ssl); } +#endif static void @@ -166,6 +191,13 @@ cb_connserver(int sock, UNUSED short what) if (res >= 0) /* connect succeeded, remove all additional addresses */ memset(&Conf_Server[res].dst_addr, 0, sizeof(&Conf_Server[res].dst_addr)); Conn_OPTION_DEL( &My_Connections[idx], CONN_ISCONNECTING ); +#ifdef SSL_SUPPORT + if ( Conn_OPTION_ISSET( &My_Connections[idx], CONN_SSL_CONNECT )) { + io_event_setcb( sock, cb_connserver_login_ssl ); + io_event_add( sock, IO_WANTWRITE|IO_WANTREAD ); + return; + } +#endif server_login(idx); } @@ -185,41 +217,101 @@ server_login(CONN_ID idx) } +#ifdef SSL_SUPPORT +static void +cb_connserver_login_ssl(int sock, short unused) +{ + CONN_ID idx = Socket2Index(sock); + + assert(idx >= 0); + if (idx < 0) { + io_close(sock); + return; + } + (void) unused; + switch (ConnSSL_Connect( &My_Connections[idx])) { + case 1: break; + case 0: LogDebug("ConnSSL_Connect: not ready"); + return; + case -1: + Log(LOG_ERR, "SSL connection on socket %d failed!", sock); + Conn_Close(idx, "Can't connect!", NULL, false); + return; + } + + Log( LOG_INFO, "SSL connection %d with \"%s:%d\" established.", idx, + My_Connections[idx].host, Conf_Server[Conf_GetServer( idx )].port ); + + server_login(idx); +} +#endif + + static void cb_clientserver(int sock, short what) { - CONN_ID idx = Socket2Index( sock ); - if (idx <= NONE) { -#ifdef DEBUG - Log(LOG_WARNING, "WTF: cb_clientserver wants to write on unknown socket?!"); + CONN_ID idx = Socket2Index(sock); + + assert(idx >= 0); + + if (idx < 0) { + io_close(sock); + return; + } +#ifdef SSL_SUPPORT + if (what & IO_WANTREAD || (Conn_OPTION_ISSET(&My_Connections[idx], CONN_SSL_WANT_WRITE))) + Read_Request( idx ); /* if TLS layer needs to write additional data, call Read_Request instead so SSL/TLS can continue */ +#else + if (what & IO_WANTREAD) + Read_Request( idx ); #endif + if (what & IO_WANTWRITE) + Handle_Write( idx ); +} + + +#ifdef SSL_SUPPORT +static void +cb_clientserver_ssl(int sock, short what) +{ + CONN_ID idx = Socket2Index(sock); + + assert(idx >= 0); + + if (idx < 0) { io_close(sock); return; } + switch (ConnSSL_Accept(&My_Connections[idx])) { + case 1: break; /* OK */ + case 0: return; /* EAGAIN: this callback will be invoked again by the io layer */ + default: + Conn_Close( idx, "Socket closed!", "SSL accept error", false ); + return; + } if (what & IO_WANTREAD) - Read_Request( idx ); + Read_Request(idx); if (what & IO_WANTWRITE) - Handle_Write( idx ); + Handle_Write(idx); + + io_event_setcb(sock, cb_clientserver); /* SSL handshake completed */ } +#endif GLOBAL void Conn_Init( void ) { - /* Modul initialisieren: statische Strukturen "ausnullen". */ - CONN_ID i; /* 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 ); @@ -247,16 +339,11 @@ Conn_Init( void ) GLOBAL void Conn_Exit( void ) { - /* Modul abmelden: alle noch offenen Connections - * schliessen und freigeben. */ - CONN_ID idx; - LogDebug("Shutting down all connections ..." ); - Conn_ExitListeners(); - /* Sockets schliessen */ + LogDebug("Shutting down all connections ..." ); for( idx = 0; idx < Pool_Size; idx++ ) { if( My_Connections[idx].sock > NONE ) { Conn_Close( idx, NULL, NGIRCd_SignalRestart ? @@ -272,7 +359,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 +368,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 +384,6 @@ ports_initlisteners(array *a, int af, void (*func)(int,short)) created++; port++; } - return created; } @@ -306,21 +392,43 @@ 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); + 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); +#ifdef SSL_SUPPORT + created += ports_initlisteners(&Conf_SSLOptions.ListenPorts, listen_addr, cb_listen_ssl); #endif - if (Conf_ListenIPv4) - created += ports_initlisteners(&Conf_ListenPorts, AF_INET, 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 */ @@ -336,7 +444,8 @@ Conn_ExitListeners( void ) #endif arraylen = array_length(&My_Listeners, sizeof (int)); - Log( LOG_INFO, "Shutting down all listening sockets (%d total)...", arraylen ); + Log(LOG_INFO, + "Shutting down all listening sockets (%d total) ...", arraylen); fd = array_start(&My_Listeners); while(arraylen--) { assert(fd != NULL); @@ -350,25 +459,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 +493,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 +536,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,105 +571,157 @@ NewListener(int af, const UINT16 Port) return sock; } /* NewListener */ +#ifdef SSL_SUPPORT +/* + * SSL/TLS connections require extra treatment: + * When either CONN_SSL_WANT_WRITE or CONN_SSL_WANT_READ is set, we + * need to take care of that first, before checking read/write buffers. + * For instance, while we might have data in our write buffer, the + * TLS/SSL protocol might need to read internal data first for TLS/SSL + * writes to succeed. + * + * If this function returns true, such a condition is met and we have + * to reverse the condition (check for read even if we've data to write, + * do not check for read but writeability even if write-buffer is empty). + */ +static bool +SSL_WantRead(const CONNECTION *c) +{ + if (Conn_OPTION_ISSET(c, CONN_SSL_WANT_READ)) { + io_event_add(c->sock, IO_WANTREAD); + return true; + } + return false; +} +static bool +SSL_WantWrite(const CONNECTION *c) +{ + if (Conn_OPTION_ISSET(c, CONN_SSL_WANT_WRITE)) { + io_event_add(c->sock, IO_WANTWRITE); + return true; + } + return false; +} +#else +static inline bool +SSL_WantRead(UNUSED const CONNECTION *c) { return false; } +static inline bool +SSL_WantWrite(UNUSED const CONNECTION *c) { return false; } +#endif + +/** + * "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 ); + if (SSL_WantRead(&My_Connections[i])) + continue; + 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; - +#ifdef SSL_SUPPORT + 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 */ - 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 */ @@ -734,13 +881,13 @@ Conn_Write( CONN_ID Idx, char *Data, size_t Len ) GLOBAL void -Conn_Close( CONN_ID Idx, char *LogMsg, char *FwdMsg, bool InformClient ) +Conn_Close( CONN_ID Idx, const char *LogMsg, const char *FwdMsg, bool InformClient ) { /* Close connection. Open pipes of asyncronous resolver * sub-processes are closed down. */ CLIENT *c; - char *txt; + const char *txt; double in_k, out_k; UINT16 port; #ifdef ZLIB @@ -805,7 +952,12 @@ Conn_Close( CONN_ID Idx, char *LogMsg, char *FwdMsg, bool InformClient ) /* Search client, if any (re-check!) */ c = Conn_GetClient( Idx ); - +#ifdef SSL_SUPPORT + if ( Conn_OPTION_ISSET( &My_Connections[Idx], CONN_SSL )) { + Log(LOG_INFO, "SSL connection %d shutting down ...", Idx); + ConnSSL_Free(&My_Connections[Idx]); + } +#endif /* Shut down socket */ if (! io_close(My_Connections[Idx].sock)) { /* Oops, we can't close the socket!? This is ... ugly! */ @@ -876,7 +1028,11 @@ 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, %ld connection%s left.", + Idx, NumConnections, NumConnections != 1 ? "s" : ""); } /* Conn_Close */ @@ -952,9 +1108,15 @@ Handle_Write( CONN_ID Idx ) ("Handle_Write() called for connection %d, %ld bytes pending ...", Idx, wdatalen); - len = write(My_Connections[Idx].sock, - array_start(&My_Connections[Idx].wbuf), wdatalen ); - +#ifdef SSL_SUPPORT + if ( Conn_OPTION_ISSET( &My_Connections[Idx], CONN_SSL )) { + len = ConnSSL_Write(&My_Connections[Idx], array_start(&My_Connections[Idx].wbuf), wdatalen); + } else +#endif + { + len = write(My_Connections[Idx].sock, + array_start(&My_Connections[Idx].wbuf), wdatalen ); + } if( len < 0 ) { if (errno == EAGAIN || errno == EINTR) return true; @@ -999,7 +1161,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, identsock; CLIENT *c; long cnt; @@ -1018,6 +1180,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 +1209,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 +1231,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++); } @@ -1095,18 +1256,29 @@ New_Connection( int Sock ) My_Connections[new_sock].addr = new_addr; My_Connections[new_sock].client = c; - Log( LOG_INFO, "Accepted connection %d from %s:%d on socket %d.", new_sock, - ip_str, ng_ipaddr_getport(&new_addr), Sock); - - /* Hostnamen ermitteln */ - strlcpy(My_Connections[new_sock].host, ip_str, sizeof(My_Connections[new_sock].host)); + /* Set initial hostname to IP address. This becomes overwritten when + * the DNS lookup is enabled and succeeds, but is used otherwise. */ + if (ng_ipaddr_af(&new_addr) != AF_INET) + snprintf(My_Connections[new_sock].host, + sizeof(My_Connections[new_sock].host), "[%s]", ip_str); + else + strlcpy(My_Connections[new_sock].host, ip_str, + sizeof(My_Connections[new_sock].host)); Client_SetHostname(c, My_Connections[new_sock].host); + Log(LOG_INFO, "Accepted connection %d from %s:%d on socket %d.", + new_sock, My_Connections[new_sock].host, + ng_ipaddr_getport(&new_addr), Sock); + + identsock = new_sock; +#ifdef IDENTAUTH + if (Conf_NoIdent) + identsock = -1; +#endif if (!Conf_NoDNS) Resolve_Addr(&My_Connections[new_sock].res_stat, &new_addr, - My_Connections[new_sock].sock, cb_Read_Resolver_Result); - + identsock, cb_Read_Resolver_Result); Conn_SetPenalty(new_sock, 4); return new_sock; } /* New_Connection */ @@ -1115,13 +1287,11 @@ New_Connection( int Sock ) static CONN_ID Socket2Index( int Sock ) { - /* zum Socket passende Connection suchen */ - assert( Sock >= 0 ); if( Sock >= Pool_Size || My_Connections[Sock].sock != Sock ) { - /* die Connection wurde vermutlich (wegen eines - * Fehlers) bereits wieder abgebaut ... */ + /* the Connection was already closed again, likely due to + * an error. */ LogDebug("Socket2Index: can't get connection for socket %d!", Sock); return NONE; } @@ -1157,6 +1327,11 @@ Read_Request( CONN_ID Idx ) return; } +#ifdef SSL_SUPPORT + if (Conn_OPTION_ISSET(&My_Connections[Idx], CONN_SSL)) + len = ConnSSL_Read( &My_Connections[Idx], readbuf, sizeof(readbuf)); + else +#endif len = read(My_Connections[Idx].sock, readbuf, sizeof(readbuf)); if (len == 0) { Log(LOG_INFO, "%s:%u (%s) is closing the connection ...", @@ -1218,108 +1393,143 @@ Read_Request( CONN_ID Idx ) } /* Read_Request */ -static bool -Handle_Buffer( CONN_ID Idx ) +/** + * Handle all data in the connection read-buffer. + * All data is precessed until no complete command is left. When a fatal + * error occurs, the connection is shut down. + */ +static void +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; - bool result; time_t starttime; #ifdef ZLIB bool old_z; #endif starttime = time(NULL); - result = false; for (;;) { /* Check penalty */ - if( My_Connections[Idx].delaytime > starttime) return result; + if (My_Connections[Idx].delaytime > starttime) + return; #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)) { + /* When unzipping fails, Unzip_Buffer() shuts + * down the connection itself */ + if (!Unzip_Buffer(Idx)) + return; + } #endif if (0 == array_bytes(&My_Connections[Idx].rbuf)) - break; + return; - if (!array_cat0_temporary(&My_Connections[Idx].rbuf)) /* make sure buf is NULL terminated */ - return false; + /* Make sure that the buffer is NULL terminated */ + if (!array_cat0_temporary(&My_Connections[Idx].rbuf)) { + Conn_Close(Idx, NULL, + "Can't allocate memory [Handle_Buffer]", + true); + return; + } - /* 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' ); + /* 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 - if( ! ptr ) - break; + if (!ptr) + return; - /* 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 ); - return false; + 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; } - if (len <= 2) { /* request was empty (only '\r\n') */ - array_moveleft(&My_Connections[Idx].rbuf, 1, delta); /* delta is either 1 or 2 */ - break; + 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); + return; } + #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) )) - return false; - - result = true; + if (!Parse_Request + (Idx, (char *)array_start(&My_Connections[Idx].rbuf))) + return; 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 )) - return false; + 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)) { + Conn_Close(Idx, NULL, + "Can't allocate memory [Handle_Buffer]", + true); + return; + } 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 */ @@ -1330,6 +1540,7 @@ Check_Connections(void) * if this doesn't help either, disconnect client. */ CLIENT *c; CONN_ID i; + char msg[64]; for (i = 0; i < Pool_Size; i++) { if (My_Connections[i].sock < 0) @@ -1349,8 +1560,8 @@ Check_Connections(void) LogDebug ("Connection %d: Ping timeout: %d seconds.", i, Conf_PongTimeout); - Conn_Close(i, NULL, "Ping timeout", - true); + snprintf(msg, sizeof(msg), "Ping timeout: %d seconds", Conf_PongTimeout); + Conn_Close(i, NULL, msg, true); } } else if (My_Connections[i].lastdata < time(NULL) - Conf_PingTimeout) { @@ -1439,7 +1650,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; } @@ -1503,9 +1714,19 @@ New_Server( int Server , ng_ipaddr_t *dest) Init_Conn_Struct( new_sock ); Conf_Server[Server].conn_id = NONE; } - - LogDebug("Registered new connection %d on socket %d.", - new_sock, My_Connections[new_sock].sock ); +#ifdef SSL_SUPPORT + if (Conf_Server[Server].SSLConnect && !ConnSSL_PrepareConnect( &My_Connections[new_sock], + &Conf_Server[Server] )) + { + Log(LOG_ALERT, "Could not initialize SSL for outgoing connection"); + Conn_Close( new_sock, "Could not initialize SSL for outgoing connection", NULL, false ); + Init_Conn_Struct( new_sock ); + Conf_Server[Server].conn_id = NONE; + } +#endif + NumConnections++; + LogDebug("Registered new connection %d on socket %d (%ld in total).", + new_sock, My_Connections[new_sock].sock, NumConnections); Conn_OPTION_ADD( &My_Connections[new_sock], CONN_ISCONNECTING ); } /* New_Server */ @@ -1604,8 +1825,8 @@ cb_Connect_to_Server(int fd, UNUSED short events) len -= sizeof(ng_ipaddr_t); if (len > sizeof(&Conf_Server[i].dst_addr)) { len = sizeof(&Conf_Server[i].dst_addr); - Log(LOG_NOTICE, "Notice: Resolver returned more IP Addresses for host than we can handle," - " additional addresses dropped"); + Log(LOG_NOTICE, + "Notice: Resolver returned more IP Addresses for host than we can handle, additional addresses dropped."); } memcpy(&Conf_Server[i].dst_addr, &dest_addrs[1], len); } @@ -1725,4 +1946,25 @@ Conn_GetClient( CONN_ID Idx ) return c ? c->client : NULL; } +#ifdef SSL_SUPPORT +/* we cannot access My_Connections in irc-info.c */ +GLOBAL bool +Conn_GetCipherInfo(CONN_ID Idx, char *buf, size_t len) +{ + if (Idx < 0) + return false; + assert(Idx < (int) array_length(&My_ConnArray, sizeof(CONNECTION))); + return ConnSSL_GetCipherInfo(&My_Connections[Idx], buf, len); +} + + +GLOBAL bool +Conn_UsesSSL(CONN_ID Idx) +{ + if (Idx < 0) + return false; + assert(Idx < (int) array_length(&My_ConnArray, sizeof(CONNECTION))); + return Conn_OPTION_ISSET(&My_Connections[Idx], CONN_SSL); +} +#endif /* -eof- */