From: pk910 Date: Wed, 17 Oct 2012 17:15:06 +0000 (+0200) Subject: [IOMultiplexer] added SSL Server Sockets and IPv4/IPv6 only Sockets X-Git-Url: http://git.pk910.de/?p=NextIRCd.git;a=commitdiff_plain;h=57b28c464a3cd3eee7d91ddf9d9fc83fae1c58f6 [IOMultiplexer] added SSL Server Sockets and IPv4/IPv6 only Sockets --- diff --git a/src/IOHandler.c b/src/IOHandler.c index db9a599..18e1b7f 100644 --- a/src/IOHandler.c +++ b/src/IOHandler.c @@ -250,6 +250,10 @@ struct IODescriptor *iohandler_timer(struct timeval timeout, iohandler_callback } struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, int ssl, const char *bindhost, iohandler_callback *callback) { + return iohandler_connect_flags(hostname, port, ssl, bindhost, callback, IOHANDLER_CONNECT_IPV4 | IOHANDLER_CONNECT_IPV6); +} + +struct IODescriptor *iohandler_connect_flags(const char *hostname, unsigned int port, int ssl, const char *bindhost, iohandler_callback *callback, int flags) { //non-blocking connect int sockfd, result; struct addrinfo hints, *res; @@ -284,7 +288,7 @@ struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, freeaddrinfo(res); } - if(ip6) { + if(ip6 && (flags & IOHANDLER_CONNECT_IPV6)) { sockfd = socket(AF_INET6, SOCK_STREAM, 0); if(sockfd == -1) { iohandler_log(IOLOG_ERROR, "could not create socket in %s:%d", __FILE__, __LINE__); @@ -313,7 +317,7 @@ struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, } dstaddr = (struct sockaddr*)ip6; dstaddrlen = sizeof(*ip6); - } else if(ip4) { + } else if(ip4 && (flags & IOHANDLER_CONNECT_IPV4)) { sockfd = socket(AF_INET, SOCK_STREAM, 0); if(sockfd == -1) { iohandler_log(IOLOG_ERROR, "could not create socket in %s:%d", __FILE__, __LINE__); @@ -384,6 +388,10 @@ struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, } struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, iohandler_callback *callback) { + return iohandler_listen_flags(hostname, port, callback, IOHANDLER_LISTEN_IPV4 | IOHANDLER_LISTEN_IPV6); +} + +struct IODescriptor *iohandler_listen_flags(const char *hostname, unsigned int port, iohandler_callback *callback, int flags) { int sockfd; struct addrinfo hints, *res; struct sockaddr_in *ip4 = NULL; @@ -415,7 +423,7 @@ struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, i freeaddrinfo(res); } - if(ip6) { + if(ip6 && (flags & IOHANDLER_LISTEN_IPV6)) { sockfd = socket(AF_INET6, SOCK_STREAM, 0); if(sockfd == -1) return NULL; @@ -426,7 +434,7 @@ struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, i ip6->sin6_port = htons(port); bind(sockfd, (struct sockaddr*)ip6, sizeof(*ip6)); - } else if(ip4) { + } else if(ip4 && (flags && IOHANDLER_LISTEN_IPV4)) { sockfd = socket(AF_INET, SOCK_STREAM, 0); if(sockfd == -1) return NULL; @@ -453,11 +461,11 @@ struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, i //make sockfd unblocking #if defined(F_GETFL) { - int flags; - flags = fcntl(sockfd, F_GETFL); - fcntl(sockfd, F_SETFL, flags|O_NONBLOCK); - flags = fcntl(sockfd, F_GETFD); - fcntl(sockfd, F_SETFD, flags|FD_CLOEXEC); + int flag; + flag = fcntl(sockfd, F_GETFL); + fcntl(sockfd, F_SETFL, flag|O_NONBLOCK); + flag = fcntl(sockfd, F_GETFD); + fcntl(sockfd, F_SETFD, flag|FD_CLOEXEC); } #else /* I hope you're using the Win32 backend or something else that @@ -476,6 +484,21 @@ struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, i return descriptor; } +struct IODescriptor *iohandler_listen_ssl(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iohandler_callback *callback) { + return iohandler_listen_ssl_flags(hostname, port, certfile, keyfile, callback, IOHANDLER_LISTEN_IPV4 | IOHANDLER_LISTEN_IPV6); +} + +struct IODescriptor *iohandler_listen_ssl_flags(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iohandler_callback *callback, int flags) { + struct IODescriptor *descriptor = iohandler_listen_flags(hostname, port, callback, flags); + if(!descriptor) + return NULL; + //SSL Server Socket + iohandler_ssl_listen(descriptor, certfile, keyfile); + if(descriptor->sslnode) + descriptor->ssl = 1; + return descriptor; +} + void iohandler_write(struct IODescriptor *iofd, const char *line) { size_t linelen = strlen(line); iohandler_send(iofd, line, linelen); @@ -580,9 +603,15 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { switch(iofd->state) { case IO_SSLWAIT: if(!readable && !writeable) { - callback_event.type = IOEVENT_SSLFAILED; - iofd->state = IO_CLOSED; - engine->update(iofd); + if(!iofd->ssl_server_hs) { + callback_event.type = IOEVENT_SSLFAILED; + iofd->state = IO_CLOSED; + engine->update(iofd); + } else + iohandler_close(iofd); + } else if(iofd->ssl_server_hs) { + iohandler_log(IOLOG_DEBUG, "triggering iohandler_ssl_server_handshake for %s (fd: %d)", iohandler_iotype_name(iofd->type), iofd->fd); + iohandler_ssl_server_handshake(iofd); } else { iohandler_log(IOLOG_DEBUG, "triggering iohandler_ssl_client_handshake for %s (fd: %d)", iohandler_iotype_name(iofd->type), iofd->fd); iohandler_ssl_client_handshake(iofd); @@ -597,6 +626,9 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { callback_event.data.accept_fd = accept(iofd->fd, NULL, 0); if(callback_event.data.accept_fd < 0) { iohandler_log(IOLOG_ERROR, "could not accept client (server fd: %d): %d - %s", iofd->fd, errno, strerror(errno)); + } else if(iofd->ssl) { + struct IODescriptor *client_iofd = iohandler_add(callback_event.data.accept_fd, IOTYPE_CLIENT, NULL, NULL); + iohandler_ssl_client_accepted(iofd, client_iofd); } else callback_event.type = IOEVENT_ACCEPT; } @@ -616,7 +648,14 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { iohandler_ssl_connect(iofd); return; } - callback_event.type = IOEVENT_CONNECTED; + if(iofd->ssl && iofd->ssl_server_hs) + callback_event.type = IOEVENT_CONNECTED; + else { + callback_event.type = IOEVENT_SSLACCEPT; + callback_event.iofd = iofd->data; + callback_event.data.accept_iofd = iofd; + iofd->data = NULL; + } iofd->state = IO_CONNECTED; engine->update(iofd); } diff --git a/src/IOHandler.h b/src/IOHandler.h index b0a6c6b..72e2705 100644 --- a/src/IOHandler.h +++ b/src/IOHandler.h @@ -67,6 +67,7 @@ enum IOEventType { IOEVENT_NOTCONNECTED, /* client socket could not connect (errid valid) */ IOEVENT_CLOSED, /* client socket lost connection (errid valid) */ IOEVENT_ACCEPT, /* server socket accepted new connection (accept_fd valid) */ + IOEVENT_SSLACCEPT, /* SSL server socket accepted new connection (accept_iofd valid) */ IOEVENT_TIMEOUT, /* timer timed out */ IOEVENT_SSLFAILED /* failed to initialize SSL session */ }; @@ -87,6 +88,7 @@ struct IODescriptor { void *data; int read_lines : 1; int ssl : 1; + int ssl_server_hs : 1; int ssl_active : 1; int ssl_hs_read : 1; int ssl_hs_write : 1; @@ -102,13 +104,24 @@ struct IOEvent { char *recv_str; int accept_fd; int errid; + struct IODescriptor *accept_iofd; } data; }; +#define IOHANDLER_LISTEN_IPV4 0x01 +#define IOHANDLER_LISTEN_IPV6 0x02 /* overrides IOHANDLER_LISTEN_IPV4 */ + +#define IOHANDLER_CONNECT_IPV4 0x01 +#define IOHANDLER_CONNECT_IPV6 0x02 /* overrides IOHANDLER_CONNECT_IPV4 */ + struct IODescriptor *iohandler_add(int sockfd, enum IOType type, struct timeval *timeout, iohandler_callback *callback); struct IODescriptor *iohandler_timer(struct timeval timeout, iohandler_callback *callback); struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, int ssl, const char *bind, iohandler_callback *callback); +struct IODescriptor *iohandler_connect_flags(const char *hostname, unsigned int port, int ssl, const char *bindhost, iohandler_callback *callback, int flags); struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, iohandler_callback *callback); +struct IODescriptor *iohandler_listen_flags(const char *hostname, unsigned int port, iohandler_callback *callback, int flags); +struct IODescriptor *iohandler_listen_ssl(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iohandler_callback *callback); +struct IODescriptor *iohandler_listen_ssl_flags(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iohandler_callback *callback, int flags); void iohandler_write(struct IODescriptor *iofd, const char *line); void iohandler_send(struct IODescriptor *iofd, const char *data, size_t datalen); void iohandler_printf(struct IODescriptor *iofd, const char *text, ...); diff --git a/src/IOHandler_SSL.c b/src/IOHandler_SSL.c index d9e1d19..5d87cec 100644 --- a/src/IOHandler_SSL.c +++ b/src/IOHandler_SSL.c @@ -20,22 +20,40 @@ void iohandler_ssl_init() { #ifdef HAVE_SSL SSL_library_init(); + OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */ SSL_load_error_strings(); #endif } +static void iohandler_ssl_error() { + unsigned long e; + while((e = ERR_get_error())) { + iohandler_log(IOLOG_ERROR, "SSLv23 ERROR %lu: %s", e, ERR_error_string(e, NULL)); + } +} + void iohandler_ssl_connect(struct IODescriptor *iofd) { #ifdef HAVE_SSL iofd->state = IO_SSLWAIT; + iofd->ssl_server_hs = 0; struct IOSSLNode *sslnode = malloc(sizeof(*sslnode)); sslnode->sslContext = SSL_CTX_new(SSLv23_client_method()); - if(!sslnode->sslContext) + if(!sslnode->sslContext) { + iohandler_ssl_error(); + iohandler_log(IOLOG_ERROR, "SSL: could not create client SSL CTX"); goto ssl_connect_err; + } sslnode->sslHandle = SSL_new(sslnode->sslContext); - if(!sslnode->sslHandle) + if(!sslnode->sslHandle) { + iohandler_ssl_error(); + iohandler_log(IOLOG_ERROR, "SSL: could not create client SSL Handle"); goto ssl_connect_err; - if(!SSL_set_fd(sslnode->sslHandle, iofd->fd)) + } + if(!SSL_set_fd(sslnode->sslHandle, iofd->fd)) { + iohandler_ssl_error(); + iohandler_log(IOLOG_ERROR, "SSL: could not set client fd in SSL Handle"); goto ssl_connect_err; + } SSL_set_connect_state(sslnode->sslHandle); iofd->sslnode = sslnode; iohandler_ssl_client_handshake(iofd); @@ -46,6 +64,42 @@ ssl_connect_err: #endif } +void iohandler_ssl_listen(struct IODescriptor *iofd, const char *certfile, const char *keyfile) { +#ifdef HAVE_SSL + struct IOSSLNode *sslnode = malloc(sizeof(*sslnode)); + sslnode->sslContext = SSL_CTX_new(SSLv23_server_method()); + if(!sslnode->sslContext) { + iohandler_ssl_error(); + iohandler_log(IOLOG_ERROR, "SSL: could not create server SSL CTX"); + goto ssl_listen_err; + } + /* load certificate */ + if(SSL_CTX_use_certificate_file(sslnode->sslContext, certfile, SSL_FILETYPE_PEM) <= 0) { + iohandler_ssl_error(); + iohandler_log(IOLOG_ERROR, "SSL: could not load server certificate (%s)", certfile); + goto ssl_listen_err; + } + /* load keyfile */ + if(SSL_CTX_use_PrivateKey_file(sslnode->sslContext, keyfile, SSL_FILETYPE_PEM) <= 0) { + iohandler_ssl_error(); + iohandler_log(IOLOG_ERROR, "SSL: could not load server keyfile (%s)", keyfile); + goto ssl_listen_err; + } + /* check certificate and keyfile */ + if(!SSL_CTX_check_private_key(sslnode->sslContext)) { + iohandler_ssl_error(); + iohandler_log(IOLOG_ERROR, "SSL: server certificate (%s) and keyfile (%s) doesn't match!", certfile, keyfile); + goto ssl_listen_err; + } + iofd->sslnode = sslnode; + return; +ssl_listen_err: + free(sslnode); + iofd->sslnode = NULL; + iohandler_events(iofd, 0, 0); +#endif +} + void iohandler_ssl_client_handshake(struct IODescriptor *iofd) { #ifdef HAVE_SSL // Perform an SSL handshake. @@ -75,6 +129,62 @@ void iohandler_ssl_client_handshake(struct IODescriptor *iofd) { #endif } +void iohandler_ssl_client_accepted(struct IODescriptor *iofd, struct IODescriptor *client_iofd) { +#ifdef HAVE_SSL + struct IOSSLNode *sslnode = malloc(sizeof(*sslnode)); + sslnode->sslHandle = SSL_new(sslnode->sslContext); + if(!sslnode->sslHandle) { + iohandler_ssl_error(); + iohandler_log(IOLOG_ERROR, "SSL: could not create client SSL Handle"); + goto ssl_accept_err; + } + if(!SSL_set_fd(sslnode->sslHandle, client_iofd->fd)) { + iohandler_ssl_error(); + iohandler_log(IOLOG_ERROR, "SSL: could not set client fd in SSL Handle"); + goto ssl_accept_err; + } + client_iofd->state = IO_SSLWAIT; + client_iofd->ssl_server_hs = 1; + client_iofd->ssl = 1; + client_iofd->sslnode = sslnode; + client_iofd->callback = iofd->callback; + client_iofd->data = iofd; + return; +ssl_accept_err: + iohandler_close(client_iofd); + free(sslnode); +#endif +} + +void iohandler_ssl_server_handshake(struct IODescriptor *iofd) { +#ifdef HAVE_SSL + // Perform an SSL handshake. + int ret = SSL_accept(iofd->sslnode->sslHandle); + iofd->ssl_hs_read = 0; + iofd->ssl_hs_write = 0; + switch(SSL_get_error(iofd->sslnode->sslHandle, ret)) { + case SSL_ERROR_NONE: + iofd->state = IO_CONNECTING; + iofd->ssl_active = 1; + iohandler_log(IOLOG_DEBUG, "SSL handshake for %s (fd: %d) successful", iohandler_iotype_name(iofd->type), iofd->fd); + iohandler_events(iofd, 0, 1); //perform IOEVENT_CONNECTED event + break; + case SSL_ERROR_WANT_READ: + iofd->ssl_hs_read = 1; + iohandler_log(IOLOG_DEBUG, "SSL_do_handshake for %s (fd: %d) returned SSL_ERROR_WANT_READ", iohandler_iotype_name(iofd->type), iofd->fd); + break; + case SSL_ERROR_WANT_WRITE: + iofd->ssl_hs_write = 1; + iohandler_log(IOLOG_DEBUG, "SSL_do_handshake for %s (fd: %d) returned SSL_ERROR_WANT_WRITE", iohandler_iotype_name(iofd->type), iofd->fd); + break; + default: + iohandler_log(IOLOG_ERROR, "SSL_do_handshake for %s (fd: %d) failed with ", iohandler_iotype_name(iofd->type), iofd->fd); + iohandler_events(iofd, 0, 0); + break; + } +#endif +} + void iohandler_ssl_disconnect(struct IODescriptor *iofd) { #ifdef HAVE_SSL if(!iofd->sslnode) return; diff --git a/src/IOHandler_SSL.h b/src/IOHandler_SSL.h index b1ccf0b..c743245 100644 --- a/src/IOHandler_SSL.h +++ b/src/IOHandler_SSL.h @@ -36,7 +36,10 @@ struct IOSSLNode { void iohandler_ssl_init(); void iohandler_ssl_connect(struct IODescriptor *iofd); +void iohandler_ssl_listen(struct IODescriptor *iofd, const char *certfile, const char *keyfile); void iohandler_ssl_client_handshake(struct IODescriptor *iofd); +void iohandler_ssl_client_accepted(struct IODescriptor *iofd, struct IODescriptor *client_iofd); +void iohandler_ssl_server_handshake(struct IODescriptor *iofd); void iohandler_ssl_disconnect(struct IODescriptor *iofd); int iohandler_ssl_read(struct IODescriptor *iofd, char *buffer, int len); int iohandler_ssl_write(struct IODescriptor *iofd, char *buffer, int len); diff --git a/src/test/Makefile b/src/test/Makefile index df861f0..37fa3d8 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -1,7 +1,7 @@ CC = gcc -CFLAGS = -g -O0 -Wall -Wshadow -Werror -LDFLAGS = -lws2_32 +CFLAGS = -g -O0 -Wall -Wshadow -Werror -DHAVE_PTHREAD_H +LDFLAGS = -lws2_32 -lpthread OBJ = ../IOEngine_epoll.o ../IOEngine_kevent.o ../IOEngine_select.o ../IOEngine_win32.o ../IOHandler.o ../IOHandler_SSL.o iotest.o