src/IOEngine_select.c \
src/IOEngine_epoll.c \
src/IOEngine_kevent.c \
+ src/IOHandler_SSL.c \
src/main.c \
src/ServerSocket.c \
src/tools.c \
void (*cleanup)(void);
};
-#define iohandler_wants_writes(IOFD) (IOFD->writebuf.bufpos || IOFD->state == IO_CONNECTING)
+#define iohandler_wants_writes(IOFD) ((IOFD->writebuf.bufpos && !IOFD->ssl_hs_read) || IOFD->state == IO_CONNECTING || IOFD->ssl_hs_write)
void iohandler_log(enum IOLogType type, char *text, ...);
void iohandler_events(struct IODescriptor *iofd, int readable, int writeable);
*/
#include "IOHandler.h"
#include "IOEngine.h"
+#include "IOHandler_SSL.h"
#define MAXLOG 1024
//do something with logBuf
//...
+ printf("%s", logBuf);
}
/* IO Engines */
struct IOEngine *engine = NULL;
static void iohandler_init_engine() {
+ if(engine) return;
//try other engines
if(!engine && engine_kevent.init && engine_kevent.init())
engine = &engine_kevent;
}
}
iohandler_log(IOLOG_DEBUG, "using %s IO engine", engine->name);
+ iohandler_ssl_init();
}
static void iohandler_append(struct IODescriptor *descriptor) {
}
connect(sockfd, dstaddr, dstaddrlen); //returns EINPROGRESS here (nonblocking)
descriptor->state = IO_CONNECTING;
+ descriptor->ssl = (ssl ? 1 : 0);
descriptor->read_lines = 1;
engine->update(descriptor);
iohandler_log(IOLOG_DEBUG, "added client socket (%d) connecting to %s:%d", sockfd, hostname, port);
return descriptor;
}
-struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, int ssl, iohandler_callback *callback) {
+struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, iohandler_callback *callback) {
int sockfd;
struct addrinfo hints, *res, *freeres;
struct sockaddr_in *ip4 = NULL;
void iohandler_try_write(struct IODescriptor *iofd) {
if(!iofd->writebuf.bufpos) return;
iohandler_log(IOLOG_DEBUG, "write writebuf (%d bytes) to socket (fd: %d)", iofd->writebuf.bufpos, iofd->fd);
- int res = send(iofd->fd, iofd->writebuf.buffer, iofd->writebuf.bufpos, 0);
+ int res;
+ if(iofd->ssl_active)
+ res = iohandler_ssl_write(iofd, iofd->writebuf.buffer, iofd->writebuf.bufpos);
+ else
+ res = send(iofd->fd, iofd->writebuf.buffer, iofd->writebuf.bufpos, 0);
if(res < 0) {
if (errno != EAGAIN) {
iohandler_log(IOLOG_ERROR, "could not write to socket (fd: %d): %d - %s", iofd->fd, errno, strerror(errno));
iohandler_try_write(iofd);
}
//close IODescriptor
+ if(iofd->ssl)
+ iohandler_ssl_disconnect(iofd);
if(iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT || iofd->type == IOTYPE_STDIN)
close(iofd->fd);
iohandler_remove(iofd, engine_remove);
callback_event.type = IOEVENT_IGNORE;
callback_event.iofd = iofd;
switch(iofd->state) {
+ case IO_SSLWAIT:
+ if(!readable && !writeable) {
+ callback_event.type = IOEVENT_SSLFAILED;
+ iofd->state = IO_CLOSED;
+ } 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);
+ }
+ break;
case IO_CLOSED:
if(iofd->type == IOTYPE_TIMER)
callback_event.type = IOEVENT_TIMEOUT;
iofd->state = IO_CLOSED;
engine->update(iofd);
} else if(writeable) {
+ if(iofd->ssl && !iofd->ssl_active) {
+ iohandler_log(IOLOG_DEBUG, "triggering iohandler_ssl_connect for %s (fd: %d)", iohandler_iotype_name(iofd->type), iofd->fd);
+ iohandler_ssl_connect(iofd);
+ return;
+ }
callback_event.type = IOEVENT_CONNECTED;
iofd->state = IO_CONNECTED;
engine->update(iofd);
case IO_CONNECTED:
if(readable) {
if(iofd->read_lines) {
- int bytes = recv(iofd->fd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos, 0);
+ int bytes;
+ if(iofd->ssl_active)
+ bytes = iohandler_ssl_read(iofd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos);
+ else
+ bytes = recv(iofd->fd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos, 0);
if(bytes <= 0) {
if (errno != EAGAIN) {
iofd->state = IO_CLOSED;
return "IO_CONNECTING";
case IO_CONNECTED:
return "IO_CONNECTED";
+ case IO_SSLWAIT:
+ return "IO_SSLWAIT";
default:
return "(UNDEFINED)";
}
struct IODescriptor;
struct IOEvent;
+struct IOSSLNode;
#define IOHANDLER_CALLBACK(NAME) void NAME(UNUSED_ARG(struct IOEvent *io_event))
typedef IOHANDLER_CALLBACK(iohandler_callback);
IO_CLOSED, /* descriptor is dead (socket waiting for removal or timer) */
IO_LISTENING, /* descriptor is waiting for connections (server socket) */
IO_CONNECTING, /* descriptor is waiting for connection approval (connecting client socket) */
- IO_CONNECTED /* descriptor is connected (connected client socket) */
+ IO_CONNECTED, /* descriptor is connected (connected client socket) */
+ IO_SSLWAIT /* waiting for SSL backend (e.g. handshake) */
};
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_TIMEOUT /* timer timed out */
+ IOEVENT_TIMEOUT, /* timer timed out */
+ IOEVENT_SSLFAILED /* failed to initialize SSL session */
};
struct IOBuffer {
void *data;
int read_lines : 1;
int ssl : 1;
+ int ssl_active : 1;
+ int ssl_hs_read : 1;
+ int ssl_hs_write : 1;
+ struct IOSSLNode *sslnode;
struct IODescriptor *next, *prev;
};
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_listen(const char *hostname, unsigned int port, int ssl, iohandler_callback *callback);
+struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, iohandler_callback *callback);
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, ...);
--- /dev/null
+/* IOHandler_SSL.c - TransparentIRC 0.1
+ * Copyright (C) 2011-2012 Philipp Kreil (pk910)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "IOHandler.h"
+#include "IOEngine.h"
+#include "IOHandler_SSL.h"
+
+void iohandler_ssl_init() {
+#ifdef HAVE_SSL
+ SSL_library_init();
+ SSL_load_error_strings();
+#endif
+}
+
+void iohandler_ssl_connect(struct IODescriptor *iofd) {
+#ifdef HAVE_SSL
+ iofd->state = IO_SSLWAIT;
+ struct IOSSLNode *sslnode = malloc(sizeof(*sslnode));
+ sslnode->sslContext = SSL_CTX_new(SSLv23_client_method());
+ if(!sslnode->sslContext)
+ goto ssl_connect_err;
+ sslnode->sslHandle = SSL_new(sslnode->sslContext);
+ if(!sslnode->sslHandle)
+ goto ssl_connect_err;
+ if(!SSL_set_fd(sslnode->sslHandle, iofd->fd))
+ goto ssl_connect_err;
+ SSL_set_connect_state(sslnode->sslHandle);
+ iofd->sslnode = sslnode;
+ iohandler_ssl_client_handshake(iofd);
+ return;
+ssl_connect_err:
+ free(sslnode);
+ iohandler_events(iofd, 0, 0);
+#endif
+}
+
+void iohandler_ssl_client_handshake(struct IODescriptor *iofd) {
+ // Perform an SSL handshake.
+ int ret = SSL_do_handshake(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;
+ }
+}
+
+void iohandler_ssl_disconnect(struct IODescriptor *iofd) {
+#ifdef HAVE_SSL
+ if(!iofd->sslnode) return;
+ SSL_shutdown(iofd->sslnode->sslHandle);
+ SSL_free(iofd->sslnode->sslHandle);
+ SSL_CTX_free(iofd->sslnode->sslContext);
+ free(iofd->sslnode);
+ iofd->sslnode = NULL;
+ iofd->ssl_active = 0;
+#endif
+}
+
+int iohandler_ssl_read(struct IODescriptor *iofd, char *buffer, int len) {
+#ifdef HAVE_SSL
+ if(!iofd->sslnode) return 0;
+ int ret = SSL_read(iofd->sslnode->sslHandle, buffer, len);
+ int update = (iofd->ssl_hs_read || iofd->ssl_hs_write);
+ iofd->ssl_hs_read = 0;
+ iofd->ssl_hs_write = 0;
+ switch(SSL_get_error(iofd->sslnode->sslHandle, ret)) {
+ case SSL_ERROR_NONE:
+ case SSL_ERROR_ZERO_RETURN:
+ if(update)
+ iohandler_update(iofd);
+ return ret;
+ break;
+ case SSL_ERROR_WANT_READ:
+ iofd->ssl_hs_read = 1;
+ iohandler_update(iofd);
+ iohandler_log(IOLOG_DEBUG, "SSL_read for %s (fd: %d) returned SSL_ERROR_WANT_READ", iohandler_iotype_name(iofd->type), iofd->fd);
+ errno = EAGAIN;
+ return -1;
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ iofd->ssl_hs_write = 1;
+ iohandler_update(iofd);
+ iohandler_log(IOLOG_DEBUG, "SSL_read for %s (fd: %d) returned SSL_ERROR_WANT_WRITE", iohandler_iotype_name(iofd->type), iofd->fd);
+ errno = EAGAIN;
+ return -1;
+ break;
+ default:
+ iohandler_log(IOLOG_ERROR, "SSL_read for %s (fd: %d) failed with ", iohandler_iotype_name(iofd->type), iofd->fd);
+ return -1;
+ break;
+ }
+#endif
+ return 0;
+}
+
+int iohandler_ssl_write(struct IODescriptor *iofd, char *buffer, int len) {
+#ifdef HAVE_SSL
+ if(!iofd->sslnode) return 0;
+ int ret = SSL_write(iofd->sslnode->sslHandle, buffer, len);
+ int update = (iofd->ssl_hs_read || iofd->ssl_hs_write);
+ iofd->ssl_hs_read = 0;
+ iofd->ssl_hs_write = 0;
+ switch(SSL_get_error(iofd->sslnode->sslHandle, ret)) {
+ case SSL_ERROR_NONE:
+ case SSL_ERROR_ZERO_RETURN:
+ if(update)
+ iohandler_update(iofd);
+ return ret;
+ break;
+ case SSL_ERROR_WANT_READ:
+ iofd->ssl_hs_read = 1;
+ iohandler_update(iofd);
+ iohandler_log(IOLOG_DEBUG, "SSL_write for %s (fd: %d) returned SSL_ERROR_WANT_READ", iohandler_iotype_name(iofd->type), iofd->fd);
+ errno = EAGAIN;
+ return -1;
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ iofd->ssl_hs_write = 1;
+ iohandler_update(iofd);
+ iohandler_log(IOLOG_DEBUG, "SSL_write for %s (fd: %d) returned SSL_ERROR_WANT_WRITE", iohandler_iotype_name(iofd->type), iofd->fd);
+ errno = EAGAIN;
+ return -1;
+ break;
+ default:
+ iohandler_log(IOLOG_ERROR, "SSL_write for %s (fd: %d) failed with ", iohandler_iotype_name(iofd->type), iofd->fd);
+ return -1;
+ break;
+ }
+#endif
+ return 0;
+}
--- /dev/null
+/* IOHandler_SSL.h - TransparentIRC 0.1
+ * Copyright (C) 2011-2012 Philipp Kreil (pk910)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _IOHandler_SSL_h
+#define _IOHandler_SSL_h
+#include "overall.h"
+
+struct IODescriptor;
+
+#ifdef HAVE_SSL
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+struct IOSSLNode {
+ SSL *sslHandle;
+ SSL_CTX *sslContext;
+};
+#else
+struct IOSSLNode {
+ //just unused
+};
+#endif
+
+void iohandler_ssl_init();
+void iohandler_ssl_connect(struct IODescriptor *iofd);
+void iohandler_ssl_client_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);
+
+#endif
}
struct ServerSocket *serversocket_listen(char *hostname, int port, int ssl) {
- struct IODescriptor *iofd = iohandler_listen(hostname, port, ssl, serversocket_callback);
+ struct IODescriptor *iofd = iohandler_listen(hostname, port, serversocket_callback);
if(!iofd) return NULL;
struct ServerSocket *server = serversocket_create(iofd);
return server;
void userclient_close(struct UserClient *client) {
if(client->flags & USERCLIENT_LOGGED_IN) {
- usersession_client_close(client->user);
+ if(client->user)
+ usersession_client_close(client->user);
} else {
struct UserLogin *login = client->user;
if(client->flags & USERCLIENT_LOGIN_PROCESSING) {
}
static void usersession_close(struct UserSession *session) {
- if(session->client)
+ if(session->client) {
+ session->client->user = NULL;
userclient_close(session->client);
+ }
if(session->irc)
ircclient_close(session->irc);
if(session->timer)