X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd-alex.git;a=blobdiff_plain;f=src%2Fngircd%2Fconn.c;h=f0a97f9cdddec0437f883d7f0716c24f4d6b9286;hp=33c374c318d64686e545bf00b5d8436da6b405f6;hb=3243d9ee441e9cd4338965bac7c2ed3b49a3c2dd;hpb=d36087139482812b6a537e88fa689cca84d4b844 diff --git a/src/ngircd/conn.c b/src/ngircd/conn.c index 33c374c3..f0a97f9c 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,7 +81,7 @@ 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 )); @@ -92,6 +92,7 @@ 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,24 +217,88 @@ 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_INFO, "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 @@ -214,12 +310,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 ); @@ -325,8 +419,12 @@ Conn_InitListeners( void ) while (listen_addr) { ngt_TrimStr(listen_addr); - if (*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 + } listen_addr = strtok(NULL, ","); } @@ -479,6 +577,44 @@ NewListener(const char *listen_addr, 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. @@ -534,7 +670,8 @@ Conn_Handler(void) if (wdatalen > 0) #endif { - /* Set the "WANTWRITE" flag on this socket */ + if (SSL_WantRead(&My_Connections[i])) + continue; io_event_add(My_Connections[i].sock, IO_WANTWRITE); } @@ -544,7 +681,10 @@ Conn_Handler(void) 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, @@ -818,7 +958,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! */ @@ -889,7 +1034,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 */ @@ -965,9 +1114,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; @@ -1012,7 +1167,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; @@ -1031,6 +1186,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 @@ -1059,18 +1215,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]" ); @@ -1083,7 +1237,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++); } @@ -1170,6 +1324,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 ...", @@ -1232,43 +1391,48 @@ Read_Request( CONN_ID Idx ) /** - * Handle data in connection read-buffer. - * @return true if a reuqest was handled, false otherwise (and on errors). + * 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 bool +static void Handle_Buffer(CONN_ID Idx) { #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; + return; #ifdef ZLIB /* 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 false; + return; } #endif if (0 == array_bytes(&My_Connections[Idx].rbuf)) - break; + return; /* Make sure that the buffer is NULL terminated */ - if (!array_cat0_temporary(&My_Connections[Idx].rbuf)) - return false; + if (!array_cat0_temporary(&My_Connections[Idx].rbuf)) { + Conn_Close(Idx, NULL, + "Can't allocate memory [Handle_Buffer]", + true); + return; + } /* RFC 2812, section "2.3 Messages", 5th paragraph: * "IRC messages are always lines of characters terminated @@ -1277,24 +1441,33 @@ Handle_Buffer(CONN_ID Idx) ptr = strstr(array_start(&My_Connections[Idx].rbuf), "\r\n"); #ifndef STRICT_RFC - 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; - 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 ? ptr2 : ptr1; - else if (ptr1) - ptr = ptr1; - else if (ptr2) - ptr = ptr2; + ptr = ptr1 < ptr2 ? ptr1 : ptr2; + else + ptr = ptr1 ? ptr1 : ptr2; + delta = 1; } #endif if (!ptr) - break; + return; /* Complete (=line terminated) request found, handle it! */ *ptr = '\0'; @@ -1309,14 +1482,14 @@ Handle_Buffer(CONN_ID Idx) Idx, array_bytes(&My_Connections[Idx].rbuf), COMMAND_LEN - 1); Conn_Close(Idx, NULL, "Request too long", true); - return false; + return; } 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; + return; } #ifdef ZLIB @@ -1327,9 +1500,7 @@ Handle_Buffer(CONN_ID Idx) My_Connections[Idx].msg_in++; if (!Parse_Request (Idx, (char *)array_start(&My_Connections[Idx].rbuf))) - return false; - - result = true; + return; array_moveleft(&My_Connections[Idx].rbuf, 1, len); LogDebug("Connection %d: %d bytes left in read buffer.", @@ -1342,8 +1513,12 @@ Handle_Buffer(CONN_ID Idx) * to the unzip buffer for decompression: */ if (!array_copy (&My_Connections[Idx].zip.rbuf, - &My_Connections[Idx].rbuf)) - return false; + &My_Connections[Idx].rbuf)) { + Conn_Close(Idx, NULL, + "Can't allocate memory [Handle_Buffer]", + true); + return; + } array_trunc(&My_Connections[Idx].rbuf); LogDebug @@ -1352,7 +1527,6 @@ Handle_Buffer(CONN_ID Idx) } #endif } - return result; } /* Handle_Buffer */ @@ -1536,9 +1710,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 */ @@ -1758,4 +1942,19 @@ 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) +{ + return ConnSSL_GetCipherInfo(&My_Connections[Idx], buf, len); +} + + +GLOBAL bool +Conn_UsesSSL(CONN_ID Idx) +{ + return Conn_OPTION_ISSET(&My_Connections[Idx], CONN_SSL); +} +#endif /* -eof- */