#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 <assert.h>
#ifdef PROTOTYPES
#include "ngircd.h"
#include "client.h"
#include "conf.h"
+#include "conn-ssl.h"
#include "conn-zip.h"
#include "conn-func.h"
#include "log.h"
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 array My_Listeners;
static array My_ConnArray;
+static size_t NumConnections;
#ifdef TCPWRAP
int allow_severity = LOG_INFO;
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));
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
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);
}
}
+#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
/* 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 );
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 ?
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, ",");
}
#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);
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.
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);
}
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,
/* 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! */
/* 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 */
("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;
#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;
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
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]" );
/* 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++);
}
Client_SetHostname(c, My_Connections[new_sock].host);
+ 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 */
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 ...",
/**
- * 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
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';
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
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.",
* 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
}
#endif
}
- return result;
} /* Handle_Buffer */
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 */
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);
}
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- */