Merge remote-tracking branch 'remotes/IOMultiplexer/v2'
authorpk910 <philipp@zoelle1.de>
Tue, 15 Jul 2014 14:55:12 +0000 (16:55 +0200)
committerpk910 <philipp@zoelle1.de>
Tue, 15 Jul 2014 14:55:12 +0000 (16:55 +0200)
Conflicts:
.gitignore

67 files changed:
.gitignore
Makefile.am [new file with mode: 0644]
autogen.sh [new file with mode: 0644]
configure.ac [new file with mode: 0644]
src/.gitignore [new file with mode: 0644]
src/IOHandler++/.gitignore [new file with mode: 0644]
src/IOHandler++/IOHandler.cpp [new file with mode: 0644]
src/IOHandler++/IOHandler.h [new file with mode: 0644]
src/IOHandler++/IOSocket.cpp [new file with mode: 0644]
src/IOHandler++/IOSocket.h [new file with mode: 0644]
src/IOHandler++/IOTimer.cpp [new file with mode: 0644]
src/IOHandler++/IOTimer.h [new file with mode: 0644]
src/IOHandler++/Makefile.am [new file with mode: 0644]
src/IOHandler/.gitignore [new file with mode: 0644]
src/IOHandler/IODNSAddress.struct.h [new file with mode: 0644]
src/IOHandler/IODNSEngine_cares.c [new file with mode: 0644]
src/IOHandler/IODNSEngine_default.c [new file with mode: 0644]
src/IOHandler/IODNSLookup.c [new file with mode: 0644]
src/IOHandler/IODNSLookup.h [new file with mode: 0644]
src/IOHandler/IOEngine_epoll.c [new file with mode: 0644]
src/IOHandler/IOEngine_kevent.c [new file with mode: 0644]
src/IOHandler/IOEngine_select.c [new file with mode: 0644]
src/IOHandler/IOEngine_win32.c [new file with mode: 0644]
src/IOHandler/IOGarbageCollector.c [new file with mode: 0644]
src/IOHandler/IOGarbageCollector.h [new file with mode: 0644]
src/IOHandler/IOHandler.c [new file with mode: 0644]
src/IOHandler/IOHandler.h [new file with mode: 0644]
src/IOHandler/IOHandler_config.h [new file with mode: 0644]
src/IOHandler/IOInternal.h [new file with mode: 0644]
src/IOHandler/IOLog.c [new file with mode: 0644]
src/IOHandler/IOLog.h [new file with mode: 0644]
src/IOHandler/IOSSLBackend.c [new file with mode: 0644]
src/IOHandler/IOSSLBackend.h [new file with mode: 0644]
src/IOHandler/IOSockets.c [new file with mode: 0644]
src/IOHandler/IOSockets.h [new file with mode: 0644]
src/IOHandler/IOTimer.c [new file with mode: 0644]
src/IOHandler/IOTimer.h [new file with mode: 0644]
src/IOHandler/Makefile.am [new file with mode: 0644]
src/IOHandler/compat/inet.c [new file with mode: 0644]
src/IOHandler/compat/inet.h [new file with mode: 0644]
src/IOHandler/compat/utime.c [new file with mode: 0644]
src/IOHandler/compat/utime.h [new file with mode: 0644]
src/IOHandler_test/Makefile.am [new file with mode: 0644]
src/IOHandler_test/client++/.gitignore [new file with mode: 0644]
src/IOHandler_test/client++/Makefile.am [new file with mode: 0644]
src/IOHandler_test/client++/iotest.cpp [new file with mode: 0644]
src/IOHandler_test/client/.gitignore [new file with mode: 0644]
src/IOHandler_test/client/Makefile.am [new file with mode: 0644]
src/IOHandler_test/client/iotest.c [new file with mode: 0644]
src/IOHandler_test/client_ssl/.gitignore [new file with mode: 0644]
src/IOHandler_test/client_ssl/Makefile.am [new file with mode: 0644]
src/IOHandler_test/client_ssl/iotest.c [new file with mode: 0644]
src/IOHandler_test/resolv/.gitignore [new file with mode: 0644]
src/IOHandler_test/resolv/Makefile.am [new file with mode: 0644]
src/IOHandler_test/resolv/iotest.c [new file with mode: 0644]
src/IOHandler_test/server_ssl/.gitignore [new file with mode: 0644]
src/IOHandler_test/server_ssl/Makefile.am [new file with mode: 0644]
src/IOHandler_test/server_ssl/cert.pem [new file with mode: 0644]
src/IOHandler_test/server_ssl/iotest.c [new file with mode: 0644]
src/IOHandler_test/server_ssl/key.pem [new file with mode: 0644]
src/IOHandler_test/timer++/.gitignore [new file with mode: 0644]
src/IOHandler_test/timer++/Makefile.am [new file with mode: 0644]
src/IOHandler_test/timer++/iotest.cpp [new file with mode: 0644]
src/IOHandler_test/timer/.gitignore [new file with mode: 0644]
src/IOHandler_test/timer/Makefile.am [new file with mode: 0644]
src/IOHandler_test/timer/iotest.c [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]

index 6121f8768dba42ff30a2d72dc915d5c1b4a05799..946fb4ac223feedb894c5837a294771ceda1397b 100644 (file)
@@ -1,6 +1,13 @@
 autom4te.cache
-*.m4
-config.*
+m4
+aclocal.m4
+config.guess
+config.h
+config.h.in
+config.h.in~
+config.log
+config.status
+config.sub
 configure
 depcomp
 install-sh
@@ -9,4 +16,4 @@ ltmain.sh
 Makefile
 Makefile.in
 missing
-stamp-h1
\ No newline at end of file
+stamp-h1
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..92ae850
--- /dev/null
@@ -0,0 +1,3 @@
+##Process this file with automake to create Makefile.in
+ACLOCAL_AMFLAGS = -I m4
+SUBDIRS = src
diff --git a/autogen.sh b/autogen.sh
new file mode 100644 (file)
index 0000000..01c075c
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+echo "Generating configure files... may take a while."
+
+autoreconf --install --force && \
+  echo "Preparing was successful if there was no error messages above." && \
+  echo "Now type:" && \
+  echo "  ./configure && make"  && \
+  echo "Run './configure --help' for more information"
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..4275959
--- /dev/null
@@ -0,0 +1,80 @@
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.67])
+AC_INIT([IOMultiplexer], [2.0], [iohandler@pk910.de], [pk910], [http://pk910.de])
+AC_PREFIX_DEFAULT([~/iotest])
+AC_CONFIG_MACRO_DIR([m4])
+AM_INIT_AUTOMAKE([foreign])
+AM_SILENT_RULES([yes])
+AC_CONFIG_HEADERS([config.h])
+
+LT_INIT([disable-static])
+
+AC_MSG_RESULT($MODULES)
+AC_SUBST(MODULES)
+
+# Checks for programs.
+AC_PROG_AWK
+AC_PROG_CC
+AC_PROG_CXX
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+AC_PROG_LIBTOOL
+
+AC_ARG_ENABLE([debug],
+  [AS_HELP_STRING([--enable-debug], [debug mode (compile using -O0 -Wall -Wshadow -Werror)])],
+  [CFLAGS='-g -O0 -Wall -Wshadow -Werror'],
+  [CFLAGS='-g -O2']
+)
+
+# Checks for libraries.
+
+CFLAGS="$CFLAGS -D_GNU_SOURCE"
+
+AC_FUNC_MALLOC
+AC_CHECK_FUNCS([usleep select socket inet_pton inet_ntop])
+AC_CHECK_HEADERS([fcntl.h sys/socket.h sys/select.h sys/time.h sys/types.h unistd.h windows.h winsock2.h errno.h sys/epoll.h sys/event.h])
+
+AC_CHECK_LIB(ws2_32, main, [ LIBS="$LIBS -lws2_32" ], [])
+have_gnutls="no"
+AC_CHECK_LIB(gnutls, gnutls_init, [
+  AC_CHECK_HEADERS(gnutls/gnutls.h, [
+    LIBS="$LIBS -lgnutls"
+    have_gnutls="yes"
+  ])
+])
+if test x"$have_gnutls" = xno; then
+  AC_CHECK_LIB(ssl, SSL_read, [
+    AC_CHECK_LIB(crypto, X509_new, [
+      AC_CHECK_HEADERS(openssl/ssl.h openssl/err.h openssl/rand.h, [
+        LIBS="$LIBS -lssl -lcrypto"
+      ])
+    ])
+  ])
+fi
+AC_CHECK_LIB(pthread, pthread_create, [
+  AC_CHECK_HEADERS(pthread.h, [
+    LIBS="$LIBS -lpthread"
+  ])
+])
+AC_CHECK_LIB(cares, ares_init, [
+  AC_CHECK_HEADERS(ares.h, [
+    LIBS="$LIBS -lcares"
+  ])
+])
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+  src/IOHandler/Makefile
+  src/IOHandler++/Makefile
+  src/IOHandler_test/Makefile
+  src/IOHandler_test/client/Makefile
+  src/IOHandler_test/client++/Makefile
+  src/IOHandler_test/client_ssl/Makefile
+  src/IOHandler_test/server_ssl/Makefile
+  src/IOHandler_test/timer/Makefile
+  src/IOHandler_test/timer++/Makefile
+  src/IOHandler_test/resolv/Makefile
+])
+AC_OUTPUT
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644 (file)
index 0000000..3dda729
--- /dev/null
@@ -0,0 +1,2 @@
+Makefile.in
+Makefile
diff --git a/src/IOHandler++/.gitignore b/src/IOHandler++/.gitignore
new file mode 100644 (file)
index 0000000..1f36bcd
--- /dev/null
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile.in
+Makefile
+*.lo
+*.la
diff --git a/src/IOHandler++/IOHandler.cpp b/src/IOHandler++/IOHandler.cpp
new file mode 100644 (file)
index 0000000..33b730c
--- /dev/null
@@ -0,0 +1,32 @@
+/* IOHandler.cpp - IOMultiplexer v2
+ * Copyright (C) 2014  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/>. 
+ */
+extern "C" {
+       #include "../IOHandler/IOHandler.h"
+}
+#include "IOHandler.h"
+
+CIOHandler::CIOHandler() {
+       iohandler_init();
+}
+
+void CIOHandler::start() {
+       iohandler_run();
+}
+
+void CIOHandler::stop() {
+       iohandler_stop();
+}
diff --git a/src/IOHandler++/IOHandler.h b/src/IOHandler++/IOHandler.h
new file mode 100644 (file)
index 0000000..fd3ebf7
--- /dev/null
@@ -0,0 +1,33 @@
+/* IOHandler.h - IOMultiplexer v2
+ * Copyright (C) 2014  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_cpp_h
+#define _IOHandler_cpp_h
+
+extern "C" {
+       #define IOSOCKET_CPP
+       #include "../IOHandler/IOHandler.h"
+}
+
+class CIOHandler {
+public:
+       CIOHandler();
+       
+       void start();
+       void stop();
+};
+
+#endif
diff --git a/src/IOHandler++/IOSocket.cpp b/src/IOHandler++/IOSocket.cpp
new file mode 100644 (file)
index 0000000..0662516
--- /dev/null
@@ -0,0 +1,142 @@
+/* IOSocket.cpp - IOMultiplexer v2
+ * Copyright (C) 2014  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/>. 
+ */
+extern "C" {
+       #include "../IOHandler/IOSockets.h"
+}
+#include "IOSocket.h"
+#include <cstdarg>
+#include <cstdio>
+#include <cstring>
+
+static IOSOCKET_CALLBACK(c_socket_callback) {
+       CIOSocket *ciosock = (CIOSocket *) event->socket->data;
+       ciosock->socket_callback(event);
+}
+
+CIOSocket::CIOSocket() {
+
+}
+CIOSocket::CIOSocket(IOSocket *iosocket) {
+       this->iosocket = iosocket;
+}
+
+int CIOSocket::connect(char *hostname, unsigned int port, int ssl, char *bindhost) {
+       return this->connect(hostname, port, ssl, bindhost, IOSOCKET_ADDR_IPV6 | IOSOCKET_ADDR_IPV4);
+}
+
+int CIOSocket::connect(char *hostname, unsigned int port, int ssl, char *bindhost, int flags) {
+       this->iosocket = iosocket_connect_flags(hostname, port, (ssl ? 1 : 0), (bindhost ? bindhost : NULL), c_socket_callback, flags);
+       if(this->iosocket) {
+               this->iosocket->data = this;
+               return 1;
+       } else
+               return 0;
+}
+
+int CIOSocket::listen(char *hostname, unsigned int port) {
+       return this->listen(hostname, port, IOSOCKET_ADDR_IPV6 | IOSOCKET_ADDR_IPV4);
+}
+int CIOSocket::listen(char *hostname, unsigned int port, int flags) {
+       this->iosocket = iosocket_listen_flags(hostname, port, c_socket_callback, flags);
+       if(this->iosocket) {
+               this->iosocket->data = this;
+               return 1;
+       } else
+               return 0;
+}
+
+int CIOSocket::listen_ssl(char *hostname, unsigned int port, char *certfile, char *keyfile) {
+       return listen_ssl(hostname, port, certfile, keyfile, IOSOCKET_ADDR_IPV6 | IOSOCKET_ADDR_IPV4);
+}
+int CIOSocket::listen_ssl(char *hostname, unsigned int port, char *certfile, char *keyfile, int flags) {
+       this->iosocket = iosocket_listen_ssl_flags(hostname, port, certfile, keyfile, c_socket_callback, flags);
+       if(this->iosocket) {
+               this->iosocket->data = this;
+               return 1;
+       } else
+               return 0;
+}
+
+void CIOSocket::write(const char *data, int len) {
+       iosocket_send(iosocket, data, len);
+}
+#define IOSOCKET_PRINTF_LEN 2048
+void CIOSocket::writef(const char *format, ...) {
+       va_list arg_list;
+       char sendBuf[IOSOCKET_PRINTF_LEN];
+       int pos;
+       sendBuf[0] = '\0';
+       va_start(arg_list, format);
+       pos = vsnprintf(sendBuf, IOSOCKET_PRINTF_LEN - 1, format, arg_list);
+       va_end(arg_list);
+       if (pos < 0 || pos > (IOSOCKET_PRINTF_LEN - 1)) pos = IOSOCKET_PRINTF_LEN - 1;
+       sendBuf[pos] = '\0';
+       iosocket_send(iosocket, sendBuf, pos);
+}
+
+void CIOSocket::close() {
+       iosocket_close(iosocket);
+};
+
+
+void CIOSocket::socket_callback(IOSocketEvent *event) {
+       switch(event->type) {
+       case IOSOCKETEVENT_RECV:
+               if(iosocket->parse_delimiter)
+                       this->recvLine(event->data.recv_str);
+               else {
+                       IOSocketBuffer *recvbuf = event->data.recv_buf;
+                       int usedlen;
+                       usedlen = this->recvEvent(recvbuf->buffer, recvbuf->bufpos);
+                       if(usedlen == recvbuf->bufpos) {
+                               recvbuf->bufpos = 0;
+                       } else {
+                               memmove(recvbuf->buffer, recvbuf->buffer + usedlen, recvbuf->bufpos - usedlen);
+                               recvbuf->bufpos -= usedlen;
+                       }
+               }
+               break;
+    case IOSOCKETEVENT_CONNECTED:
+               this->connectedEvent();
+               break;
+       case IOSOCKETEVENT_NOTCONNECTED:
+               this->notConnectedEvent(event->data.errid);
+               break;
+       case IOSOCKETEVENT_CLOSED:
+               this->closedEvent(event->data.errid);
+               break;
+       case IOSOCKETEVENT_ACCEPT:
+               this->acceptedEvent(new CIOSocket(event->data.accept_socket));
+               break;
+       case IOSOCKETEVENT_DNSFAILED:
+               this->dnsErrEvent(event->data.recv_str);
+               break;
+       }
+}
+
+int CIOSocket::getSSL() {
+       
+}
+int CIOSocket::getIPv6() {
+       
+}
+int CIOSocket::getConnected() {
+       
+}
+int CIOSocket::getListening() {
+       
+}
diff --git a/src/IOHandler++/IOSocket.h b/src/IOHandler++/IOSocket.h
new file mode 100644 (file)
index 0000000..3c886d3
--- /dev/null
@@ -0,0 +1,74 @@
+/* IOSocket.h - IOMultiplexer v2
+ * Copyright (C) 2014  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 _IOSocket_cpp_h
+#define _IOSocket_cpp_h
+
+extern "C" {
+       #define IOSOCKET_CPP
+       #include "../IOHandler/IOSockets.h"
+}
+#include <iostream>
+#include <string>
+
+struct IOSocket;
+
+class CIOSocket {
+public:
+       CIOSocket();
+       
+       int connect(char *hostname, unsigned int port, int ssl, char *bindhost);
+       int connect(char *hostname, unsigned int port, int ssl, char *bindhost, int flags);
+       
+       int listen(char *hostname, unsigned int port);
+       int listen(char *hostname, unsigned int port, int flags);
+       int listen_ssl(char *hostname, unsigned int port, char *certfile, char *keyfile);
+       int listen_ssl(char *hostname, unsigned int port, char *certfile, char *keyfile, int flags);
+       
+       void write(const char *data, int len);
+       void writef(const char *format, ...);
+       
+       CIOSocket accept();
+       
+       void close();
+       
+       
+       int getSSL();
+       int getIPv6();
+       int getConnected();
+       int getListening();
+       
+       
+       void socket_callback(IOSocketEvent *event);
+protected:
+       virtual int recvEvent(const char *data, int len) { return len; };
+       virtual void recvLine(char *line) {};
+       void enableRecvLine();
+       void disableRecvLine();
+       
+       virtual void connectedEvent() {};
+       virtual void notConnectedEvent(int errid) {};
+       virtual void closedEvent(int errid) {};
+       virtual void acceptedEvent(CIOSocket *client) { client->close(); };
+       virtual void dnsErrEvent(char *errormsg) {};
+       
+private:
+       IOSocket *iosocket;
+       
+       CIOSocket(IOSocket *iosocket);
+};
+
+#endif
diff --git a/src/IOHandler++/IOTimer.cpp b/src/IOHandler++/IOTimer.cpp
new file mode 100644 (file)
index 0000000..e6a2cd7
--- /dev/null
@@ -0,0 +1,140 @@
+/* IOTimer.cpp - IOMultiplexer v2
+ * Copyright (C) 2014  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/>. 
+ */
+extern "C" {
+       #include "../IOHandler/IOTimer.h"
+}
+#include "IOTimer.h"
+#include <cstdarg>
+#include <cstdio>
+#include <cstring>
+
+static IOTIMER_CALLBACK(c_timer_callback) {
+       CIOTimer *ciotimer = (CIOTimer *) iotimer->data;
+       ciotimer->timer_callback();
+}
+
+CIOTimer::CIOTimer() {
+       this->iotimer = iotimer_create(NULL);
+       this->iotimer->data = this;
+       iotimer_set_callback(this->iotimer, c_timer_callback);
+       iotimer_set_persistent(this->iotimer, 1);
+}
+
+CIOTimer::~CIOTimer() {
+       iotimer_destroy(this->iotimer);
+}
+
+void CIOTimer::setTimeout(timeval timeout) {
+       iotimer_set_timeout(this->iotimer, &timeout);
+}
+
+void CIOTimer::setRelativeTimeout(timeval timeout) {
+       timeval now;
+       gettimeofday(&now, NULL);
+       
+       timeout.tv_sec += now.tv_sec;
+       timeout.tv_usec += now.tv_usec;
+       while(timeout.tv_usec > 1000000) {
+               timeout.tv_usec -= 1000000;
+               timeout.tv_sec++;
+       }
+       
+       this->setTimeout(timeout);
+}
+
+void CIOTimer::setRelativeTimeout(timeval timeout, int auto_reload) {
+       this->setRelativeTimeout(timeout);
+       if(auto_reload)
+               this->setAutoReload(timeout);
+}
+
+void CIOTimer::setRelativeTimeoutSeconds(double seconds) {
+       this->setRelativeTimeoutSeconds(seconds, 0);
+}
+
+void CIOTimer::setRelativeTimeoutSeconds(double seconds, int auto_reload) {
+       timeval tout;
+       tout.tv_sec = (int) seconds;
+       tout.tv_usec = ((int) (seconds * 1000000)) % 1000000;
+       this->setRelativeTimeout(tout);
+       if(auto_reload)
+               this->setAutoReload(tout);
+}
+
+timeval CIOTimer::getTimeout() {
+       return iotimer_get_timeout(this->iotimer);
+}
+
+timeval CIOTimer::getRelativeTimeout() {
+       timeval tout, now;
+       gettimeofday(&now, NULL);
+       
+       tout = iotimer_get_timeout(this->iotimer);
+       
+       if(tout.tv_sec || tout.tv_usec) {
+               tout.tv_sec = tout.tv_sec - now.tv_sec;
+               tout.tv_usec = tout.tv_usec - now.tv_usec;
+               if(tout.tv_usec < 0) {
+                       tout.tv_usec += 1000000;
+                       tout.tv_sec--;
+               }
+       }
+       
+       return tout;
+}
+
+double CIOTimer::getRelativeTimeoutSeconds() {
+       timeval tout = this->getRelativeTimeout();
+       return tout.tv_sec + (tout.tv_usec / 1000000);
+}
+
+
+void CIOTimer::setAutoReload(timeval timeout) {
+       iotimer_set_autoreload(this->iotimer, &timeout);
+}
+
+void CIOTimer::clearAutoReload() {
+       iotimer_set_autoreload(this->iotimer, NULL);
+}
+
+timeval CIOTimer::getAutoReloadTime() {
+       return iotimer_get_autoreload(this->iotimer);
+}
+
+int CIOTimer::getAutoReloadState() {
+       timeval timeout = this->getAutoReloadTime();
+       if(timeout.tv_sec == 0 && timeout.tv_usec == 0)
+               return 0;
+       else
+               return 1;
+}
+
+void CIOTimer::setActive(int active) {
+       if(active)
+               iotimer_start(this->iotimer);
+       else
+               iotimer_stop(this->iotimer);
+}
+
+int CIOTimer::getActive() {
+       return iotimer_state(this->iotimer);
+}
+
+void CIOTimer::timer_callback() {
+       this->timeout();
+}
+
diff --git a/src/IOHandler++/IOTimer.h b/src/IOHandler++/IOTimer.h
new file mode 100644 (file)
index 0000000..df586a5
--- /dev/null
@@ -0,0 +1,57 @@
+/* IOTimer.h - IOMultiplexer v2
+ * Copyright (C) 2014  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 _IOTimer_cpp_h
+#define _IOTimer_cpp_h
+
+#include <iostream>
+#include <string>
+#include <sys/time.h>
+
+struct IOTimerDescriptor;
+
+class CIOTimer {
+public:
+       CIOTimer();
+       ~CIOTimer();
+       
+       void setTimeout(timeval timeout);
+       timeval getTimeout();
+       void setRelativeTimeout(timeval timeout);
+       void setRelativeTimeout(timeval timeout, int auto_reload);
+       timeval getRelativeTimeout();
+       void setRelativeTimeoutSeconds(double seconds);
+       void setRelativeTimeoutSeconds(double seconds, int auto_reload);
+       double getRelativeTimeoutSeconds();
+       
+       void setAutoReload(timeval timeout);
+       void clearAutoReload();
+       int getAutoReloadState();
+       timeval getAutoReloadTime();
+       
+       void setActive(int active);
+       int getActive();
+       
+       
+       void timer_callback();
+protected:
+       virtual void timeout() {};
+       
+private:
+       IOTimerDescriptor *iotimer;
+};
+
+#endif
diff --git a/src/IOHandler++/Makefile.am b/src/IOHandler++/Makefile.am
new file mode 100644 (file)
index 0000000..681f9fb
--- /dev/null
@@ -0,0 +1,12 @@
+##Process this file with automake to create Makefile.in
+ACLOCAL_AMFLAGS = -I m4
+
+noinst_LTLIBRARIES = libiohandler.cpp.la
+
+libiohandler_cpp_la_LIBADD = ../IOHandler/libiohandler.la
+libiohandler_cpp_la_SOURCES = IOHandler.cpp \
+IOSocket.cpp \
+IOTimer.cpp
+
+all-local: libiohandler.cpp.la
+       
\ No newline at end of file
diff --git a/src/IOHandler/.gitignore b/src/IOHandler/.gitignore
new file mode 100644 (file)
index 0000000..1f36bcd
--- /dev/null
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile.in
+Makefile
+*.lo
+*.la
diff --git a/src/IOHandler/IODNSAddress.struct.h b/src/IOHandler/IODNSAddress.struct.h
new file mode 100644 (file)
index 0000000..64f6f31
--- /dev/null
@@ -0,0 +1,27 @@
+/* IODNSAddress.struct.h - IOMultiplexer v2
+ * Copyright (C) 2014  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 _IODNSAddress_struct_h
+#define _IODNSAddress_struct_h
+#include <sys/time.h>
+#include <stddef.h>
+
+struct IODNSAddress {
+       size_t addresslen;
+       struct sockaddr *address;
+};
+
+#endif
diff --git a/src/IOHandler/IODNSEngine_cares.c b/src/IOHandler/IODNSEngine_cares.c
new file mode 100644 (file)
index 0000000..e37df98
--- /dev/null
@@ -0,0 +1,365 @@
+/* IODNSEngine_cares.c - IOMultiplexer
+ * Copyright (C) 2014  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/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IODNSLookup.h"
+#include "IOLog.h"
+#include "IOSockets.h"
+#include "IOTimer.h"
+
+#ifdef HAVE_ARES_H
+#include <ares.h>
+#include <string.h>
+#include <sys/time.h>
+#ifdef WIN32
+#define _WIN32_WINNT 0x501
+#include <windows.h>
+#include <winsock2.h>
+#elif defined HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#endif
+
+#include "compat/inet.h"
+
+struct dnsengine_cares_socket {
+       struct _IOSocket *iosock;
+       int want_read : 1;
+       int want_write : 1;
+};
+
+struct dnsengine_cares_query {
+       int query_count;
+       int query_successful;
+       struct _IODNSQuery *iodns;
+};
+
+static IOTIMER_CALLBACK(dnsengine_cares_timer_callback);
+
+static ares_channel dnsengine_cares_channel;
+static struct dnsengine_cares_socket dnsengine_cares_sockets[ARES_GETSOCK_MAXNUM];
+static struct IOTimerDescriptor *dnsengine_cares_timer = NULL;
+
+static int dnsengine_cares_init() {
+       int res;
+       
+       // zero dnsengine_cares_sockets array
+       memset(dnsengine_cares_sockets, 0, sizeof(*dnsengine_cares_sockets) * ARES_GETSOCK_MAXNUM);
+       
+       // initialize cares
+       if((res = ares_init(&dnsengine_cares_channel)) != ARES_SUCCESS) {
+               iolog_trigger(IOLOG_ERROR, "Failed to initialize c-ares in %s:%d", __FILE__, __LINE__);
+               return 0;
+       }
+       return 1;
+}
+
+static void dnsengine_cares_update_sockets() {
+       int ares_socks[ARES_GETSOCK_MAXNUM];
+       memset(ares_socks, 0, sizeof(*ares_socks) * ARES_GETSOCK_MAXNUM);
+       int sockreqs = ares_getsock(dnsengine_cares_channel, ares_socks, ARES_GETSOCK_MAXNUM);
+       int i, j, sockid, newsock, updatesock;
+       struct _IOSocket *iosock;
+       
+       //unregister "old" sockets
+       for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
+               if(!dnsengine_cares_sockets[i].iosock)
+                       continue;
+               
+               //search matching ares_socks
+               sockid = -1;
+               for(j = 0; j < ARES_GETSOCK_MAXNUM; j++) {
+                       if(dnsengine_cares_sockets[i].iosock->fd == ares_socks[j]) {
+                               sockid = j;
+                               break;
+                       }
+               }
+               if(sockid == -1) {
+                       //unregister socket
+                       _free_socket(dnsengine_cares_sockets[i].iosock);
+                       dnsengine_cares_sockets[i].iosock = NULL;
+               }
+       }
+       
+       //register new / update existing sockets
+       for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
+               if(!ares_socks[i])
+                       break;
+               
+               //search matching dnsengine_cares_socket
+               sockid = -1;
+               for(j = 0; j < ARES_GETSOCK_MAXNUM; j++) {
+                       if(dnsengine_cares_sockets[j].iosock && dnsengine_cares_sockets[j].iosock->fd == ares_socks[i]) {
+                               sockid = j;
+                               break;
+                       }
+               }
+               
+               if(sockid == -1) {
+                       //append new socket
+                       for(j = 0; j < ARES_GETSOCK_MAXNUM; j++) {
+                               if(!dnsengine_cares_sockets[j].iosock) {
+                                       sockid = j;
+                                       break;
+                               }
+                       }
+                       if(sockid == -1) {
+                               iolog_trigger(IOLOG_ERROR, "Error in dnsengine_cares_update_sockets: could not find free dnsengine_cares_socket in %s:%d", __FILE__, __LINE__);
+                               continue;
+                       }
+                       iosock = _create_socket();
+                       if(!iosock)
+                               continue;
+                       
+                       //set up iosock
+                       iosock->socket_flags |= IOSOCKETFLAG_PARENT_DNSENGINE | IOSOCKETFLAG_OVERRIDE_WANT_RW;
+                       iosock->fd = ares_socks[i];
+                       dnsengine_cares_sockets[sockid].iosock = iosock;
+                       dnsengine_cares_sockets[sockid].want_read = 0;
+                       dnsengine_cares_sockets[sockid].want_write = 0;
+                       
+                       newsock = 1;
+               } else
+                       newsock = 0;
+               
+               updatesock = 0;
+               if(dnsengine_cares_sockets[sockid].want_read ^ ARES_GETSOCK_READABLE(sockreqs, i)) {
+                       if(ARES_GETSOCK_READABLE(sockreqs, i)) {
+                               dnsengine_cares_sockets[sockid].iosock->socket_flags |= IOSOCKETFLAG_OVERRIDE_WANT_R;
+                               dnsengine_cares_sockets[sockid].want_read = 1;
+                       } else {
+                               dnsengine_cares_sockets[sockid].iosock->socket_flags &= ~IOSOCKETFLAG_OVERRIDE_WANT_R;
+                               dnsengine_cares_sockets[sockid].want_read = 0;
+                       }
+                       updatesock = 1;
+               }
+               if(dnsengine_cares_sockets[sockid].want_write ^ ARES_GETSOCK_WRITABLE(sockreqs, i)) {
+                       if(ARES_GETSOCK_WRITABLE(sockreqs, i)) {
+                               dnsengine_cares_sockets[sockid].iosock->socket_flags |= IOSOCKETFLAG_OVERRIDE_WANT_W;
+                               dnsengine_cares_sockets[sockid].want_write = 1;
+                       } else {
+                               dnsengine_cares_sockets[sockid].iosock->socket_flags &= ~IOSOCKETFLAG_OVERRIDE_WANT_W;
+                               dnsengine_cares_sockets[sockid].want_write = 0;
+                       }
+                       updatesock = 1;
+               }
+               if(updatesock || newsock) {
+                       if(newsock)
+                               iosocket_activate(dnsengine_cares_sockets[sockid].iosock);
+                       else
+                               iosocket_update(dnsengine_cares_sockets[sockid].iosock);
+               }
+       }
+}
+
+static void dnsengine_cares_update_timeout() {
+       struct timeval timeout, now;
+       timeout.tv_sec = 60;
+       timeout.tv_usec = 0;
+       ares_timeout(dnsengine_cares_channel, &timeout, &timeout);
+       
+       gettimeofday(&now, NULL);
+       timeout.tv_sec += now.tv_sec;
+       timeout.tv_usec += now.tv_usec;
+       if(timeout.tv_usec > 1000000) {
+               timeout.tv_sec += 1;
+               timeout.tv_usec -= 1000000;
+       }
+       
+       if(dnsengine_cares_timer)
+               iotimer_set_timeout(dnsengine_cares_timer, &timeout);
+       else {
+               dnsengine_cares_timer = iotimer_create(&timeout);
+               iotimer_set_callback(dnsengine_cares_timer, dnsengine_cares_timer_callback);
+               iotimer_start(dnsengine_cares_timer);
+       }
+}
+
+static IOTIMER_CALLBACK(dnsengine_cares_timer_callback) {
+       dnsengine_cares_timer = NULL;
+       ares_process_fd(dnsengine_cares_channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
+       dnsengine_cares_update_timeout();
+       dnsengine_cares_update_sockets();
+}
+
+static void dnsengine_cares_socket_callback(struct _IOSocket *iosock, int wantread, int wantwrite) {
+       int socketfd = iosock->fd;
+       ares_process_fd(dnsengine_cares_channel, (wantread ? socketfd : ARES_SOCKET_BAD), (wantread ? socketfd : ARES_SOCKET_BAD));
+       dnsengine_cares_update_timeout();
+       dnsengine_cares_update_sockets();
+}
+
+static void dnsengine_cares_stop() {
+       if(dnsengine_cares_timer)
+               iotimer_destroy(dnsengine_cares_timer);
+}
+
+
+static void dnsengine_cares_callback(void *arg, int status, int timeouts, struct hostent *host) {
+       struct dnsengine_cares_query *query = arg;
+       struct _IODNSQuery *iodns = query->iodns;
+       query->query_count--;
+       if(iodns) {
+               if(!(iodns->flags & IODNSFLAG_RUNNING)) {
+                       // query stopped
+                       query->iodns = NULL;
+                       iodns = NULL;
+                       iodns_free_result(iodns->result);
+                       _free_dnsquery(iodns);
+               }
+               if(iodns && status == ARES_SUCCESS) {
+                       if((iodns->type & IODNS_FORWARD)) {
+                               char **h_addr;
+                               for(h_addr = host->h_addr_list; *h_addr; h_addr++) {
+                                       struct IODNSResult *dnsresult = malloc(sizeof(*dnsresult));
+                                       if(!dnsresult) {
+                                               iolog_trigger(IOLOG_ERROR, "Failed to allocate memory for IODNSResult in %s:%d", __FILE__, __LINE__);
+                                               goto dnsengine_cares_callback_finally;
+                                       }
+                                       
+                                       int sockaddrlen;
+                                       if(host->h_addrtype == AF_INET) {
+                                               dnsresult->type = IODNS_RECORD_A;
+                                               sockaddrlen = sizeof(struct sockaddr_in);
+                                       } else {
+                                               dnsresult->type = IODNS_RECORD_AAAA;
+                                               sockaddrlen = sizeof(struct sockaddr_in6);
+                                       }
+                                       dnsresult->result.addr.addresslen = sockaddrlen;
+                                       dnsresult->result.addr.address = malloc(sockaddrlen);
+                                       if(!dnsresult->result.addr.address) {
+                                               iolog_trigger(IOLOG_ERROR, "Failed to allocate memory for sockaddr in %s:%d", __FILE__, __LINE__);
+                                               goto dnsengine_cares_callback_finally;
+                                       }
+                                       void *target = (host->h_addrtype == AF_INET ? ((void *) &((struct sockaddr_in *)dnsresult->result.addr.address)->sin_addr) : ((void *) &((struct sockaddr_in6 *)dnsresult->result.addr.address)->sin6_addr));
+                                       memcpy(target, *h_addr, host->h_length);
+                                       
+                                       dnsresult->result.addr.address->sa_family = host->h_addrtype;
+                                       if(host->h_addrtype == AF_INET) {
+                                               char str[INET_ADDRSTRLEN];
+                                               inet_ntop( AF_INET, &((struct sockaddr_in *)dnsresult->result.addr.address)->sin_addr, str, INET_ADDRSTRLEN );
+                                               iolog_trigger(IOLOG_DEBUG, "Resolved %s to (A): %s", iodns->request.host, str);
+                                       } else {
+                                               char str[INET6_ADDRSTRLEN];
+                                               inet_ntop( AF_INET6, &((struct sockaddr_in6 *)dnsresult->result.addr.address)->sin6_addr, str, INET6_ADDRSTRLEN );
+                                               iolog_trigger(IOLOG_DEBUG, "Resolved %s to (AAAA): %s", iodns->request.host, str);
+                                       }
+                                       
+                                       dnsresult->next = iodns->result;
+                                       iodns->result = dnsresult;
+                               }
+                               
+                       } else if((iodns->type & IODNS_REVERSE)) {
+                               struct IODNSResult *dnsresult = malloc(sizeof(*dnsresult));
+                               if(!dnsresult) {
+                                       iolog_trigger(IOLOG_ERROR, "Failed to allocate memory for IODNSResult in %s:%d", __FILE__, __LINE__);
+                                       goto dnsengine_cares_callback_finally;
+                               }
+                               
+                               dnsresult->type = IODNS_RECORD_PTR;
+                               dnsresult->result.host = strdup(host->h_name);
+                               if(!dnsresult->result.host) {
+                                       iolog_trigger(IOLOG_ERROR, "Failed to duplicate h_name string for IODNSResult in %s:%d", __FILE__, __LINE__);
+                                       goto dnsengine_cares_callback_finally;
+                               }
+                               
+                               dnsresult->next = iodns->result;
+                               iodns->result = dnsresult;
+                       }
+                       
+                       query->query_successful++;
+               }
+       }
+       dnsengine_cares_callback_finally:
+       if(query->query_count <= 0) {
+               if(iodns) {
+                       iodns->flags &= ~(IODNSFLAG_PROCESSING | IODNSFLAG_RUNNING);
+                       iodns_event_callback(iodns, (query->query_successful ? IODNSEVENT_SUCCESS : IODNSEVENT_FAILED));
+               }
+               free(query);
+       }
+}
+
+static void dnsengine_cares_add(struct _IODNSQuery *iodns) {
+       struct dnsengine_cares_query *query = malloc(sizeof(*query));
+       if(!query) {
+               iolog_trigger(IOLOG_ERROR, "Failed to allocate memory for dnsengine_cares_query in %s:%d", __FILE__, __LINE__);
+               iodns_event_callback(iodns, IODNSEVENT_FAILED);
+               return;
+       }
+       iodns->query = query;
+       query->query_count = 0;
+       query->query_successful = 0;
+       query->iodns = iodns;
+       iodns->flags |= IODNSFLAG_PROCESSING;
+       if((iodns->type & IODNS_FORWARD)) {
+               if((iodns->type & IODNS_RECORD_A)) {
+                       query->query_count++;
+                       ares_gethostbyname(dnsengine_cares_channel, iodns->request.host, AF_INET, dnsengine_cares_callback, query);
+               }
+               if((iodns->type & IODNS_RECORD_AAAA)) {
+                       query->query_count++;
+                       ares_gethostbyname(dnsengine_cares_channel, iodns->request.host, AF_INET6, dnsengine_cares_callback, query);
+               }
+       } else if((iodns->type & IODNS_REVERSE)) {
+               query->query_count++;
+               struct sockaddr *addr = iodns->request.addr.address;
+               if(addr->sa_family == AF_INET) {
+                       struct sockaddr_in *addr4 = (struct sockaddr_in *) iodns->request.addr.address;
+                       ares_gethostbyaddr(dnsengine_cares_channel, &addr4->sin_addr, sizeof(addr4->sin_addr), addr->sa_family, dnsengine_cares_callback, query);
+               } else {
+                       struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)iodns->request.addr.address;
+                       ares_gethostbyaddr(dnsengine_cares_channel, &addr6->sin6_addr, sizeof(addr6->sin6_addr), addr->sa_family, dnsengine_cares_callback, query);
+               }
+       }
+       dnsengine_cares_update_timeout();
+       dnsengine_cares_update_sockets();
+}
+
+static void dnsengine_cares_remove(struct _IODNSQuery *iodns) {
+       /* empty */
+}
+
+static void dnsengine_cares_loop() {
+       /* empty */
+}
+
+struct IODNSEngine dnsengine_cares = {
+       .name = "c-ares",
+       .init = dnsengine_cares_init,
+       .stop = dnsengine_cares_stop,
+       .add = dnsengine_cares_add,
+       .remove = dnsengine_cares_remove,
+       .loop = dnsengine_cares_loop,
+       .socket_callback = dnsengine_cares_socket_callback,
+};
+
+#else
+
+struct IODNSEngine dnsengine_cares = {
+       .name = "c-ares",
+       .init = NULL,
+       .stop = NULL,
+       .add = NULL,
+       .remove = NULL,
+       .loop = NULL,
+       .socket_callback = NULL,
+};
+
+#endif
diff --git a/src/IOHandler/IODNSEngine_default.c b/src/IOHandler/IODNSEngine_default.c
new file mode 100644 (file)
index 0000000..b818927
--- /dev/null
@@ -0,0 +1,292 @@
+/* IODNSEngine_default.c - IOMultiplexer
+ * Copyright (C) 2014  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/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IOLog.h"
+#include "IODNSLookup.h"
+
+#ifdef WIN32
+#define _WIN32_WINNT 0x501
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#endif
+#include "compat/inet.h"
+#include <stdlib.h>
+#include <string.h>
+
+
+#ifdef IODNS_USE_THREADS
+#define IODNS_MAX_THREAD 10
+#define IODNS_INC_THREAD_BY_LOAD 5 /* add another thread when there are more than IODNS_INC_THREAD_BY_LOAD querys per thread */
+static pthread_t *iodns_thread[IODNS_MAX_THREAD];
+static int iodns_threads_wanted = 1;
+static int iodns_threads_running = 0;
+
+static pthread_cond_t iodns_cond;
+static pthread_mutex_t iodns_sync, iodns_sync2;
+#endif
+static int iodns_loop_blocking = 0;
+
+static void iodns_process_queries();
+
+#ifdef IODNS_USE_THREADS
+static void *dnsengine_worker_main(void *arg) {
+       struct _IODNSQuery *query;
+       while(1) {
+               IOSYNCHRONIZE(iodns_sync);
+               if(iodns_threads_wanted < iodns_threads_running) {
+                       iodns_threads_running--;
+                       break;
+               }
+               
+               for(query = iodnsquery_first; query; query = query->next) {
+                       if((query->flags & IODNSFLAG_RUNNING))
+                               break;
+               }
+               IODESYNCHRONIZE(iodns_sync);
+               if(!query)
+                       pthread_cond_wait(&iodns_cond, &iodns_sync2);
+               
+               if(iodns_threads_wanted < iodns_threads_running) {
+                       iodns_threads_running--;
+                       break;
+               }
+               
+               iodns_process_queries();
+       }
+       return NULL;
+}
+
+static int dnsengine_default_start_worker() {
+       if(iodns_threads_wanted >= IODNS_MAX_THREAD-1)
+               return 0;
+       int i;
+       for(i = 0; i < IODNS_MAX_THREAD; i++) {
+               if(!iodns_thread[i]) 
+                       break;
+       }
+       if(i >= IODNS_MAX_THREAD)
+               return 0;
+       iodns_thread[i] = malloc(sizeof(**iodns_thread));
+       if(!iodns_thread[i])
+               return 0;
+       iodns_threads_wanted++;
+       if(pthread_create(iodns_thread[i], NULL, dnsengine_worker_main, NULL)) {
+               iodns_threads_wanted--;
+               iolog_trigger(IOLOG_ERROR, "could not create pthread in %s:%d (Returned: %i)", __FILE__, __LINE__, thread_err);
+               return 0;
+       }
+       iodns_threads_running++;
+       return 1;
+}
+#endif
+
+static int dnsengine_default_init() {
+       #ifdef IODNS_USE_THREADS
+       /* create worker thread */
+       pthread_cond_init(&iodns_cond, NULL);
+       IOTHREAD_MUTEX_INIT(iodns_sync);
+       IOTHREAD_MUTEX_INIT(iodns_sync2);
+       
+       if(!dnsengine_default_start_worker()) {
+               iodns_loop_blocking = 1;
+               iodns_threads_running = 0;
+       }
+       #else
+       iodns_loop_blocking = 1;
+       #endif
+       return 1;
+}
+
+static void dnsengine_default_stop() {
+       #ifdef IODNS_USE_THREADS
+       int i;
+       if(iodns_thread_running) {
+               iodns_threads_wanted = 0;
+               IOSYNCHRONIZE(iodns_sync2);
+               pthread_cond_broadcast(&iodns_cond);
+               IODESYNCHRONIZE(iodns_sync2);
+               for(i = 0; i < IODNS_MAX_THREAD; i++) {
+                       if(iodns_thread[i]) {
+                               pthread_join(*iodns_thread[i], NULL);
+                               free(iodns_thread[i]);
+                               iodns_thread[i] = NULL;
+                       }
+               }
+       }
+       #endif
+}
+
+static void dnsengine_default_add(struct _IODNSQuery *iodns) {
+       #ifdef IODNS_USE_THREADS
+       if(iodns_thread_running) {
+               IOSYNCHRONIZE(iodns_sync2);
+               pthread_cond_signal(&iodns_cond);
+               IODESYNCHRONIZE(iodns_sync2);
+               
+               int querycount = 0;
+               for(iodns = iodnsquery_first; iodns; iodns = iodns->next) {
+                       if(!(iodns->flags & IODNSFLAG_RUNNING))
+                               continue;
+                       if((iodns->flags & IODNSFLAG_PROCESSING))
+                               continue;
+                       querycount++;
+               }
+               if(querycount / iodns_threads_wanted > IODNS_INC_THREAD_BY_LOAD) {
+                       dnsengine_default_start_worker();
+               }
+       }
+       #endif
+}
+
+static void dnsengine_default_remove(struct _IODNSQuery *iodns) {
+       /* unused */
+}
+
+static void dnsengine_default_loop() {
+       if(iodns_loop_blocking)
+               iodns_process_queries();
+}
+
+static void iodns_process_queries() {
+       enum IODNSEventType querystate;
+       struct addrinfo hints, *res, *allres;
+       struct _IODNSQuery *iodns, *next_iodns;
+       struct IODNSResult *dnsresult;
+       int ret;
+       iodns_process_queries_start:
+       IOSYNCHRONIZE(iodns_sync);
+       for(iodns = iodnsquery_first; iodns; iodns = next_iodns) {
+               next_iodns = iodns->next;
+               
+               if(!(iodns->flags & IODNSFLAG_RUNNING))
+                       continue;
+               if((iodns->flags & IODNSFLAG_PROCESSING))
+                       continue;
+               iodns->flags |= IODNSFLAG_PROCESSING;
+               
+               IODESYNCHRONIZE(iodns_sync);
+               
+               querystate = IODNSEVENT_FAILED;
+               
+               if((iodns->type & IODNS_FORWARD)) {
+                       memset (&hints, 0, sizeof (hints));
+                       hints.ai_family = PF_UNSPEC;
+                       hints.ai_socktype = SOCK_STREAM;
+                       hints.ai_flags |= AI_CANONNAME;
+                       if (!(ret = getaddrinfo(iodns->request.host, NULL, &hints, &allres))) {
+                               res = allres;
+                               while (res) {
+                                       switch (res->ai_family) {
+                                       case AF_INET:
+                                               if((iodns->type & IODNS_RECORD_A)) {
+                                                       dnsresult = malloc(sizeof(*dnsresult));
+                                                       dnsresult->type = IODNS_RECORD_A;
+                                                       dnsresult->result.addr.addresslen = res->ai_addrlen;
+                                                       dnsresult->result.addr.address = calloc(dnsresult->result.addr.addresslen, 1);
+                                                       dnsresult->result.addr.address->sa_family = AF_INET;
+                                                       memcpy(dnsresult->result.addr.address, res->ai_addr, dnsresult->result.addr.addresslen);
+                                                       dnsresult->next = iodns->result;
+                                                       iodns->result = dnsresult;
+                                                       
+                                                       char str[INET_ADDRSTRLEN];
+                                                       inet_ntop( AF_INET, &((struct sockaddr_in *)dnsresult->result.addr.address)->sin_addr, str, INET_ADDRSTRLEN );
+                                                       iolog_trigger(IOLOG_DEBUG, "Resolved %s to (A): %s", iodns->request.host, str);
+                                                       
+                                                       querystate = IODNSEVENT_SUCCESS;
+                                               }
+                                               break;
+                                       case AF_INET6:
+                                               if((iodns->type & IODNS_RECORD_AAAA)) {
+                                                       dnsresult = malloc(sizeof(*dnsresult));
+                                                       dnsresult->type = IODNS_RECORD_AAAA;
+                                                       dnsresult->result.addr.addresslen = res->ai_addrlen;
+                                                       dnsresult->result.addr.address = calloc(dnsresult->result.addr.addresslen, 1);
+                                                       dnsresult->result.addr.address->sa_family = AF_INET6;
+                                                       memcpy(dnsresult->result.addr.address, res->ai_addr, dnsresult->result.addr.addresslen);
+                                                       dnsresult->next = iodns->result;
+                                                       iodns->result = dnsresult;
+                                                       
+                                                       char str[INET6_ADDRSTRLEN];
+                                                       inet_ntop( AF_INET6, &((struct sockaddr_in6 *)dnsresult->result.addr.address)->sin6_addr, str, INET6_ADDRSTRLEN );
+                                                       iolog_trigger(IOLOG_DEBUG, "Resolved %s to (AAAA): %s", iodns->request.host, str);
+                                                       
+                                                       querystate = IODNSEVENT_SUCCESS;
+                                               }
+                                               break;
+                                       }
+                                       res = res->ai_next;
+                               }
+                               freeaddrinfo(allres);
+                       } else {
+                               iolog_trigger(IOLOG_WARNING, "getaddrinfo returned error code: %d", ret);
+                       }
+               } else if((iodns->type & IODNS_REVERSE)) {
+                       char hostname[NI_MAXHOST];
+                       if(!(ret = getnameinfo(iodns->request.addr.address, iodns->request.addr.addresslen, hostname, sizeof(hostname), NULL, 0, 0))) {
+                               dnsresult = malloc(sizeof(*dnsresult));
+                               dnsresult->type = IODNS_RECORD_PTR;
+                               dnsresult->result.host = strdup(hostname);
+                               dnsresult->next = iodns->result;
+                               iodns->result = dnsresult;
+                               
+                               if(iodns->request.addr.address->sa_family == AF_INET) {
+                                       char str[INET_ADDRSTRLEN];
+                                       inet_ntop(AF_INET, &((struct sockaddr_in *)iodns->request.addr.address)->sin_addr, str, INET_ADDRSTRLEN);
+                                       iolog_trigger(IOLOG_DEBUG, "Resolved %s to (PTR): %s", str, hostname);
+                               } else {
+                                       char str[INET6_ADDRSTRLEN];
+                                       inet_ntop(AF_INET6, &((struct sockaddr_in6 *)iodns->request.addr.address)->sin6_addr, str, INET6_ADDRSTRLEN);
+                                       iolog_trigger(IOLOG_DEBUG, "Resolved %s to (PTR): %s", str, hostname);
+                               }
+                               
+                               querystate = IODNSEVENT_SUCCESS;
+                       } else {
+                               iolog_trigger(IOLOG_WARNING, "getnameinfo returned error code: %d", ret);
+                       }
+                       
+               }
+               IOSYNCHRONIZE(iodns_sync);
+               if(!(iodns->flags & IODNSFLAG_RUNNING)) {
+                       iodns_free_result(iodns->result);
+                       _free_dnsquery(iodns);
+                       IODESYNCHRONIZE(iodns_sync);
+                       goto iodns_process_queries_start;
+               }
+               iodns->flags &= ~(IODNSFLAG_PROCESSING | IODNSFLAG_RUNNING);
+               IODESYNCHRONIZE(iodns_sync);
+               iodns_event_callback(iodns, querystate);
+               goto iodns_process_queries_start;
+       }
+}
+
+struct IODNSEngine dnsengine_default = {
+       .name = "default",
+       .init = dnsengine_default_init,
+       .stop = dnsengine_default_stop,
+       .add = dnsengine_default_add,
+       .remove = dnsengine_default_remove,
+       .loop = dnsengine_default_loop,
+       .socket_callback = NULL,
+};
diff --git a/src/IOHandler/IODNSLookup.c b/src/IOHandler/IODNSLookup.c
new file mode 100644 (file)
index 0000000..f458bf5
--- /dev/null
@@ -0,0 +1,230 @@
+/* IODNSLookup.c - IOMultiplexer
+ * Copyright (C) 2014  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/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IODNSLookup.h"
+#include "IOLog.h"
+#include "IOSockets.h"
+
+#include <string.h>
+
+struct _IODNSQuery *iodnsquery_first = NULL;
+struct _IODNSQuery *iodnsquery_last = NULL;
+
+struct IODNSEngine *dnsengine = NULL;
+
+static void iodns_init_engine() {
+       if(dnsengine)
+               return;
+       //try DNS engines
+       if(dnsengine_cares.init && dnsengine_cares.init())
+               dnsengine = &dnsengine_cares;
+       else if(dnsengine_default.init && dnsengine_default.init())
+               dnsengine = &dnsengine_default;
+       else {
+               iolog_trigger(IOLOG_FATAL, "found no useable IO DNS engine");
+               return;
+       }
+       iolog_trigger(IOLOG_DEBUG, "using %s IODNS engine", dnsengine->name);
+}
+
+void _init_iodns() {
+       iodns_init_engine();
+}
+
+struct _IODNSQuery *_create_dnsquery() {
+       struct _IODNSQuery *query = calloc(1, sizeof(*query));
+       if(!query) {
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for _IODNSQuery in %s:%d", __FILE__, __LINE__);
+               return NULL;
+       }
+       if(iodnsquery_last)
+               iodnsquery_last->next = query;
+       else
+               iodnsquery_first = query;
+       query->prev = iodnsquery_last;
+       iodnsquery_last = query;
+       return query;
+}
+
+void _start_dnsquery(struct _IODNSQuery *query) {
+       query->flags |= IODNSFLAG_RUNNING;
+       dnsengine->add(query);
+}
+
+void _free_dnsquery(struct _IODNSQuery *query) {
+       if(query->prev)
+               query->prev->next = query->next;
+       else
+               iodnsquery_first = query->next;
+       if(query->next)
+               query->next->prev = query->prev;
+       else
+               iodnsquery_last = query->prev;
+       if((query->type & IODNS_REVERSE) && query->request.addr.address)
+               free(query->request.addr.address);
+       free(query);
+}
+
+void _stop_dnsquery(struct _IODNSQuery *query) {
+       if((query->flags & IODNSFLAG_RUNNING)) {
+               query->flags &= ~IODNSFLAG_RUNNING;
+               dnsengine->remove(query);
+       }
+       if(!(query->flags & IODNSFLAG_PROCESSING))
+               _free_dnsquery(query);
+}
+
+void iodns_socket_callback(struct _IOSocket *iosock, int wantread, int wantwrite) {
+       if(dnsengine && dnsengine->socket_callback)
+               dnsengine->socket_callback(iosock, wantread, wantwrite);
+}
+
+void iodns_event_callback(struct _IODNSQuery *query, enum IODNSEventType state) {
+       if((query->flags & IODNSFLAG_PARENT_PUBLIC)) {
+               struct IODNSQuery *descriptor = query->parent;
+               struct IODNSEvent event;
+               event.type = state;
+               event.query = descriptor;
+               event.result = query->result;
+               
+               descriptor->query = NULL;
+               _stop_dnsquery(query);
+               
+               if(descriptor->callback)
+                       descriptor->callback(&event);
+               
+               iogc_add(descriptor);
+       } else if((query->flags & IODNSFLAG_PARENT_SOCKET)) {
+               struct IODNSEvent event;
+               event.type = state;
+               event.query = NULL;
+               event.result = query->result;
+               void *parent = query->parent;
+               
+               _stop_dnsquery(query);
+               iosocket_lookup_callback(parent, &event);
+               
+       }
+}
+
+void iodns_poll() {
+       if(dnsengine)
+               dnsengine->loop();
+}
+
+/* public functions */
+
+struct IODNSQuery *iodns_getaddrinfo(char *hostname, int records, iodns_callback *callback, void *arg) {
+       if(!(records & IODNS_FORWARD) || !hostname || !callback)
+               return NULL;
+       
+       struct IODNSQuery *descriptor = calloc(1, sizeof(*descriptor));
+       if(!descriptor) {
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for IODNSQuery in %s:%d", __FILE__, __LINE__);
+               return NULL;
+       }
+       
+       struct _IODNSQuery *query = _create_dnsquery();
+       if(!query) {
+               free(descriptor);
+               return NULL;
+       }
+       
+       query->parent = descriptor;
+       query->flags |= IODNSFLAG_PARENT_PUBLIC;
+       descriptor->query = query;
+       descriptor->data = arg;
+       
+       query->request.host = strdup(hostname);
+       query->type = (records & IODNS_FORWARD);
+       
+       descriptor->callback = callback;
+       
+       _start_dnsquery(query);
+       return descriptor;
+}
+
+struct IODNSQuery *iodns_getnameinfo(const struct sockaddr *addr, size_t addrlen, iodns_callback *callback, void *arg) {
+       if(!addr || !callback)
+               return NULL;
+       
+       struct IODNSQuery *descriptor = calloc(1, sizeof(*descriptor));
+       if(!descriptor) {
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for IODNSQuery in %s:%d", __FILE__, __LINE__);
+               return NULL;
+       }
+       
+       struct _IODNSQuery *query = _create_dnsquery();
+       if(!query) {
+               free(descriptor);
+               return NULL;
+       }
+       
+       query->parent = descriptor;
+       query->flags |= IODNSFLAG_PARENT_PUBLIC;
+       descriptor->query = query;
+       descriptor->data = arg;
+       
+       query->type = IODNS_RECORD_PTR;
+       query->request.addr.addresslen = addrlen;
+       query->request.addr.address = malloc(addrlen);
+       if(!query->request.addr.address) {
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for sockaddr in %s:%d", __FILE__, __LINE__);
+               _free_dnsquery(query);
+               free(descriptor);
+               return NULL;
+       }
+       memcpy(query->request.addr.address, addr, addrlen);
+       
+       descriptor->callback = callback;
+       
+       _start_dnsquery(query);
+       return descriptor;
+}
+
+void iodns_abort(struct IODNSQuery *descriptor) {
+       if(!descriptor)
+               return;
+       
+       struct _IODNSQuery *query = descriptor->query;
+       if(!query) {
+               iolog_trigger(IOLOG_WARNING, "called iodns_abort for destroyed IODNSQuery in %s:%d", __FILE__, __LINE__);
+               return;
+       }
+       
+       _stop_dnsquery(query);
+}
+
+void iodns_free_result(struct IODNSResult *result) {
+       struct IODNSResult *next;
+       for(;result;result = next) {
+               next = result->next;
+               
+               if((result->type & IODNS_FORWARD)) {
+                       if(result->result.addr.address)
+                               free(result->result.addr.address);
+               }
+               if((result->type & IODNS_REVERSE)) {
+                       if(result->result.host)
+                               free(result->result.host);
+               }
+               free(result);
+       }
+}
+
diff --git a/src/IOHandler/IODNSLookup.h b/src/IOHandler/IODNSLookup.h
new file mode 100644 (file)
index 0000000..78113ee
--- /dev/null
@@ -0,0 +1,157 @@
+/* IODNSLookup.h - IOMultiplexer v2
+ * Copyright (C) 2014  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 _IODNSLookup_h
+#define _IODNSLookup_h
+#include <stdlib.h>
+#include "IODNSAddress.struct.h"
+
+#ifndef _IOHandler_internals
+#include "IOHandler.h"
+#else
+
+struct IODNSEngine;
+extern struct IODNSEngine dnsengine_cares;
+extern struct IODNSEngine dnsengine_default;
+
+struct _IODNSQuery;
+extern struct _IODNSQuery *iodnsquery_first;
+extern struct _IODNSQuery *iodnsquery_last;
+
+/* Multithreading */
+#ifdef IODNS_USE_THREADS
+#ifndef HAVE_PTHREAD_H
+#undef IODNS_USE_THREADS
+#endif
+#endif
+#ifdef IODNS_USE_THREADS
+#include <pthread.h>
+#ifdef PTHREAD_MUTEX_RECURSIVE_NP
+#define PTHREAD_MUTEX_RECURSIVE_VAL PTHREAD_MUTEX_RECURSIVE_NP
+#else
+#define PTHREAD_MUTEX_RECURSIVE_VAL PTHREAD_MUTEX_RECURSIVE
+#endif
+#define IOTHREAD_MUTEX_INIT(var) { \
+       pthread_mutexattr_t mutex_attr; \
+       pthread_mutexattr_init(&mutex_attr);\
+       pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE_VAL);\
+       pthread_mutex_init(&var, &mutex_attr); \
+}
+#define IOSYNCHRONIZE(var) pthread_mutex_lock(&var)
+#define IODESYNCHRONIZE(var) pthread_mutex_unlock(&var)
+#else
+#define IOTHREAD_MUTEX_INIT(var)
+#define IOSYNCHRONIZE(var)
+#define IODESYNCHRONIZE(var)
+#endif
+
+#define IODNSFLAG_RUNNING        0x01
+#define IODNSFLAG_PROCESSING     0x02
+#define IODNSFLAG_PARENT_PUBLIC  0x04
+#define IODNSFLAG_PARENT_SOCKET  0x08
+
+struct IODNSResult;
+struct _IOSocket;
+
+struct _IODNSQuery {
+       void *query;
+       
+       unsigned int flags : 8;
+       unsigned int type : 8;
+       union {
+               struct IODNSAddress addr;
+               char *host;
+       } request;
+       
+       struct IODNSResult *result;
+       
+       void *parent;
+       
+       struct _IODNSQuery *next, *prev;
+};
+
+struct IODNSEngine {
+       const char *name;
+       int (*init)();
+       void (*stop)();
+       void (*add)(struct _IODNSQuery *query);
+       void (*remove)(struct _IODNSQuery *query);
+       void (*loop)();
+       void (*socket_callback)(struct _IOSocket *iosock, int readable, int writeable);
+};
+
+void _init_iodns();
+void _stop_iodns();
+struct _IODNSQuery *_create_dnsquery();
+void _start_dnsquery(struct _IODNSQuery *query);
+void _stop_dnsquery(struct _IODNSQuery *query);
+
+/* call only from engines! */
+enum IODNSEventType;
+void _free_dnsquery(struct _IODNSQuery *query);
+void iodns_socket_callback(struct _IOSocket *iosock, int wantread, int wantwrite);
+void iodns_event_callback(struct _IODNSQuery *query, enum IODNSEventType state);
+void iodns_poll();
+
+#endif
+
+struct IODNSEvent;
+struct sockaddr;
+
+#define IODNS_CALLBACK(NAME) void NAME(struct IODNSEvent *event)
+typedef IODNS_CALLBACK(iodns_callback);
+
+enum IODNSEventType {
+       IODNSEVENT_SUCCESS,
+       IODNSEVENT_FAILED
+};
+
+#define IODNS_RECORD_A    0x01
+#define IODNS_RECORD_AAAA 0x02
+#define IODNS_RECORD_PTR  0x04
+
+#define IODNS_FORWARD     0x03
+#define IODNS_REVERSE     0x04
+
+struct IODNSQuery {
+       void *query;
+       
+       iodns_callback *callback;
+       void *data;
+};
+
+struct IODNSResult {
+       unsigned int type : 8;
+       union {
+               struct IODNSAddress addr;
+               char *host;
+       } result;
+       struct IODNSResult *next;
+};
+
+struct IODNSEvent {
+       enum IODNSEventType type;
+       struct IODNSQuery *query;
+       struct IODNSResult *result;
+};
+
+struct IODNSQuery *iodns_getaddrinfo(char *hostname, int records, iodns_callback *callback, void *arg);
+struct IODNSQuery *iodns_getnameinfo(const struct sockaddr *addr, size_t addrlen, iodns_callback *callback, void *arg);
+void iodns_abort(struct IODNSQuery *query);
+
+void iodns_free_result(struct IODNSResult *result);
+
+#endif
diff --git a/src/IOHandler/IOEngine_epoll.c b/src/IOHandler/IOEngine_epoll.c
new file mode 100644 (file)
index 0000000..5c5ab38
--- /dev/null
@@ -0,0 +1,141 @@
+/* IOEngine_epoll.c - IOMultiplexer
+ * Copyright (C) 2014  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/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IOLog.h"
+#include "IOSockets.h"
+#include "IOTimer.h"
+
+#ifdef HAVE_SYS_EPOLL_H
+#include <sys/epoll.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MAX_EVENTS 32
+
+static int epoll_fd;
+
+static int engine_epoll_init() {
+       epoll_fd = epoll_create(IOHANDLER_MAX_SOCKETS);
+       if (epoll_fd < 0)
+               return 0;
+       return 1;
+}
+
+static void engine_epoll_add(struct _IOSocket *iosock) {
+       //add Socket FD to the epoll queue
+       struct epoll_event evt;
+       int res;
+
+       evt.events = EPOLLHUP | (iosocket_wants_reads(iosock) ? EPOLLIN : 0) | (iosocket_wants_writes(iosock) ? EPOLLOUT : 0);
+       evt.data.ptr = iosock;
+       res = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, iosock->fd, &evt);
+       if(res < 0)
+               iolog_trigger(IOLOG_ERROR, "could not add _IOSocket %d to epoll queue. (returned: %d)", iosock->fd, res);
+}
+
+static void engine_epoll_remove(struct _IOSocket *iosock) {
+       struct epoll_event evt;
+       epoll_ctl(epoll_fd, EPOLL_CTL_DEL, iosock->fd, &evt);
+}
+
+static void engine_epoll_update(struct _IOSocket *iosock) {
+       struct epoll_event evt;
+       int res;
+
+       evt.events = EPOLLHUP | (iosocket_wants_reads(iosock) ? EPOLLIN : 0) | (iosocket_wants_writes(iosock) ? EPOLLOUT : 0);
+       evt.data.ptr = iosock;
+       res = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, iosock->fd, &evt);
+       if(res < 0)
+               iolog_trigger(IOLOG_ERROR, "could not update _IOSocket %d in epoll queue. (returned: %d)", iosock->fd, res);
+}
+
+static void engine_epoll_loop(struct timeval *timeout) {
+       struct epoll_event evts[MAX_EVENTS];
+       int msec, msec2;
+       int events;
+       int epoll_result;
+       struct timeval now;
+       
+       //check timers
+       gettimeofday(&now, NULL);
+       if(iotimer_sorted_descriptors && timeval_is_bigger(now, iotimer_sorted_descriptors->timeout))
+               _trigger_timer();
+       
+       //get timeout (timer or given timeout)
+       if(iotimer_sorted_descriptors) {
+               msec = (iotimer_sorted_descriptors->timeout.tv_sec - now.tv_sec) * 1000;
+               msec += (iotimer_sorted_descriptors->timeout.tv_usec - now.tv_usec) / 1000;
+       }
+       if(timeout) {
+               msec2 = (timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
+               if(!iotimer_sorted_descriptors || msec2 < msec)
+                       msec = msec2;
+       } else if(!iotimer_sorted_descriptors)
+               msec = -1;
+       
+       //epoll system call
+       epoll_result = epoll_wait(epoll_fd, evts, MAX_EVENTS, msec);
+       
+       if (epoll_result < 0) {
+               if (errno != EINTR) {
+                       iolog_trigger(IOLOG_FATAL, "epoll_wait() failed with errno %d: %s", errno, strerror(errno));
+                       return;
+               }
+       } else {
+               int i;
+               for(i = 0; i < epoll_result; i++) {
+                       events = evts[i].events;
+                       iosocket_events_callback(evts[i].data.ptr, (events & (EPOLLIN | EPOLLHUP)), (events & EPOLLOUT));
+               }
+       }
+       
+       //check timers
+       gettimeofday(&now, NULL);
+       if(iotimer_sorted_descriptors && timeval_is_bigger(now, iotimer_sorted_descriptors->timeout))
+               _trigger_timer();
+}
+
+static void engine_epoll_cleanup() {
+       close(epoll_fd);
+}
+
+struct IOEngine engine_epoll = {
+       .name = "epoll",
+       .init = engine_epoll_init,
+       .add = engine_epoll_add,
+       .remove = engine_epoll_remove,
+       .update = engine_epoll_update,
+       .loop = engine_epoll_loop,
+       .cleanup = engine_epoll_cleanup,
+};
+
+#else
+
+struct IOEngine engine_epoll = {
+       .name = "epoll",
+       .init = NULL,
+       .add = NULL,
+       .remove = NULL,
+       .update = NULL,
+       .loop = NULL,
+       .cleanup = NULL,
+};
+
+#endif
diff --git a/src/IOHandler/IOEngine_kevent.c b/src/IOHandler/IOEngine_kevent.c
new file mode 100644 (file)
index 0000000..e654aa9
--- /dev/null
@@ -0,0 +1,154 @@
+/* IOengine_kevent.c - IOMultiplexer
+ * Copyright (C) 2014  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/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IOLog.h"
+#include "IOSockets.h"
+
+#ifdef HAVE_SYS_EVENT_H
+#include <sys/event.h>
+#include <errno.h>
+
+#define MAX_EVENTS 32
+
+static int kevent_fd;
+
+static int engine_kevent_init() {
+       kevent_fd = kqueue();
+       if (kevent_fd < 0)
+               return 0;
+       return 1;
+}
+
+static void engine_kevent_add(struct _IOSocket *iosock) {
+       //add Socket FD to the kevent queue
+       struct kevent changes[2];
+       int nchanges = 0;
+       int res;
+
+       if (iosocket_wants_reads(iosock))
+               EV_SET(&changes[nchanges++], iosock->fd, EVFILT_READ, EV_ADD, 0, 0, iosock);
+       if (iosocket_wants_writes(iosock))
+               EV_SET(&changes[nchanges++], iosock->fd, EVFILT_WRITE, EV_ADD, 0, 0, iosock);
+       
+       res = kevent(kevent_fd, changes, nchanges, NULL, 0, NULL);
+       if(res < 0)
+               iolog_trigger(IOLOG_ERROR, "could not add _IOSocket %d to kevent queue. (returned: %d)", iosock->fd, res);
+}
+
+static void engine_kevent_remove(struct _IOSocket *iosock) {
+       struct kevent changes[2];
+       int nchanges = 0;
+
+       EV_SET(&changes[nchanges++], iosock->fd, EVFILT_READ, EV_DELETE, 0, 0, iosock);
+       EV_SET(&changes[nchanges++], iosock->fd, EVFILT_WRITE, EV_DELETE, 0, 0, iosock);
+       kevent(kevent_fd, changes, nchanges, NULL, 0, NULL);
+}
+
+static void engine_kevent_update(struct _IOSocket *iosock) {
+       struct kevent changes[2];
+       int nchanges = 0;
+       int res;
+
+       EV_SET(&changes[nchanges++], iosock->fd, EVFILT_READ, iosocket_wants_reads(iosock) ? EV_ADD : EV_DELETE, 0, 0, iosock);
+       EV_SET(&changes[nchanges++], iosock->fd, EVFILT_WRITE, iosocket_wants_writes(iosock) ? EV_ADD : EV_DELETE, 0, 0, iosock);
+       
+       res = kevent(kevent_fd, changes, nchanges, NULL, 0, NULL);
+       if(res < 0)
+               iolog_trigger(IOLOG_ERROR, "could not update _IOSocket %d in kevent queue. (returned: %d)", iosock->fd, res);
+}
+
+static void engine_kevent_loop(struct timeval *timeout) {
+       struct kevent events[MAX_EVENTS];
+       struct timespec ts;
+       int msec;
+       int kevent_result;
+       struct timeval now, tout;
+       
+       //check timers
+       gettimeofday(&now, NULL);
+       if(iotimer_sorted_descriptors && timeval_is_bigger(now, iotimer_sorted_descriptors->timeout))
+               _trigger_timer();
+       
+       //get timeout (timer or given timeout)
+       if(iotimer_sorted_descriptors) {
+               tout = iotimer_sorted_descriptors->timeout;
+               tout.tv_sec -= now.tv_sec;
+               tout.tv_usec -= now.tv_usec;
+               if(tout.tv_usec < 0) {
+                       tout.tv_sec --;
+                       tout.tv_usec += 1000000;
+               }
+       }
+       if(timeout) {
+               if(!iotimer_sorted_descriptors || timeval_is_smaler((*timeout), tout)) {
+                       tout.tv_usec = timeout->tv_usec;
+                       tout.tv_sec = timeout->tv_sec;
+               }
+               timeout = &tout;
+       } else if(iotimer_sorted_descriptors)
+               timeout = &tout;
+       
+       
+       //select system call
+       kevent_result = kevent(kevent_fd, NULL, 0, events, MAX_EVENTS, timeout);
+       
+       if (kevent_result < 0) {
+               if (errno != EINTR) {
+                       iolog_trigger(IOLOG_FATAL, "kevent() failed with errno %d: %s", errno, strerror(errno));
+                       return;
+               }
+       } else {
+               int i;
+               for(i = 0; i < kevent_result; i++)
+                       iosocket_events_callback(events[i].udata, (events[i].filter == EVFILT_READ), (events[i].filter == EVFILT_WRITE));
+       }
+       
+       //check timers
+       gettimeofday(&now, NULL);
+       if(iotimer_sorted_descriptors && timeval_is_bigger(now, iotimer_sorted_descriptors->timeout))
+               _trigger_timer();
+}
+
+static void engine_kevent_cleanup() {
+       close(kevent_fd);
+}
+
+struct IOEngine engine_kevent = {
+       .name = "kevent",
+       .init = engine_kevent_init,
+       .add = engine_kevent_add,
+       .remove = engine_kevent_remove,
+       .update = engine_kevent_update,
+       .loop = engine_kevent_loop,
+       .cleanup = engine_kevent_cleanup,
+};
+
+#else
+
+struct IOEngine engine_kevent = {
+       .name = "kevent",
+       .init = NULL,
+       .add = NULL,
+       .remove = NULL,
+       .update = NULL,
+       .loop = NULL,
+       .cleanup = NULL,
+};
+
+#endif
diff --git a/src/IOHandler/IOEngine_select.c b/src/IOHandler/IOEngine_select.c
new file mode 100644 (file)
index 0000000..b725572
--- /dev/null
@@ -0,0 +1,148 @@
+/* IOEngine_select.c - IOMultiplexer
+ * Copyright (C) 2014  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/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IOLog.h"
+#include "IOSockets.h"
+#include "IOTimer.h"
+
+#include <errno.h>
+#include <time.h>
+#include <string.h>
+#include <stdio.h>
+#ifdef WIN32
+#define _WIN32_WINNT 0x501
+#include <windows.h>
+#include <winsock2.h>
+#elif defined HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+/* compat */
+#include "compat/utime.h"
+
+static int engine_select_init() {
+       return 1;
+}
+
+static void engine_select_add(struct _IOSocket *iosock) {
+       /* empty */
+}
+
+static void engine_select_remove(struct _IOSocket *iosock) {
+       /* empty */
+}
+
+static void engine_select_update(struct _IOSocket *iosock) {
+       /* empty */
+}
+
+static void engine_select_loop(struct timeval *timeout) {
+       fd_set read_fds;
+       fd_set write_fds;
+       unsigned int fds_size = 0;
+       struct _IOSocket *iosock, *next_iosock;
+       struct timeval now, tout;
+       int select_result;
+       
+       //clear fds
+       FD_ZERO(&read_fds);
+       FD_ZERO(&write_fds);
+       
+       //check timers
+       gettimeofday(&now, NULL);
+       if(iotimer_sorted_descriptors && timeval_is_bigger(now, iotimer_sorted_descriptors->timeout))
+               _trigger_timer();
+       
+       //get timeout (timer or given timeout)
+       if(iotimer_sorted_descriptors) {
+               tout = iotimer_sorted_descriptors->timeout;
+               tout.tv_sec -= now.tv_sec;
+               tout.tv_usec -= now.tv_usec;
+               if(tout.tv_usec < 0) {
+                       tout.tv_sec --;
+                       tout.tv_usec += 1000000;
+               }
+       }
+       if(timeout) {
+               if(!iotimer_sorted_descriptors || timeval_is_smaler((*timeout), tout)) {
+                       tout.tv_usec = timeout->tv_usec;
+                       tout.tv_sec = timeout->tv_sec;
+               }
+               timeout = &tout;
+       } else if(iotimer_sorted_descriptors)
+               timeout = &tout;
+       
+       select_result = 0;
+       for(iosock = iosocket_first; iosock; iosock = iosock->next) {
+               if(!(iosock->socket_flags & IOSOCKETFLAG_ACTIVE)) 
+                       continue;
+               if(iosock->fd > fds_size)
+                       fds_size = iosock->fd;
+               select_result++;
+               if(iosocket_wants_reads(iosock))
+                       FD_SET(iosock->fd, &read_fds);
+               if(iosocket_wants_writes(iosock))
+                       FD_SET(iosock->fd, &write_fds);
+       }
+
+       if(select_result) //select system call
+               select_result = select(fds_size + 1, &read_fds, &write_fds, NULL, timeout);
+       else if(timeout) {
+               usleep_tv(*timeout);
+               select_result = 0;
+       } else
+               usleep(10000); // 10ms
+       
+       if (select_result < 0) {
+               if (errno != EINTR) {
+                       iolog_trigger(IOLOG_FATAL, "select() failed with errno %d %d: %s", select_result, errno, strerror(errno));
+                       return;
+               }
+       }
+       
+       gettimeofday(&now, NULL);
+       
+       //check all descriptors
+       for(iosock = iosocket_first; iosock; iosock = next_iosock) {
+               next_iosock = iosock->next;
+               if(!(iosock->socket_flags & IOSOCKETFLAG_ACTIVE))
+                       return;
+               if(FD_ISSET(iosock->fd, &read_fds) || FD_ISSET(iosock->fd, &write_fds))
+                       iosocket_events_callback(iosock, FD_ISSET(iosock->fd, &read_fds), FD_ISSET(iosock->fd, &write_fds));
+       }
+       
+       //check timers
+       if(iotimer_sorted_descriptors && timeval_is_bigger(now, iotimer_sorted_descriptors->timeout))
+               _trigger_timer();
+       
+}
+
+static void engine_select_cleanup() {
+       /* empty */
+}
+
+struct IOEngine engine_select = {
+       .name = "select",
+       .init = engine_select_init,
+       .add = engine_select_add,
+       .remove = engine_select_remove,
+       .update = engine_select_update,
+       .loop = engine_select_loop,
+       .cleanup = engine_select_cleanup,
+};
diff --git a/src/IOHandler/IOEngine_win32.c b/src/IOHandler/IOEngine_win32.c
new file mode 100644 (file)
index 0000000..26ac0a6
--- /dev/null
@@ -0,0 +1,212 @@
+/* IOEngine_win32.c - IOMultiplexer
+ * Copyright (C) 2014  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/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IOLog.h"
+#include "IOSockets.h"
+#include "IOTimer.h"
+
+#ifdef WIN32
+
+#define _WIN32_WINNT 0x501
+#include <windows.h>
+#include <winsock2.h>
+
+/* This is massively kludgy.  Unfortunately, the only performant I/O
+ * multiplexer with halfway decent semantics under Windows is
+ * WSAAsyncSelect() -- which requires a window that can receive
+ * messages.
+ *
+ * So ioset_win32_init() creates a hidden window and sets it up for
+ * asynchronous socket notifications.
+ */
+
+#define IDT_TIMER1 1000
+#define IDT_TIMER2 1001
+#define IDT_SOCKET 1002
+
+static HWND ioset_window;
+
+static struct _IOSocket *engine_win32_get_iosock(int fd) {
+       struct _IOSocket *iosock;
+       for(iosock = iosocket_first; iosock; iosock = iosock->next) {
+               if(iosock->fd == fd)
+                       return iosock;
+       }
+       return NULL;
+}
+
+static LRESULT CALLBACK engine_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+       struct _IOSocket *iosock;
+       int events;
+
+       if (hWnd == ioset_window) {
+               switch (uMsg) {
+               case IDT_TIMER1:
+                       return 0;
+               case IDT_TIMER2:
+                       //check timers
+                       _trigger_timer();
+                       return 0;
+               case IDT_SOCKET:
+                       iosock = engine_win32_get_iosock(wParam);
+                       events = WSAGETSELECTEVENT(lParam);
+                       
+                       if((events & FD_CONNECT)) {
+                               int err;
+                               if((err = WSAGETSELECTERROR(lParam)))
+                                       iosocket_events_callback(iosock, err, 0);
+                               else
+                                       iosocket_events_callback(iosock, 0, 1);
+                       } else
+                               iosocket_events_callback(iosock, (events & (FD_READ | FD_ACCEPT | FD_CLOSE)) != 0, (events & FD_WRITE) != 0);
+                       return 0;
+               case WM_QUIT:
+                       return 0;
+               }
+       }
+       return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+static int engine_win32_init() {
+       WNDCLASSEX wcx;
+       HINSTANCE hinst;
+       WSADATA wsadata;
+       
+       // Start Windows Sockets.
+       if (WSAStartup(MAKEWORD(2, 0), &wsadata)) {
+               iolog_trigger(IOLOG_FATAL, "Unable to start Windows Sockets");
+               return 0;
+       }
+       
+       // Get Windows HINSTANCE.
+       hinst = GetModuleHandle(NULL);
+
+       // Describe and register a window class.
+       memset(&wcx, 0, sizeof(wcx));
+       wcx.cbSize = sizeof(wcx);
+       wcx.lpfnWndProc = engine_win32_wndproc;
+       wcx.hInstance = hinst;
+       wcx.lpszClassName = "IOMultiplexerMainWindow";
+       if (!RegisterClassEx(&wcx))
+               return 0;
+
+       ioset_window = CreateWindow("IOMultiplexerMainWindow", "IOMultiplexer", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
+       if (!ioset_window)
+               return 0;
+       return 1;
+}
+
+static long engine_win32_events(struct _IOSocket *iosock) {
+       if(iosock->socket_flags & IOSOCKETFLAG_LISTENING)
+               return FD_ACCEPT;
+       if(iosock->socket_flags & IOSOCKETFLAG_CONNECTING)
+               return FD_CONNECT;
+       
+       return FD_CLOSE | (iosocket_wants_reads(iosock) ? FD_READ : 0) | (iosocket_wants_writes(iosock) ? FD_WRITE : 0);
+}
+
+static void engine_win32_update(struct _IOSocket *iosock) {
+       long events;
+       events = engine_win32_events(iosock);
+       WSAAsyncSelect(iosock->fd, ioset_window, IDT_SOCKET, events);
+}
+
+static void engine_win32_add(struct _IOSocket *iosock) {
+       engine_win32_update(iosock);
+}
+
+static void engine_win32_remove(struct _IOSocket *iosock) {
+       unsigned long ulong = 0;
+       WSAAsyncSelect(iosock->fd, ioset_window, IDT_SOCKET, 0);
+       ioctlsocket(iosock->fd, FIONBIO, &ulong);
+}
+
+static void engine_win32_loop(struct timeval *timeout) {
+       MSG msg;
+       BOOL res;
+       int msec, msec2;
+       struct timeval now;
+       
+       //check timers
+       gettimeofday(&now, NULL);
+       if(iotimer_sorted_descriptors && timeval_is_bigger(now, iotimer_sorted_descriptors->timeout))
+               _trigger_timer();
+       
+       //get timeout (timer or given timeout)
+       if(iotimer_sorted_descriptors) {
+               msec = (iotimer_sorted_descriptors->timeout.tv_sec - now.tv_sec) * 1000;
+               msec += (iotimer_sorted_descriptors->timeout.tv_usec - now.tv_usec) / 1000;
+       }
+       if(timeout) {
+               msec2 = (timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
+               if(!iotimer_sorted_descriptors || msec2 < msec)
+                       msec = msec2;
+       } else if(!iotimer_sorted_descriptors)
+               msec = -1;
+       
+       //set TIMER
+       SetTimer(ioset_window, IDT_TIMER1, 1000, NULL);
+       if(msec > -1)
+               SetTimer(ioset_window, IDT_TIMER2, msec, NULL);
+       
+       //GetMessage system call
+       res = GetMessage(&msg, NULL, 0, 0);
+       
+       //kill TIMER
+       KillTimer(ioset_window, IDT_TIMER1);
+       if(msec > -1)
+               KillTimer(ioset_window, IDT_TIMER2);
+       
+       if (res <=0)
+               return;
+       else {
+               TranslateMessage(&msg);
+               DispatchMessage(&msg);
+       }
+}
+
+static void engine_win32_cleanup() {
+       DestroyWindow(ioset_window);
+       ioset_window = NULL;
+       WSACleanup();
+}
+
+struct IOEngine engine_win32 = {
+       .name = "win32",
+       .init = engine_win32_init,
+       .add = engine_win32_add,
+       .remove = engine_win32_remove,
+       .update = engine_win32_update,
+       .loop = engine_win32_loop,
+       .cleanup = engine_win32_cleanup,
+};
+
+#else
+
+struct IOEngine engine_win32 = {
+       .name = "win32",
+       .init = NULL,
+       .add = NULL,
+       .remove = NULL,
+       .update = NULL,
+       .loop = NULL,
+       .cleanup = NULL,
+};
+
+#endif
diff --git a/src/IOHandler/IOGarbageCollector.c b/src/IOHandler/IOGarbageCollector.c
new file mode 100644 (file)
index 0000000..cf25700
--- /dev/null
@@ -0,0 +1,102 @@
+/* IOGarbageCollector.c - IOMultiplexer v2
+ * Copyright (C) 2014  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/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IOGarbageCollector.h"
+#include "IOLog.h"
+
+#include <sys/time.h>
+#include <stdlib.h>
+
+struct IOGCObject {
+       void *object;
+       iogc_free *free_callback;
+       struct timeval timeout;
+       
+       struct IOGCObject *next;
+};
+
+static int iogc_enabled = 1;
+static struct timeval iogc_timeout;
+static struct IOGCObject *first_object = NULL, *last_object = NULL;
+
+void iogc_init() {
+       iogc_timeout.tv_usec = 0;
+       iogc_timeout.tv_sec = 10;
+}
+
+
+void iohandler_set_gc(int enabled) {
+       if(enabled)
+               iogc_enabled = 1;
+       else
+               iogc_enabled = 0;
+}
+
+void iogc_add(void *object) {
+       iogc_add_callback(object, NULL);
+}
+
+void iogc_add_callback(void *object, iogc_free *free_callback) {
+       if(!iogc_enabled) {
+               if(free_callback)
+                       free_callback(object);
+               else
+                       free(object);
+               return;
+       }
+       struct IOGCObject *obj = malloc(sizeof(*obj));
+       if(!obj) {
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOGCObject in %s:%d", __FILE__, __LINE__);
+               if(free_callback)
+                       free_callback(object);
+               else
+                       free(object);
+               return;
+       }
+       obj->object = object;
+       obj->free_callback = free_callback;
+       gettimeofday(&obj->timeout, NULL);
+       obj->timeout.tv_sec += IOGC_TIMEOUT;
+       
+       obj->next = NULL;
+       if(last_object)
+               last_object->next = obj;
+       else
+               first_object = obj;
+       last_object = obj;
+}
+
+void iogc_exec() {
+       struct timeval now;
+       gettimeofday(&now, NULL);
+       
+       struct IOGCObject *obj, *next_obj;
+       for(obj = first_object; obj; obj = next_obj) {
+               if(timeval_is_smaler(obj->timeout, now)) {
+                       next_obj = obj->next;
+                       if(obj->free_callback)
+                               obj->free_callback(obj->object);
+                       else
+                               free(obj->object);
+                       free(obj);
+               } else
+                       break;
+       }
+       first_object = obj;
+}
diff --git a/src/IOHandler/IOGarbageCollector.h b/src/IOHandler/IOGarbageCollector.h
new file mode 100644 (file)
index 0000000..d1e848b
--- /dev/null
@@ -0,0 +1,27 @@
+/* IOGarbageCollector.h - IOMultiplexer v2
+ * Copyright (C) 2014  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 _IOGarbageCollector_h
+#define _IOGarbageCollector_h
+#ifndef _IOHandler_internals
+#include "IOHandler.h"
+#else
+
+void iogc_init();
+void iogc_exec();
+
+#endif
+#endif
diff --git a/src/IOHandler/IOHandler.c b/src/IOHandler/IOHandler.c
new file mode 100644 (file)
index 0000000..dc51bcb
--- /dev/null
@@ -0,0 +1,74 @@
+/* IOHandler.c - IOMultiplexer v2
+ * Copyright (C) 2014  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/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+
+#include "IOLog.h"
+#include "IOGarbageCollector.h"
+#include "IOTimer.h"
+#include "IODNSLookup.h"
+#include "IOSockets.h"
+
+#include <stdlib.h>
+#include <time.h>
+
+/* compat */
+#include "compat/utime.h"
+
+#define IOHANDLER_STATE_INITIALIZED  0x0001
+#define IOHANDLER_STATE_RUNNING      0x0002
+
+static int iohandler_state = 0;
+
+
+void iohandler_init() {
+       if((iohandler_state & IOHANDLER_STATE_INITIALIZED)) 
+               return;
+       
+       srand(time(NULL));
+       
+       iolog_init();
+       iogc_init();
+       
+       _init_timers();
+       _init_iodns();
+       _init_sockets();
+       
+       iohandler_state |= IOHANDLER_STATE_INITIALIZED;
+}
+
+void iohandler_stop() {
+       iohandler_state &= ~IOHANDLER_STATE_RUNNING;
+}
+
+static void iohandler_loop() {
+       while(iohandler_state & IOHANDLER_STATE_RUNNING) { // endless loop
+               // iohandler calls
+               iogc_exec();
+               iodns_poll();
+               iosocket_loop(IOHANDLER_LOOP_MAXTIME);
+               
+       }
+}
+
+void iohandler_run() {
+       iohandler_state |= IOHANDLER_STATE_RUNNING;
+       
+       iohandler_loop();
+}
+
diff --git a/src/IOHandler/IOHandler.h b/src/IOHandler/IOHandler.h
new file mode 100644 (file)
index 0000000..1424eb5
--- /dev/null
@@ -0,0 +1,30 @@
+/* IOHandler.h - IOMultiplexer v2
+ * Copyright (C) 2014  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_h
+#define _IOHandler_h
+#include "IOHandler_config.h"
+#ifdef _IOHandler_internals
+
+#endif
+
+void iohandler_init();
+void iohandler_run();
+void iohandler_stop();
+
+void iohandler_set_gc(int enabled); /* default: enabled */
+
+#endif
diff --git a/src/IOHandler/IOHandler_config.h b/src/IOHandler/IOHandler_config.h
new file mode 100644 (file)
index 0000000..4053a2a
--- /dev/null
@@ -0,0 +1,63 @@
+/* IOHanlder_config.h - IOMultiplexer
+ * Copyright (C) 2014  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/>. 
+ */
+
+/* required configure script checks
+ AC_FUNC_MALLOC
+ AC_CHECK_FUNCS([usleep select socket inet_pton inet_ntop])
+ AC_CHECK_HEADERS([fcntl.h sys/socket.h sys/select.h sys/time.h sys/types.h unistd.h windows.h winsock2.h errno.h sys/epoll.h sys/event.h])
+ AC_CHECK_LIB(ws2_32, main, [ LIBS="$LIBS -lws2_32" ], [])
+ have_gnutls="no"
+ AC_CHECK_LIB(gnutls, gnutls_init, [
+   AC_CHECK_HEADERS(gnutls/gnutls.h, [
+     LIBS="$LIBS -lgnutls"
+     have_gnutls="yes"
+   ])
+ ])
+ if test x"$have_gnutls" = xno; then
+   AC_CHECK_LIB(ssl, SSL_read, [
+     AC_CHECK_LIB(crypto, X509_new, [
+       AC_CHECK_HEADERS(openssl/ssl.h openssl/err.h openssl/rand.h, [
+         LIBS="$LIBS -lssl -lcrypto"
+       ])
+     ])
+   ])
+ fi
+ AC_CHECK_LIB(pthread, pthread_create, [
+   AC_CHECK_HEADERS(pthread.h, [
+     LIBS="$LIBS -lpthread"
+   ])
+ ])
+ AC_CHECK_LIB(cares, ares_init, [
+   AC_CHECK_HEADERS(ares.h, [
+     LIBS="$LIBS -lcares"
+   ])
+ ])
+*/
+// configure config file
+#include "../../config.h"
+
+#define IOHANDLER_MAX_SOCKETS 1024
+#define IOHANDLER_LOOP_MAXTIME 100000 /* 100ms */
+
+#define IOSOCKET_PARSE_DELIMITERS_COUNT 5
+#define IOSOCKET_PARSE_LINE_LIMIT 1024
+#define IOSOCKET_PRINTF_LINE_LEN  1024
+
+//#define IODNS_USE_THREADS
+
+#define IOGC_TIMEOUT 60
diff --git a/src/IOHandler/IOInternal.h b/src/IOHandler/IOInternal.h
new file mode 100644 (file)
index 0000000..e5dc207
--- /dev/null
@@ -0,0 +1,32 @@
+/* IOInternal.h - IOMultiplexer v2
+ * Copyright (C) 2014  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 _IOInternal_h
+#define _IOInternal_h
+#ifndef _IOHandler_internals
+#include "IOHandler.h"
+#else
+
+#define timeval_is_bigger(x,y) ((x.tv_sec > y.tv_sec) || (x.tv_sec == y.tv_sec && x.tv_usec > y.tv_usec))
+#define timeval_is_smaler(x,y) ((x.tv_sec < y.tv_sec) || (x.tv_sec == y.tv_sec && x.tv_usec < y.tv_usec))
+
+#define IOGC_FREE(NAME) void NAME(void *object)
+typedef IOGC_FREE(iogc_free);
+void iogc_add(void *object);
+void iogc_add_callback(void *object, iogc_free *free_callback);
+
+#endif
+#endif
diff --git a/src/IOHandler/IOLog.c b/src/IOHandler/IOLog.c
new file mode 100644 (file)
index 0000000..6a298b9
--- /dev/null
@@ -0,0 +1,64 @@
+/* IOLog.c - IOMultiplexer v2
+ * Copyright (C) 2014  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/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IOLog.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+struct iolog_callback_entry {
+       iolog_callback *callback;
+       struct iolog_callback_entry *next;
+};
+static struct iolog_callback_entry *iolog_callbacks = NULL;
+
+void iolog_init() {
+
+}
+
+#define MAXLOG 1024
+
+void iolog_trigger(enum IOLogType type, char *text, ...) {
+       va_list arg_list;
+       char logBuf[MAXLOG+1];
+       int pos;
+       logBuf[0] = '\0';
+       va_start(arg_list, text);
+       pos = vsnprintf(logBuf, MAXLOG - 1, text, arg_list);
+       va_end(arg_list);
+       if (pos < 0 || pos > (MAXLOG - 1)) pos = MAXLOG - 1;
+       logBuf[pos] = '\n';
+       logBuf[pos+1] = '\0';
+       
+       struct iolog_callback_entry *callback;
+       for(callback = iolog_callbacks; callback; callback = callback->next)
+               callback->callback(type, logBuf);
+}
+
+void iolog_register_callback(iolog_callback *callback) {
+       struct iolog_callback_entry *logcb = malloc(sizeof(*logcb));
+       if(!logcb) {
+               iolog_trigger(IOLOG_ERROR, "Failed to allocate memory for iolog_callback_entry in %s:%d", __FILE__, __LINE__);
+               return;
+       }
+       logcb->callback = callback;
+       logcb->next = iolog_callbacks;
+       iolog_callbacks = logcb;
+}
diff --git a/src/IOHandler/IOLog.h b/src/IOHandler/IOLog.h
new file mode 100644 (file)
index 0000000..a628527
--- /dev/null
@@ -0,0 +1,41 @@
+/* IOLog.h - IOMultiplexer v2
+ * Copyright (C) 2014  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 _IOLog_h
+#define _IOLog_h
+#ifndef _IOHandler_internals
+#include "IOHandler.h"
+#else
+enum IOLogType;
+
+void iolog_init();
+void iolog_trigger(enum IOLogType type, char *text, ...);
+
+#endif
+
+enum IOLogType {
+       IOLOG_DEBUG,
+       IOLOG_WARNING,
+       IOLOG_ERROR,
+       IOLOG_FATAL
+};
+
+#define IOLOG_CALLBACK(NAME) void NAME(enum IOLogType type, char *message)
+typedef IOLOG_CALLBACK(iolog_callback);
+
+void iolog_register_callback(iolog_callback *callback);
+
+#endif
diff --git a/src/IOHandler/IOSSLBackend.c b/src/IOHandler/IOSSLBackend.c
new file mode 100644 (file)
index 0000000..f45b6d4
--- /dev/null
@@ -0,0 +1,493 @@
+/* IOSSLBackend.c - IOMultiplexer
+ * Copyright (C) 2014  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/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IOLog.h"
+#include "IOSockets.h"
+#include "IOSSLBackend.h"
+
+#if defined(HAVE_GNUTLS_GNUTLS_H)
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+/* GnuTLS Backend */
+static gnutls_dh_params_t dh_params;
+static unsigned int dh_params_bits;
+
+static int generate_dh_params() {
+       dh_params_bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_LEGACY);
+       gnutls_dh_params_init(&dh_params);
+       gnutls_dh_params_generate2(dh_params, dh_params_bits);
+       return 0;
+}
+
+void iossl_init() {
+       int ret;
+       if((ret = gnutls_global_init()) != GNUTLS_E_SUCCESS) {
+               iolog_trigger(IOLOG_ERROR, "SSL: gnutls_global_init(): failed (%d)", ret);
+               //TODO: Error handling?
+               return;
+       }
+       generate_dh_params();
+}
+
+// Client
+void iossl_connect(struct _IOSocket *iosock) {
+       struct IOSSLDescriptor *sslnode = malloc(sizeof(*sslnode));
+       int err;
+       
+       err = gnutls_certificate_allocate_credentials(&sslnode->ssl.client.credentials);
+       if(err < 0) {
+               goto ssl_connect_err;
+       }
+       
+       gnutls_init(&sslnode->ssl.client.session, GNUTLS_CLIENT);
+       
+       gnutls_priority_set_direct(sslnode->ssl.client.session, "SECURE128:+SECURE192:-VERS-TLS-ALL:+VERS-TLS1.2", NULL);
+       gnutls_credentials_set(sslnode->ssl.client.session, GNUTLS_CRD_CERTIFICATE, sslnode->ssl.client.credentials);
+       
+       gnutls_transport_set_int(sslnode->ssl.client.session, iosock->fd);
+       gnutls_handshake_set_timeout(sslnode->ssl.client.session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+       
+       iosock->sslnode = sslnode;
+       iosock->socket_flags |= IOSOCKETFLAG_SSL_HANDSHAKE;
+       iossl_client_handshake(iosock);
+       return;
+ssl_connect_err:
+       free(sslnode);
+       iosocket_events_callback(iosock, 0, 0);
+}
+
+void iossl_client_handshake(struct _IOSocket *iosock) {
+       // Perform an SSL handshake.
+       int ret = gnutls_handshake(iosock->sslnode->ssl.client.session);
+       iosock->socket_flags &= ~IOSOCKETFLAG_SSL_WANTWRITE;
+       
+       if(ret < 0) {
+               if(gnutls_error_is_fatal(ret) == 0) {
+                       if(gnutls_record_get_direction(iosock->sslnode->ssl.client.session)) {
+                               iosock->socket_flags |= IOSOCKETFLAG_SSL_WANTWRITE;
+                               iolog_trigger(IOLOG_DEBUG, "gnutls_handshake for fd %d wants to write...", iosock->fd);
+                       } else {
+                               iolog_trigger(IOLOG_DEBUG, "gnutls_handshake for fd %d wants to read...", iosock->fd);
+                       }
+               } else {
+                       iolog_trigger(IOLOG_ERROR, "gnutls_handshake for fd %d failed with %s", iosock->fd, gnutls_strerror(ret));
+                       iosocket_events_callback(iosock, 0, 0);
+               }
+       } else {
+               char *desc;
+               desc = gnutls_session_get_desc(iosock->sslnode->ssl.client.session);
+               iolog_trigger(IOLOG_DEBUG, "SSL handshake for fd %d successful: %s", iosock->fd, desc);
+               gnutls_free(desc);
+               iosock->socket_flags |= IOSOCKETFLAG_SSL_ESTABLISHED;
+               iosocket_events_callback(iosock, 0, 0); //perform IOEVENT_CONNECTED event
+       }
+}
+
+
+// Server
+void iossl_listen(struct _IOSocket *iosock, const char *certfile, const char *keyfile) {
+       struct IOSSLDescriptor *sslnode = malloc(sizeof(*sslnode));
+       
+       gnutls_priority_init(&sslnode->ssl.server.priority, "SECURE128:+SECURE192:-VERS-TLS-ALL:+VERS-TLS1.2", NULL);
+       
+       gnutls_certificate_allocate_credentials(&sslnode->ssl.server.credentials);
+       int ret = gnutls_certificate_set_x509_key_file(sslnode->ssl.server.credentials, certfile, keyfile, GNUTLS_X509_FMT_PEM);
+       if (ret < 0) {
+               iolog_trigger(IOLOG_ERROR, "SSL: could not load server certificate/key (%s %s): %d - %s", certfile, keyfile, ret, gnutls_strerror(ret));
+               goto ssl_listen_err;
+       }
+       
+       gnutls_certificate_set_dh_params(sslnode->ssl.server.credentials, dh_params);
+       
+       iosock->sslnode = sslnode;
+       iosock->socket_flags |= IOSOCKETFLAG_SSL_ESTABLISHED;
+       return;
+ssl_listen_err:
+       free(sslnode);
+       iosock->sslnode = NULL;
+       iosocket_events_callback(iosock, 0, 0);
+}
+
+void iossl_client_accepted(struct _IOSocket *iosock, struct _IOSocket *new_iosock) {
+       struct IOSSLDescriptor *sslnode = malloc(sizeof(*sslnode));
+       
+       gnutls_init(&sslnode->ssl.client.session, GNUTLS_SERVER);
+       gnutls_priority_set(sslnode->ssl.client.session, iosock->sslnode->ssl.server.priority);
+       gnutls_credentials_set(sslnode->ssl.client.session, GNUTLS_CRD_CERTIFICATE, iosock->sslnode->ssl.server.credentials);
+       gnutls_dh_set_prime_bits(sslnode->ssl.client.session, dh_params_bits);
+       
+       /* We don't request any certificate from the client.
+        * If we did we would need to verify it.
+        */
+       gnutls_certificate_server_set_request(sslnode->ssl.client.session, GNUTLS_CERT_IGNORE);
+       
+       gnutls_transport_set_int(sslnode->ssl.client.session, new_iosock->fd);
+       
+       new_iosock->sslnode = sslnode;
+       new_iosock->socket_flags |= IOSOCKETFLAG_SSL_HANDSHAKE;
+       return;
+       /*
+ssl_accept_err:
+       free(sslnode);
+       iosock->sslnode = NULL;
+       iosocket_events_callback(new_iosock, 0, 0);
+       */
+}
+
+void iossl_server_handshake(struct _IOSocket *iosock) {
+       return iossl_client_handshake(iosock);
+}
+
+void iossl_disconnect(struct _IOSocket *iosock) {
+       if(!iosock->sslnode) return;
+       
+       if((iosock->socket_flags & IOSOCKETFLAG_LISTENING)) {
+               gnutls_certificate_free_credentials(iosock->sslnode->ssl.server.credentials);
+               gnutls_priority_deinit(iosock->sslnode->ssl.server.priority);
+       } else {
+               gnutls_bye(iosock->sslnode->ssl.client.session, GNUTLS_SHUT_RDWR);
+               if(!(iosock->socket_flags & IOSOCKETFLAG_INCOMING))
+                       gnutls_certificate_free_credentials(iosock->sslnode->ssl.client.credentials);
+               gnutls_deinit(iosock->sslnode->ssl.client.session);
+       }
+       
+       free(iosock->sslnode);
+       iosock->sslnode = NULL;
+       iosock->socket_flags &= ~IOSOCKETFLAG_SSLSOCKET;
+}
+
+static void iossl_rehandshake(struct _IOSocket *iosock, int hsflag) {
+       int ret = gnutls_handshake(iosock->sslnode->ssl.client.session);
+       iosock->socket_flags &= ~IOSOCKETFLAG_SSL_WANTWRITE;
+       
+       if(ret < 0) {
+               if(gnutls_error_is_fatal(ret) == 0) {
+                       if(gnutls_record_get_direction(iosock->sslnode->ssl.client.session)) {
+                               iosock->socket_flags |= IOSOCKETFLAG_SSL_WANTWRITE | hsflag;
+                               iolog_trigger(IOLOG_DEBUG, "gnutls_handshake for fd %d wants to write...", iosock->fd);
+                       } else {
+                               iosock->socket_flags |= hsflag;
+                               iolog_trigger(IOLOG_DEBUG, "gnutls_handshake for fd %d wants to read...", iosock->fd);
+                       }
+               } else {
+                       iolog_trigger(IOLOG_ERROR, "gnutls_handshake for fd %d failed: %s", iosock->fd, gnutls_strerror(ret));
+                       //TODO: Error Action?
+               }
+       } else {
+               char *desc;
+               desc = gnutls_session_get_desc(iosock->sslnode->ssl.client.session);
+               iolog_trigger(IOLOG_DEBUG, "SSL handshake for fd %d successful: %s", iosock->fd, desc);
+               gnutls_free(desc);
+               iosock->socket_flags &= ~hsflag;
+       }
+}
+
+int iossl_read(struct _IOSocket *iosock, char *buffer, int len) {
+       if((iosock->socket_flags & (IOSOCKETFLAG_SSLSOCKET | IOSOCKETFLAG_SSL_ESTABLISHED)) != (IOSOCKETFLAG_SSLSOCKET | IOSOCKETFLAG_SSL_ESTABLISHED))
+               return 0;
+       if((iosock->socket_flags & IOSOCKETFLAG_SSL_READHS)) {
+               iossl_rehandshake(iosock, IOSOCKETFLAG_SSL_READHS);
+               errno = EAGAIN;
+               return -1;
+       }
+       int ret = gnutls_record_recv(iosock->sslnode->ssl.client.session, buffer, len);
+       if(ret == 0) {
+               //TLS session closed
+               //TODO: Action?
+       } else if(ret < 0 && gnutls_error_is_fatal(ret) == 0) {
+               iolog_trigger(IOLOG_WARNING, "gnutls_record_recv for fd %d returned %s", iosock->fd, gnutls_strerror(ret));
+               if(ret == GNUTLS_E_REHANDSHAKE) {
+                       iossl_rehandshake(iosock, IOSOCKETFLAG_SSL_READHS);
+                       errno = EAGAIN;
+                       return -1;
+               }
+       } else if(ret < 0) {
+               iolog_trigger(IOLOG_ERROR, "gnutls_record_recv for fd %d failed: %s", iosock->fd, gnutls_strerror(ret));
+               errno = ret;
+               return ret;
+       }
+       return ret;
+}
+
+int iossl_write(struct _IOSocket *iosock, char *buffer, int len) {
+       if((iosock->socket_flags & (IOSOCKETFLAG_SSLSOCKET | IOSOCKETFLAG_SSL_ESTABLISHED)) != (IOSOCKETFLAG_SSLSOCKET | IOSOCKETFLAG_SSL_ESTABLISHED))
+               return 0;
+       if((iosock->socket_flags & IOSOCKETFLAG_SSL_WRITEHS)) {
+               iossl_rehandshake(iosock, IOSOCKETFLAG_SSL_WRITEHS);
+               errno = EAGAIN;
+               return -1;
+       }
+       int ret = gnutls_record_send(iosock->sslnode->ssl.client.session, buffer, len);
+       if(ret < 0 && gnutls_error_is_fatal(ret) == 0) {
+               iolog_trigger(IOLOG_WARNING, "gnutls_record_send for fd %d returned %s", iosock->fd, gnutls_strerror(ret));
+               if(ret == GNUTLS_E_REHANDSHAKE) {
+                       iossl_rehandshake(iosock, IOSOCKETFLAG_SSL_WRITEHS);
+                       errno = EAGAIN;
+                       return -1;
+               }
+       } else if(ret < 0) {
+               iolog_trigger(IOLOG_ERROR, "gnutls_record_send for fd %d failed: %s", iosock->fd, gnutls_strerror(ret));
+               errno = ret;
+               return ret;
+       }
+       return ret;
+}
+
+#elif defined(HAVE_OPENSSL_SSL_H)
+/* OpenSSL Backend */
+
+
+void iossl_init() {
+       SSL_library_init();
+       OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */
+       SSL_load_error_strings();
+}
+
+static void iossl_error() {
+       unsigned long e;
+       while((e = ERR_get_error())) {
+               iolog_trigger(IOLOG_ERROR, "SSLv23 ERROR %lu: %s", e, ERR_error_string(e, NULL));
+       }
+}
+
+// Client
+void iossl_connect(struct _IOSocket *iosock) {
+       struct IOSSLDescriptor *sslnode = malloc(sizeof(*sslnode));
+       sslnode->sslContext = SSL_CTX_new(SSLv23_client_method());
+       if(!sslnode->sslContext) {
+               iossl_error();
+               iolog_trigger(IOLOG_ERROR, "SSL: could not create client SSL CTX");
+               goto ssl_connect_err;
+       }
+       sslnode->sslHandle = SSL_new(sslnode->sslContext);
+       if(!sslnode->sslHandle) {
+               iossl_error();
+               iolog_trigger(IOLOG_ERROR, "SSL: could not create client SSL Handle");
+               goto ssl_connect_err;
+       }
+       if(!SSL_set_fd(sslnode->sslHandle, iosock->fd)) {
+               iossl_error();
+               iolog_trigger(IOLOG_ERROR, "SSL: could not set client fd in SSL Handle");
+               goto ssl_connect_err;
+       }
+       SSL_set_connect_state(sslnode->sslHandle);
+       iosock->sslnode = sslnode;
+       iosock->socket_flags |= IOSOCKETFLAG_SSL_HANDSHAKE;
+       iossl_client_handshake(iosock);
+       return;
+ssl_connect_err:
+       free(sslnode);
+       iosocket_events_callback(iosock, 0, 0);
+}
+
+void iossl_client_handshake(struct _IOSocket *iosock) {
+       // Perform an SSL handshake.
+       int ret = SSL_do_handshake(iosock->sslnode->sslHandle);
+       iosock->socket_flags &= ~IOSOCKETFLAG_SSL_WANTWRITE;
+       switch(SSL_get_error(iosock->sslnode->sslHandle, ret)) {
+               case SSL_ERROR_NONE:
+                       iolog_trigger(IOLOG_DEBUG, "SSL handshake for fd %d successful", iosock->fd);
+                       iosock->socket_flags |= IOSOCKETFLAG_SSL_ESTABLISHED;
+                       iosocket_events_callback(iosock, 0, 0); //perform IOEVENT_CONNECTED event
+                       break;
+               case SSL_ERROR_WANT_READ:
+                       iolog_trigger(IOLOG_DEBUG, "SSL_do_handshake for fd %d returned SSL_ERROR_WANT_READ", iosock->fd);
+                       break;
+               case SSL_ERROR_WANT_WRITE:
+                       iosock->socket_flags |= IOSOCKETFLAG_SSL_WANTWRITE;
+                       iolog_trigger(IOLOG_DEBUG, "SSL_do_handshake for fd %d returned SSL_ERROR_WANT_WRITE", iosock->fd);
+                       break;
+               default:
+                       iolog_trigger(IOLOG_ERROR, "SSL_do_handshake for fd %d failed with ", iosock->fd);
+                       iosocket_events_callback(iosock, 0, 0);
+                       break;
+       }
+}
+
+
+// Server
+void iossl_listen(struct _IOSocket *iosock, const char *certfile, const char *keyfile) {
+       struct IOSSLDescriptor *sslnode = malloc(sizeof(*sslnode));
+       sslnode->sslContext = SSL_CTX_new(SSLv23_server_method());
+       if(!sslnode->sslContext) {
+               iossl_error();
+               iolog_trigger(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) {
+               iossl_error();
+               iolog_trigger(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) {
+               iossl_error();
+               iolog_trigger(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)) {
+               iossl_error();
+               iolog_trigger(IOLOG_ERROR, "SSL: server certificate (%s) and keyfile (%s) doesn't match!", certfile, keyfile);
+               goto ssl_listen_err;
+       }
+       iosock->sslnode = sslnode;
+       iosock->socket_flags |= IOSOCKETFLAG_SSL_ESTABLISHED;
+       return;
+ssl_listen_err:
+       free(sslnode);
+       iosock->sslnode = NULL;
+       iosocket_events_callback(iosock, 0, 0);
+}
+
+void iossl_client_accepted(struct _IOSocket *iosock, struct _IOSocket *new_iosock) {
+       struct IOSSLDescriptor *sslnode = malloc(sizeof(*sslnode));
+       sslnode->sslHandle = SSL_new(iosock->sslnode->sslContext);
+       if(!sslnode->sslHandle) {
+               iossl_error();
+               iolog_trigger(IOLOG_ERROR, "SSL: could not create client SSL Handle");
+               goto ssl_accept_err;
+       }
+       if(!SSL_set_fd(sslnode->sslHandle, new_iosock->fd)) {
+               iossl_error();
+               iolog_trigger(IOLOG_ERROR, "SSL: could not set client fd in SSL Handle");
+               goto ssl_accept_err;
+       }
+       new_iosock->sslnode = sslnode;
+       new_iosock->socket_flags |= IOSOCKETFLAG_SSL_HANDSHAKE;
+       return;
+ssl_accept_err:
+       free(sslnode);
+       iosock->sslnode = NULL;
+       iosocket_events_callback(new_iosock, 0, 0);
+}
+
+void iossl_server_handshake(struct _IOSocket *iosock) {
+       // Perform an SSL handshake.
+       int ret = SSL_accept(iosock->sslnode->sslHandle);
+       iosock->socket_flags &= ~IOSOCKETFLAG_SSL_WANTWRITE;
+       switch(SSL_get_error(iosock->sslnode->sslHandle, ret)) {
+               case SSL_ERROR_NONE:
+                       iolog_trigger(IOLOG_DEBUG, "SSL handshake for fd %d successful", iosock->fd);
+                       iosock->socket_flags |= IOSOCKETFLAG_SSL_ESTABLISHED;
+                       iosocket_events_callback(iosock, 0, 0); //perform IOEVENT_CONNECTED event
+                       break;
+               case SSL_ERROR_WANT_READ:
+                       iolog_trigger(IOLOG_DEBUG, "SSL_do_handshake for fd %d returned SSL_ERROR_WANT_READ", iosock->fd);
+                       break;
+               case SSL_ERROR_WANT_WRITE:
+                       iosock->socket_flags |= IOSOCKETFLAG_SSL_WANTWRITE;
+                       iolog_trigger(IOLOG_DEBUG, "SSL_do_handshake for fd %d returned SSL_ERROR_WANT_WRITE", iosock->fd);
+                       break;
+               default:
+                       iolog_trigger(IOLOG_ERROR, "SSL_do_handshake for fd %d failed with ", iosock->fd);
+                       iosocket_events_callback(iosock, 0, 0);
+                       break;
+       }
+}
+
+void iossl_disconnect(struct _IOSocket *iosock) {
+       if(!iosock->sslnode) return;
+       SSL_shutdown(iosock->sslnode->sslHandle);
+       SSL_free(iosock->sslnode->sslHandle);
+       if(!(iosock->socket_flags & IOSOCKETFLAG_INCOMING))
+               SSL_CTX_free(iosock->sslnode->sslContext);
+       free(iosock->sslnode);
+       iosock->sslnode = NULL;
+       iosock->socket_flags &= ~IOSOCKETFLAG_SSLSOCKET;
+}
+
+int iossl_read(struct _IOSocket *iosock, char *buffer, int len) {
+       if((iosock->socket_flags & (IOSOCKETFLAG_SSLSOCKET | IOSOCKETFLAG_SSL_ESTABLISHED)) != (IOSOCKETFLAG_SSLSOCKET | IOSOCKETFLAG_SSL_ESTABLISHED))
+               return 0;
+       int ret = SSL_read(iosock->sslnode->sslHandle, buffer, len);
+       iosock->socket_flags &= ~(IOSOCKETFLAG_SSL_WANTWRITE | IOSOCKETFLAG_SSL_READHS);
+       int err = SSL_get_error(iosock->sslnode->sslHandle, ret);
+       switch(err) {
+               case SSL_ERROR_NONE:
+               case SSL_ERROR_ZERO_RETURN:
+                       break;
+               case SSL_ERROR_WANT_READ:
+                       iosock->socket_flags |= IOSOCKETFLAG_SSL_READHS;
+                       iolog_trigger(IOLOG_DEBUG, "SSL_read for fd %d returned SSL_ERROR_WANT_READ", iosock->fd);
+                       errno = EAGAIN;
+                       ret = -1;
+                       break;
+               case SSL_ERROR_WANT_WRITE:
+                       iosock->socket_flags |= IOSOCKETFLAG_SSL_WANTWRITE | IOSOCKETFLAG_SSL_READHS;
+                       iolog_trigger(IOLOG_DEBUG, "SSL_read for fd %d returned SSL_ERROR_WANT_WRITE", iosock->fd);
+                       errno = EAGAIN;
+                       ret = -1;
+                       break;
+               case SSL_ERROR_SYSCALL:
+                       iolog_trigger(IOLOG_ERROR, "SSL_read for fd %d failed with SSL_ERROR_SYSCALL: %d", iosock->fd, errno);
+                       ret = -1;
+                       break;
+               default:
+                       iolog_trigger(IOLOG_ERROR, "SSL_read for fd %d failed with %d", iosock->fd, err);
+                       ret = -1;
+                       break;
+       }
+       return ret;
+}
+
+int iossl_write(struct _IOSocket *iosock, char *buffer, int len) {
+       if((iosock->socket_flags & (IOSOCKETFLAG_SSLSOCKET | IOSOCKETFLAG_SSL_ESTABLISHED)) != (IOSOCKETFLAG_SSLSOCKET | IOSOCKETFLAG_SSL_ESTABLISHED))
+               return 0;
+       int ret = SSL_write(iosock->sslnode->sslHandle, buffer, len);
+       iosock->socket_flags &= ~(IOSOCKETFLAG_SSL_WANTWRITE | IOSOCKETFLAG_SSL_WRITEHS);
+       switch(SSL_get_error(iosock->sslnode->sslHandle, ret)) {
+               case SSL_ERROR_NONE:
+               case SSL_ERROR_ZERO_RETURN:
+                       break;
+               case SSL_ERROR_WANT_READ:
+                       iosock->socket_flags |= IOSOCKETFLAG_SSL_WRITEHS;
+                       iolog_trigger(IOLOG_DEBUG, "SSL_write for fd %d returned SSL_ERROR_WANT_READ", iosock->fd);
+                       errno = EAGAIN;
+                       ret = -1;
+                       break;
+               case SSL_ERROR_WANT_WRITE:
+                       iosock->socket_flags |= IOSOCKETFLAG_SSL_WANTWRITE | IOSOCKETFLAG_SSL_WRITEHS;
+                       iolog_trigger(IOLOG_DEBUG, "SSL_write for fd %d returned SSL_ERROR_WANT_WRITE", iosock->fd);
+                       errno = EAGAIN;
+                       ret = -1;
+                       break;
+               default:
+                       iolog_trigger(IOLOG_ERROR, "SSL_write for fd %d failed with ", iosock->fd);
+                       ret = -1;
+                       break;
+       }
+       return ret;
+}
+
+#else
+// NULL-backend
+
+void iossl_init() {};
+void iossl_connect(struct _IOSocket *iosock) {};
+void iossl_listen(struct _IOSocket *iosock, const char *certfile, const char *keyfile) {};
+void iossl_client_handshake(struct _IOSocket *iosock) {};
+void iossl_client_accepted(struct _IOSocket *iosock, struct _IOSocket *client_iofd) {};
+void iossl_server_handshake(struct _IOSocket *iosock) {};
+void iossl_disconnect(struct _IOSocket *iosock) {};
+int iossl_read(struct _IOSocket *iosock, char *buffer, int len) { return 0; };
+int iossl_write(struct _IOSocket *iosock, char *buffer, int len) { return 0; };
+#endif
diff --git a/src/IOHandler/IOSSLBackend.h b/src/IOHandler/IOSSLBackend.h
new file mode 100644 (file)
index 0000000..d6680e6
--- /dev/null
@@ -0,0 +1,62 @@
+/* IOHandler_SSL.h - IOMultiplexer
+ * Copyright (C) 2014  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 _IOSSLBackend_h
+#define _IOSSLBackend_h
+
+struct _IOSocket;
+
+#if defined(HAVE_GNUTLS_GNUTLS_H)
+#include <gnutls/gnutls.h>
+struct IOSSLDescriptor {
+       union {
+               struct {
+                       gnutls_session_t session;
+                       gnutls_certificate_client_credentials credentials;
+               } client;
+               struct {
+                       gnutls_priority_t priority;
+                       gnutls_certificate_credentials_t credentials;
+               } server;
+       } ssl;
+};
+
+#elif defined(HAVE_OPENSSL_SSL_H)
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+struct IOSSLDescriptor {
+       SSL *sslHandle;
+       SSL_CTX *sslContext;
+};
+#else
+struct IOSSLDescriptor {
+       //just unused
+};
+#endif
+
+void iossl_init();
+void iossl_connect(struct _IOSocket *iosock);
+void iossl_listen(struct _IOSocket *iosock, const char *certfile, const char *keyfile);
+void iossl_client_handshake(struct _IOSocket *iosock);
+void iossl_client_accepted(struct _IOSocket *iosock, struct _IOSocket *new_iosock);
+void iossl_server_handshake(struct _IOSocket *iosock);
+void iossl_disconnect(struct _IOSocket *iosock);
+int iossl_read(struct _IOSocket *iosock, char *buffer, int len);
+int iossl_write(struct _IOSocket *iosock, char *buffer, int len);
+
+#endif
diff --git a/src/IOHandler/IOSockets.c b/src/IOHandler/IOSockets.c
new file mode 100644 (file)
index 0000000..a361126
--- /dev/null
@@ -0,0 +1,1111 @@
+/* IOSockets.c - IOMultiplexer v2
+ * Copyright (C) 2014  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/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IOSockets.h"
+#include "IOLog.h"
+#include "IODNSLookup.h"
+#include "IOSSLBackend.h"
+
+#ifdef WIN32
+#define _WIN32_WINNT 0x501
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h> 
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#endif
+#include "compat/inet.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#ifndef EWOULDBLOCK
+#define EWOULDBLOCK EAGAIN
+#endif
+
+struct _IOSocket *iosocket_first = NULL;
+struct _IOSocket *iosocket_last = NULL;
+
+struct IOEngine *engine = NULL;
+
+static void iosocket_increase_buffer(struct IOSocketBuffer *iobuf, size_t required);
+static int iosocket_parse_address(const char *hostname, struct IODNSAddress *addr, int records);
+static int iosocket_lookup_hostname(struct _IOSocket *iosock, const char *hostname, int records, int bindaddr);
+static int iosocket_lookup_apply(struct _IOSocket *iosock, int noip6);
+static void socket_lookup_clear(struct _IOSocket *iosock);
+static void iosocket_connect_finish(struct _IOSocket *iosock);
+static void iosocket_listen_finish(struct _IOSocket *iosock);
+static int iosocket_try_write(struct _IOSocket *iosock);
+static void iosocket_trigger_event(struct IOSocketEvent *event);
+
+#ifdef WIN32
+static int close(int fd) {
+       return closesocket(fd);
+}
+#endif
+
+static void iosockets_init_engine() {
+       //try other engines
+       if(!engine && engine_kevent.init && engine_kevent.init())
+               engine = &engine_kevent;
+       if(!engine && engine_epoll.init && engine_epoll.init())
+               engine = &engine_epoll;
+       if(!engine && engine_win32.init && engine_win32.init())
+               engine = &engine_win32;
+       
+       if (!engine) {
+               if(engine_select.init())
+                       engine = &engine_select;
+               else {
+                       iolog_trigger(IOLOG_FATAL, "found no useable IO engine");
+                       return;
+               }
+       }
+       iolog_trigger(IOLOG_DEBUG, "using %s IOSockets engine", engine->name);
+}
+
+void _init_sockets() {
+       #ifdef WIN32
+       WSADATA wsaData;
+       int iResult;
+       //Initialize Winsock
+       iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+       if(iResult != 0){
+               iolog_trigger(IOLOG_ERROR, "WSAStartup returned error code: %d", iResult);
+       }
+       #endif
+       
+       iosockets_init_engine();\r
+       iossl_init();
+}
+
+
+struct _IOSocket *_create_socket() {
+       struct _IOSocket *iosock = calloc(1, sizeof(*iosock));
+       if(!iosock) {
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for _IOSocket in %s:%d", __FILE__, __LINE__);
+               return NULL;
+       }
+       if(iosocket_last)
+               iosocket_last->next = iosock;
+       else
+               iosocket_first = iosock;
+       iosock->prev = iosocket_last;
+       iosocket_last = iosock;
+       return iosock;
+}
+
+void _free_socket(struct _IOSocket *iosock) {
+       iosocket_deactivate(iosock);
+       if(iosock->prev)
+               iosock->prev->next = iosock->next;
+       else
+               iosocket_first = iosock->next;
+       if(iosock->next)
+               iosock->next->prev = iosock->prev;
+       else
+               iosocket_last = iosock->prev;
+       
+       if(iosock->bind.addr.addresslen)
+               free(iosock->bind.addr.address);
+       if(iosock->dest.addr.addresslen)
+               free(iosock->dest.addr.address);
+       if(iosock->bind.addrlookup || iosock->dest.addrlookup)
+               socket_lookup_clear(iosock);
+       if(iosock->readbuf.buffer)
+               free(iosock->readbuf.buffer);
+       if(iosock->writebuf.buffer)
+               free(iosock->writebuf.buffer);
+       if((iosock->socket_flags & IOSOCKETFLAG_SSLSOCKET))\r
+               iossl_disconnect(iosock);\r
+       
+       free(iosock);
+}
+
+void iosocket_activate(struct _IOSocket *iosock) {
+       if((iosock->socket_flags & IOSOCKETFLAG_ACTIVE))
+               return;
+       iosock->socket_flags |= IOSOCKETFLAG_ACTIVE;
+       engine->add(iosock);
+}
+
+void iosocket_deactivate(struct _IOSocket *iosock) {
+       if(!(iosock->socket_flags & IOSOCKETFLAG_ACTIVE))
+               return;
+       iosock->socket_flags &= ~IOSOCKETFLAG_ACTIVE;
+       engine->remove(iosock);
+}
+
+void iosocket_update(struct _IOSocket *iosock) {
+       if(!(iosock->socket_flags & IOSOCKETFLAG_ACTIVE))
+               return;
+       engine->update(iosock);
+}
+
+static void iosocket_increase_buffer(struct IOSocketBuffer *iobuf, size_t required) {
+       if(iobuf->buflen >= required) return;
+       char *new_buf;
+       if(iobuf->buffer)
+               new_buf = realloc(iobuf->buffer, required + 2);
+       else
+               new_buf = malloc(required + 2);
+       if(new_buf) {
+               iobuf->buffer = new_buf;
+               iobuf->buflen = required;
+       }
+}
+
+static int iosocket_parse_address(const char *hostname, struct IODNSAddress *addr, int records) {
+       int ret;
+       if((records & IOSOCKET_ADDR_IPV4)) {
+               struct sockaddr_in ip4addr;
+               ret = inet_pton(AF_INET, hostname, &(ip4addr.sin_addr));
+               if(ret == 1) {
+                       addr->addresslen = sizeof(ip4addr);
+                       addr->address = malloc(addr->addresslen);
+                       if(!addr->address) {
+                               iolog_trigger(IOLOG_ERROR, "could not allocate memory for sockaddr in %s:%d", __FILE__, __LINE__);
+                               return -1;
+                       }
+                       memcpy(addr->address, &ip4addr, sizeof(ip4addr));
+                       return 1;
+               }
+       }
+       if((records & IOSOCKET_ADDR_IPV6)) {
+               struct sockaddr_in6 ip6addr;
+               ret = inet_pton(AF_INET6, hostname, &(ip6addr.sin6_addr));
+               if(ret == 1) {
+                       addr->addresslen = sizeof(ip6addr);
+                       addr->address = malloc(addr->addresslen);
+                       if(!addr->address) {
+                               iolog_trigger(IOLOG_ERROR, "could not allocate memory for sockaddr in %s:%d", __FILE__, __LINE__);
+                               return -1;
+                       }
+                       memcpy(addr->address, &ip6addr, sizeof(ip6addr));
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+static int iosocket_lookup_hostname(struct _IOSocket *iosock, const char *hostname, int records, int bindaddr) {
+       struct IOSocketDNSLookup *lookup = calloc(1, sizeof(*lookup));
+       if(!lookup) {
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOSocketDNSLookup in %s:%d", __FILE__, __LINE__);
+               return 0;
+       }
+       
+       struct _IODNSQuery *query = _create_dnsquery();
+       if(!query) {
+               free(lookup);
+               return 0;
+       }
+       
+       query->parent = lookup;
+       query->flags |= IODNSFLAG_PARENT_SOCKET;
+       lookup->iosocket = iosock;
+       lookup->query = query;
+       strncpy(lookup->hostname, hostname, sizeof(lookup->hostname));
+       lookup->hostname[sizeof(lookup->hostname)-1] = 0;
+       if(bindaddr) {
+               lookup->bindlookup = 1;
+               iosock->bind.addrlookup = lookup;
+       } else {
+               lookup->bindlookup = 0;
+               iosock->dest.addrlookup = lookup;
+       }
+       
+       int dnsrecords = 0;
+       if((records & IOSOCKET_ADDR_IPV4))
+               dnsrecords |= IODNS_RECORD_A;
+       if((records & IOSOCKET_ADDR_IPV6))
+               dnsrecords |= IODNS_RECORD_AAAA;
+       
+       query->request.host = strdup(hostname);
+       query->type = (dnsrecords & IODNS_FORWARD);
+       
+       _start_dnsquery(query);
+       return 1;
+}
+
+void iosocket_lookup_callback(struct IOSocketDNSLookup *lookup, struct IODNSEvent *event) {
+       lookup->query = NULL;
+       struct _IOSocket *iosock = lookup->iosocket;
+       if(iosock == NULL)
+               return;
+       
+       if(event->type == IODNSEVENT_SUCCESS)
+               lookup->result = event->result;
+       else
+               lookup->result = NULL;
+       
+       if(lookup->bindlookup) {
+               iosock->socket_flags &= ~IOSOCKETFLAG_PENDING_BINDDNS;
+               iosock->socket_flags |= IOSOCKETFLAG_DNSDONE_BINDDNS;
+       } else {
+               iosock->socket_flags &= ~IOSOCKETFLAG_PENDING_DESTDNS;
+               iosock->socket_flags |= IOSOCKETFLAG_DNSDONE_DESTDNS;
+       }
+       
+       int dns_finished = 0;
+       if((iosock->socket_flags & (IOSOCKETFLAG_PENDING_BINDDNS | IOSOCKETFLAG_PENDING_DESTDNS)) == 0)
+               dns_finished = 1;
+       
+       if(dns_finished) {
+               int ret;
+               ret = iosocket_lookup_apply(iosock, 0);
+               if(ret) { //if ret=0 an error occured in iosocket_lookup_apply and we should stop here.
+                       if((iosock->socket_flags & IOSOCKETFLAG_LISTENING)) {
+                               socket_lookup_clear(iosock);
+                               iosocket_listen_finish(iosock);
+                       } else
+                               iosocket_connect_finish(iosock);
+               }
+       }
+}
+
+static int iosocket_lookup_apply(struct _IOSocket *iosock, int noip6) {
+       char errbuf[512];
+       struct IOSocketDNSLookup *bind_lookup = ((iosock->socket_flags & IOSOCKETFLAG_DNSDONE_BINDDNS) ? iosock->bind.addrlookup : NULL);
+       struct IOSocketDNSLookup *dest_lookup = ((iosock->socket_flags & IOSOCKETFLAG_DNSDONE_DESTDNS) ? iosock->dest.addrlookup : NULL);
+       
+       iolog_trigger(IOLOG_DEBUG, "all pending lookups finished. trying to apply lookup results...");
+       
+       if(!bind_lookup && !dest_lookup) {
+               iosock->socket_flags |= IOSOCKETFLAG_DNSERROR;
+               sprintf(errbuf, "Internal Error");
+               iolog_trigger(IOLOG_ERROR, "trying to apply lookup results without any lookups processed in %s:%d", __FILE__, __LINE__);
+               goto iosocket_lookup_apply_end;
+       }
+       
+       struct IODNSResult *result;
+       int bind_numip4 = 0, bind_numip6 = 0;
+       int dest_numip4 = 0, dest_numip6 = 0;
+       
+       if(bind_lookup) {
+               for(result = bind_lookup->result; result; result = result->next) {
+                       if((result->type & IODNS_RECORD_A))
+                               bind_numip4++;
+                       if((result->type & IODNS_RECORD_AAAA))
+                               bind_numip6++;
+               }
+       }
+       if(dest_lookup) {
+               for(result = dest_lookup->result; result; result = result->next) {
+                       if((result->type & IODNS_RECORD_A))
+                               dest_numip4++;
+                       if((result->type & IODNS_RECORD_AAAA))
+                               dest_numip6++;
+               }
+       }
+       int useip6 = 0;
+       int useip4 = 0;
+       if(bind_lookup && (bind_numip6 == 0 && bind_numip4 == 0)) {
+               iosock->socket_flags |= IOSOCKETFLAG_DNSERROR;
+               sprintf(errbuf, "could not lookup bind address (%s)", bind_lookup->hostname);
+               goto iosocket_lookup_apply_end;
+       } else if(dest_lookup && (dest_numip6 == 0 && dest_numip4 == 0)) {
+               iosock->socket_flags |= IOSOCKETFLAG_DNSERROR;
+               sprintf(errbuf, "could not lookup destination address (%s)", dest_lookup->hostname);
+               goto iosocket_lookup_apply_end;
+       } else if(bind_lookup && dest_lookup) {
+               if(bind_numip6 > 0 && dest_numip6 > 0)
+                       useip6 = 1;
+               if(bind_numip4 > 0 && dest_numip4 > 0)
+                       useip4 = 1;
+       } else if(bind_lookup) {
+               if(bind_numip6)
+                       useip6 = 1;
+               if(bind_numip4)
+                       useip4 = 1;
+       } else if(dest_lookup) {
+               if(dest_numip6)
+                       useip6 = 1;
+               if(dest_numip4)
+                       useip4 = 1;
+       }
+       
+       int usetype = 0;
+       if(useip6 && !noip6) {
+               usetype = IODNS_RECORD_AAAA;
+               iosock->socket_flags |= IOSOCKETFLAG_IPV6SOCKET;
+               if(useip4)
+                       iosock->socket_flags |= IOSOCKETFLAG_RECONNECT_IPV4;
+       } else if(useip4) {
+               usetype = IODNS_RECORD_A;
+               iosock->socket_flags &= ~(IOSOCKETFLAG_IPV6SOCKET | IOSOCKETFLAG_RECONNECT_IPV4);
+       } else {
+               iosock->socket_flags |= IOSOCKETFLAG_DNSERROR;
+               sprintf(errbuf, "could not lookup adresses of the same IP family for bind and destination host. (bind: %d ip4, %d ip6 | dest: %d ip4, %d ip6)", bind_numip4, bind_numip6, dest_numip4, dest_numip6);
+               goto iosocket_lookup_apply_end;
+       }
+       
+       #define IOSOCKET_APPLY_COPYADDR(type) \
+       iosock->type.addr.addresslen = result->result.addr.addresslen; \
+       iosock->type.addr.address = malloc(result->result.addr.addresslen); \
+       if(!iosock->type.addr.address) { \
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for sockaddr in %s:%d", __FILE__, __LINE__); \
+               iosock->type.addr.addresslen = 0; \
+               iosock->socket_flags |= IOSOCKETFLAG_DNSERROR; \
+               sprintf(errbuf, "could not allocate memory for dns information"); \
+               goto iosocket_lookup_apply_end; \
+       } \
+       memcpy(iosock->type.addr.address, result->result.addr.address, result->result.addr.addresslen);
+       
+       
+       if(bind_lookup) {
+               int usenum = ((usetype == IODNS_RECORD_AAAA) ? bind_numip6 : bind_numip4);
+               usenum = rand() % usenum;
+               for(result = bind_lookup->result; result; result = result->next) {
+                       if((result->type & usetype)) {
+                               if(usenum == 0) {
+                                       inet_ntop(((usetype == IODNS_RECORD_AAAA) ? AF_INET6 : AF_INET), ((usetype == IODNS_RECORD_AAAA) ? (void *)(&((struct sockaddr_in6 *)result->result.addr.address)->sin6_addr) : (void *)(&((struct sockaddr_in *)result->result.addr.address)->sin_addr)), errbuf, sizeof(errbuf));
+                                       iolog_trigger(IOLOG_DEBUG, "using IPv%s Address (%s) as bind address", ((usetype == IODNS_RECORD_AAAA) ? "6" : "4"), errbuf);
+                                       IOSOCKET_APPLY_COPYADDR(bind)
+                                       break;
+                               }
+                               usenum--;
+                       }
+               }
+       } else
+               iosock->bind.addr.addresslen = 0;
+       
+       if(dest_lookup) {
+               int usenum = ((usetype == IODNS_RECORD_AAAA) ? dest_numip6 : dest_numip4);
+               usenum = rand() % usenum;
+               for(result = dest_lookup->result; result; result = result->next) {
+                       if((result->type & usetype)) {
+                               if(usenum == 0) {
+                                       inet_ntop(((usetype == IODNS_RECORD_AAAA) ? AF_INET6 : AF_INET), ((usetype == IODNS_RECORD_AAAA) ? (void *)(&((struct sockaddr_in6 *)result->result.addr.address)->sin6_addr) : (void *)(&((struct sockaddr_in *)result->result.addr.address)->sin_addr)), errbuf, sizeof(errbuf));
+                                       iolog_trigger(IOLOG_DEBUG, "using IPv%s Address (%s) as dest address", ((usetype == IODNS_RECORD_AAAA) ? "6" : "4"), errbuf);
+                                       IOSOCKET_APPLY_COPYADDR(dest)
+                                       break;
+                               }
+                               usenum--;
+                       }
+               }
+       } else
+               iosock->dest.addr.addresslen = 0;
+       
+       iosocket_lookup_apply_end:
+       
+       if((iosock->socket_flags & IOSOCKETFLAG_DNSERROR)) {
+               // TODO: trigger error
+               iolog_trigger(IOLOG_ERROR, "error while trying to apply dns lookup information: %s", errbuf);
+               
+               if((iosock->socket_flags & IOSOCKETFLAG_PARENT_PUBLIC)) {
+                       //trigger event
+                       struct IOSocket *iosocket = iosock->parent;
+                       
+                       struct IOSocketEvent callback_event;
+                       callback_event.type = IOSOCKETEVENT_DNSFAILED;
+                       callback_event.socket = iosocket;
+                       callback_event.data.recv_str = errbuf;
+                       iosocket_trigger_event(&callback_event);
+                       
+                       iosocket_close(iosocket);
+               } else {
+                       // TODO: IODNS Callback
+               }
+               return 0;
+       } else
+               return 1;
+}
+
+static void socket_lookup_clear(struct _IOSocket *iosock) {
+       struct IOSocketDNSLookup *bind_lookup = ((iosock->socket_flags & IOSOCKETFLAG_DNSDONE_BINDDNS) ? iosock->bind.addrlookup : NULL);
+       struct IOSocketDNSLookup *dest_lookup = ((iosock->socket_flags & IOSOCKETFLAG_DNSDONE_DESTDNS) ? iosock->dest.addrlookup : NULL);
+       if(bind_lookup) {
+               if(bind_lookup->result)
+                       iodns_free_result(bind_lookup->result);
+               free(bind_lookup);
+               iosock->bind.addrlookup = NULL;
+       }
+       if(dest_lookup) {
+               if(dest_lookup->result)
+                       iodns_free_result(dest_lookup->result);
+               free(dest_lookup);
+               iosock->dest.addrlookup = NULL;
+       }
+}
+
+static void iosocket_prepare_fd(int sockfd) {
+       // prevent SIGPIPE
+       #ifndef WIN32
+       #if defined(SO_NOSIGPIPE)
+       {
+               int set = 1;
+               setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
+       }
+       #else
+       signal(SIGPIPE, SIG_IGN);
+       #endif
+       #endif
+       
+       // make sockfd unblocking
+       #if defined(F_GETFL)
+       {
+               int fcntl_flags;
+               fcntl_flags = fcntl(sockfd, F_GETFL);
+               fcntl(sockfd, F_SETFL, fcntl_flags|O_NONBLOCK);
+               fcntl_flags = fcntl(sockfd, F_GETFD);
+               fcntl(sockfd, F_SETFD, fcntl_flags|FD_CLOEXEC);
+       }
+       #elif defined(FIONBIO)
+       {
+               unsigned long ulong = 1;
+               ioctlsocket(sockfd, FIONBIO, &ulong);
+       }
+       #endif
+}
+
+static void iosocket_connect_finish(struct _IOSocket *iosock) {
+       int sockfd;
+       if((iosock->socket_flags & IOSOCKETFLAG_IPV6SOCKET))
+               sockfd = socket(AF_INET6, SOCK_STREAM, 0);
+       else
+               sockfd = socket(AF_INET, SOCK_STREAM, 0);
+       if(sockfd == -1) {
+               iolog_trigger(IOLOG_ERROR, "could not create socket in %s:%d", __FILE__, __LINE__);
+               // TODO: trigger error
+               
+               return;
+       }
+       
+       // set port and bind address
+       if((iosock->socket_flags & IOSOCKETFLAG_IPV6SOCKET)) {
+               struct sockaddr_in6 *ip6 = (void*) iosock->dest.addr.address;
+               ip6->sin6_family = AF_INET6;
+               ip6->sin6_port = htons(iosock->port);
+               
+               if(iosock->bind.addr.addresslen) {
+                       struct sockaddr_in6 *ip6bind = (void*) iosock->bind.addr.address;
+                       ip6bind->sin6_family = AF_INET6;
+                       ip6bind->sin6_port = htons(0);
+                       
+                       bind(sockfd, (struct sockaddr*)ip6bind, sizeof(*ip6bind));
+               }
+       } else {
+               struct sockaddr_in *ip4 = (void*) iosock->dest.addr.address;
+               ip4->sin_family = AF_INET;
+               ip4->sin_port = htons(iosock->port);
+               
+               if(iosock->bind.addr.addresslen) {
+                       struct sockaddr_in *ip4bind = (void*) iosock->bind.addr.address;
+                       ip4bind->sin_family = AF_INET;
+                       ip4bind->sin_port = htons(0);
+                       
+                       bind(sockfd, (struct sockaddr*)ip4bind, sizeof(*ip4bind));
+               }
+       }
+       
+       iosocket_prepare_fd(sockfd);
+       
+       int ret = connect(sockfd, iosock->dest.addr.address, iosock->dest.addr.addresslen); //returns EINPROGRESS here (nonblocking)
+       iolog_trigger(IOLOG_DEBUG, "connecting socket (connect: %d)", ret);
+       
+       iosock->fd = sockfd;
+       iosock->socket_flags |= IOSOCKETFLAG_CONNECTING;
+       
+       iosocket_activate(iosock);
+}
+
+static void iosocket_listen_finish(struct _IOSocket *iosock) {
+       int sockfd;
+       if((iosock->socket_flags & IOSOCKETFLAG_IPV6SOCKET))
+               sockfd = socket(AF_INET6, SOCK_STREAM, 0);
+       else
+               sockfd = socket(AF_INET, SOCK_STREAM, 0);
+       if(sockfd == -1) {
+               iolog_trigger(IOLOG_ERROR, "could not create socket in %s:%d", __FILE__, __LINE__);
+               // TODO: trigger error
+               
+               return;
+       }
+       
+       // set port and bind address
+       if((iosock->socket_flags & IOSOCKETFLAG_IPV6SOCKET)) {
+               struct sockaddr_in6 *ip6bind = (void*) iosock->bind.addr.address;
+               ip6bind->sin6_family = AF_INET6;
+               ip6bind->sin6_port = htons(iosock->port);
+               
+               int opt = 1;
+               setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
+               
+               bind(sockfd, (struct sockaddr*)ip6bind, sizeof(*ip6bind));
+       } else {
+               struct sockaddr_in *ip4bind = (void*) iosock->bind.addr.address;
+               ip4bind->sin_family = AF_INET;
+               ip4bind->sin_port = htons(iosock->port);
+               
+               int opt = 1;
+               setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
+               
+               bind(sockfd, (struct sockaddr*)ip4bind, sizeof(*ip4bind));
+       }
+       
+       iosocket_prepare_fd(sockfd);
+       
+       listen(sockfd, 1);
+       iosock->fd = sockfd;
+       
+       iosocket_activate(iosock);
+}
+
+struct _IOSocket *iosocket_accept_client(struct _IOSocket *iosock) {
+       struct IOSocket *new_iosocket = calloc(1, sizeof(*new_iosocket));
+       if(!new_iosocket) {
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOSocket in %s:%d", __FILE__, __LINE__);
+               close(accept(iosock->fd, NULL, 0)); // simply drop connection
+               return NULL;
+       }
+       struct _IOSocket *new_iosock = _create_socket();
+       if(!new_iosock) {
+               free(new_iosocket);
+               close(accept(iosock->fd, NULL, 0)); // simply drop connection
+               return NULL;
+       }
+       new_iosocket->iosocket = new_iosock;
+       new_iosocket->status = IOSOCKET_CONNECTED;
+       new_iosocket->data = iosock;
+       new_iosock->parent = new_iosocket;
+       new_iosock->socket_flags |= IOSOCKETFLAG_PARENT_PUBLIC | IOSOCKETFLAG_INCOMING | (iosock->socket_flags & IOSOCKETFLAG_IPV6SOCKET);
+       
+       struct sockaddr_storage addr;
+       socklen_t addrlen = sizeof(addr);
+       
+       //accept client
+       new_iosock->fd = accept(iosock->fd, (struct sockaddr *)&addr, &addrlen);
+       
+       //copy remote address
+       new_iosock->dest.addr.address = malloc(addrlen);
+       if(!new_iosock->dest.addr.address) {
+               close(new_iosock->fd);
+               free(new_iosock);
+               free(new_iosock);
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for sockaddr in %s:%d", __FILE__, __LINE__);
+               return NULL;
+       }
+       memcpy(new_iosock->dest.addr.address, &addr, addrlen);
+       new_iosock->dest.addr.addresslen = addrlen;
+       
+       //copy local address
+       new_iosock->bind.addr.address = malloc(iosock->bind.addr.addresslen);
+       if(!new_iosock->bind.addr.address) {
+               close(new_iosock->fd);
+               free(new_iosock);
+               free(new_iosock);
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for sockaddr in %s:%d", __FILE__, __LINE__);
+               return NULL;
+       }
+       memcpy(new_iosock->bind.addr.address, iosock->bind.addr.address, iosock->bind.addr.addresslen);
+       new_iosock->bind.addr.addresslen = iosock->bind.addr.addresslen;
+       
+       //prepare new socket fd
+       iosocket_prepare_fd(new_iosock->fd);
+       
+       if((iosock->socket_flags & IOSOCKETFLAG_SSLSOCKET)) {
+               new_iosocket->ssl = 1;
+               new_iosock->socket_flags |= IOSOCKETFLAG_SSLSOCKET;
+               \r
+               iossl_client_accepted(iosock, new_iosock);
+       }
+       
+       iosocket_activate(new_iosock);
+       return new_iosock;
+}
+
+/* public functions */
+
+struct IOSocket *iosocket_connect(const char *hostname, unsigned int port, int ssl, const char *bindhost, iosocket_callback *callback) {
+       return iosocket_connect_flags(hostname, port, ssl, bindhost, callback, (IOSOCKET_ADDR_IPV4 | IOSOCKET_ADDR_IPV6));
+}
+
+struct IOSocket *iosocket_connect_flags(const char *hostname, unsigned int port, int ssl, const char *bindhost, iosocket_callback *callback, int flags) {
+       struct IOSocket *iodescriptor = calloc(1, sizeof(*iodescriptor));
+       if(!iodescriptor) {
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOSocket in %s:%d", __FILE__, __LINE__);
+               return NULL;
+       }
+       
+       struct _IOSocket *iosock = _create_socket();
+       if(!iosock) {
+               free(iodescriptor);
+               return NULL;
+       }
+       
+       iodescriptor->iosocket = iosock;
+       iodescriptor->status = IOSOCKET_CONNECTING;
+       iodescriptor->callback = callback;
+       iosock->parent = iodescriptor;
+       iosock->socket_flags |= IOSOCKETFLAG_PARENT_PUBLIC;
+       iosock->port = port;
+       if(ssl) {
+               iodescriptor->ssl = 1;
+               iosock->socket_flags |= IOSOCKETFLAG_SSLSOCKET;
+       }
+       
+       if(bindhost) {
+               switch(iosocket_parse_address(bindhost, &iosock->bind.addr, flags)) {
+               case -1:
+                       free(iosock);
+                       return NULL;
+               case 0:
+                       /* start dns lookup */
+                       iosock->socket_flags |= IOSOCKETFLAG_PENDING_BINDDNS;
+                       iosocket_lookup_hostname(iosock, bindhost, flags, 1);
+                       break;
+               case 1:
+                       /* valid address */
+                       break;
+               }
+       }
+       switch(iosocket_parse_address(hostname, &iosock->dest.addr, flags)) {
+       case -1:
+               free(iosock);
+               return NULL;
+       case 0:
+               /* start dns lookup */
+               iosock->socket_flags |= IOSOCKETFLAG_PENDING_DESTDNS;
+               iosocket_lookup_hostname(iosock, hostname, flags, 0);
+               break;
+       case 1:
+               /* valid address */
+               break;
+       }
+       if((iosock->socket_flags & (IOSOCKETFLAG_PENDING_BINDDNS | IOSOCKETFLAG_PENDING_DESTDNS)) == 0) {
+               iosocket_connect_finish(iosock);
+       }
+       return iodescriptor;
+}
+
+struct IOSocket *iosocket_listen(const char *hostname, unsigned int port, iosocket_callback *callback) {
+       return iosocket_listen_flags(hostname, port, callback, (IOSOCKET_ADDR_IPV4 | IOSOCKET_ADDR_IPV6));
+}
+
+struct IOSocket *iosocket_listen_flags(const char *hostname, unsigned int port, iosocket_callback *callback, int flags) {
+       struct IOSocket *iodescriptor = calloc(1, sizeof(*iodescriptor));
+       if(!iodescriptor) {
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOSocket in %s:%d", __FILE__, __LINE__);
+               return NULL;
+       }
+       
+       struct _IOSocket *iosock = _create_socket();
+       if(!iosock) {
+               free(iodescriptor);
+               return NULL;
+       }
+       
+       iodescriptor->iosocket = iosock;
+       iodescriptor->status = IOSOCKET_LISTENING;
+       iodescriptor->listening = 1;
+       iodescriptor->callback = callback;
+       iosock->parent = iodescriptor;
+       iosock->socket_flags |= IOSOCKETFLAG_PARENT_PUBLIC | IOSOCKETFLAG_LISTENING;
+       iosock->port = port;
+       
+       switch(iosocket_parse_address(hostname, &iosock->bind.addr, flags)) {
+       case -1:
+               free(iosock);
+               return NULL;
+       case 0:
+               /* start dns lookup */
+               iosock->socket_flags |= IOSOCKETFLAG_PENDING_BINDDNS;
+               iosocket_lookup_hostname(iosock, hostname, flags, 1);
+               break;
+       case 1:
+               /* valid address */
+               break;
+       }
+       if((iosock->socket_flags & IOSOCKETFLAG_PENDING_BINDDNS) == 0) {
+               iosocket_listen_finish(iosock);
+       }
+       return iodescriptor;
+}
+
+struct IOSocket *iosocket_listen_ssl(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iosocket_callback *callback) {
+       return iosocket_listen_ssl_flags(hostname, port, certfile, keyfile, callback, (IOSOCKET_ADDR_IPV4 | IOSOCKET_ADDR_IPV6));
+}
+
+struct IOSocket *iosocket_listen_ssl_flags(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iosocket_callback *callback, int flags) {
+       struct IOSocket *iosocket = iosocket_listen_flags(hostname, port, callback, flags);\r
+       struct _IOSocket *iosock = iosocket->iosocket;\r
+       iosock->socket_flags |= IOSOCKETFLAG_SSLSOCKET;\r
+       iossl_listen(iosock, certfile, keyfile);
+       return iosocket;
+}
+
+void iosocket_close(struct IOSocket *iosocket) {
+       struct _IOSocket *iosock = iosocket->iosocket;
+       if(iosock == NULL) {
+               iolog_trigger(IOLOG_WARNING, "called iosocket_close for destroyed IOSocket in %s:%d", __FILE__, __LINE__);
+               return;
+       }
+       
+       iosock->socket_flags |= IOSOCKETFLAG_SHUTDOWN;
+       
+       if(iosock->writebuf.bufpos) {
+               //try to send everything before closing
+#if defined(F_GETFL)
+               {
+                       int flags;
+                       flags = fcntl(iosock->fd, F_GETFL);
+                       fcntl(iosock->fd, F_SETFL, flags & ~O_NONBLOCK);
+                       flags = fcntl(iosock->fd, F_GETFD);
+                       fcntl(iosock->fd, F_SETFD, flags|FD_CLOEXEC);
+               }
+#else
+               iosocket_deactivate(iosock);
+#endif
+               iosocket_try_write(iosock);
+       }
+       //close IOSocket
+       if((iosock->socket_flags & IOSOCKETFLAG_SSLSOCKET))
+               iossl_disconnect(iosock);
+       if(iosock->fd)
+               close(iosock->fd);
+       _free_socket(iosock);
+       iosocket->iosocket = NULL;
+       iosocket->status = IOSOCKET_CLOSED;
+       iogc_add(iosocket);
+}
+
+static int iosocket_try_write(struct _IOSocket *iosock) {
+       if(!iosock->writebuf.bufpos && !(iosock->socket_flags & IOSOCKETFLAG_SSL_WRITEHS)) \r
+               return 0;
+       iolog_trigger(IOLOG_DEBUG, "write writebuf (%d bytes) to socket (fd: %d)", iosock->writebuf.bufpos, iosock->fd);
+       int res;
+       if((iosock->socket_flags & IOSOCKETFLAG_SSLSOCKET))
+               res = iossl_write(iosock, iosock->writebuf.buffer, iosock->writebuf.bufpos);
+       else
+               res = send(iosock->fd, iosock->writebuf.buffer, iosock->writebuf.bufpos, 0);
+       if(res < 0) {
+               if (errno != EAGAIN && errno != EWOULDBLOCK)
+                       iolog_trigger(IOLOG_ERROR, "could not write to socket (fd: %d): %d - %s", iosock->fd, errno, strerror(errno));
+               else
+                       res = 0;
+       } else {
+               iosock->writebuf.bufpos -= res;
+               if((iosock->socket_flags & (IOSOCKETFLAG_ACTIVE | IOSOCKETFLAG_SHUTDOWN)) == IOSOCKETFLAG_ACTIVE)
+                       engine->update(iosock);
+       }
+       return res;
+}
+
+void iosocket_send(struct IOSocket *iosocket, const char *data, size_t datalen) {
+       struct _IOSocket *iosock = iosocket->iosocket;
+       if(iosock == NULL) {
+               iolog_trigger(IOLOG_WARNING, "called iosocket_close for destroyed IOSocket in %s:%d", __FILE__, __LINE__);
+               return;
+       }
+       if(iosock->socket_flags & IOSOCKETFLAG_SHUTDOWN) {
+               iolog_trigger(IOLOG_ERROR, "could not write to socket (socket is closing)");
+               return;
+       }
+       iolog_trigger(IOLOG_DEBUG, "add %d to writebuf (fd: %d): %s", datalen, iosock->fd, data);
+       if(iosock->writebuf.buflen < iosock->writebuf.bufpos + datalen) {
+               iolog_trigger(IOLOG_DEBUG, "increase writebuf (curr: %d) to %d (+%d bytes)", iosock->writebuf.buflen, iosock->writebuf.bufpos + datalen, (iosock->writebuf.bufpos + datalen - iosock->writebuf.buflen));
+               iosocket_increase_buffer(&iosock->writebuf, iosock->writebuf.bufpos + datalen);
+               if(iosock->writebuf.buflen < iosock->writebuf.bufpos + datalen) {
+                       iolog_trigger(IOLOG_ERROR, "increase writebuf (curr: %d) to %d (+%d bytes) FAILED", iosock->writebuf.buflen, iosock->writebuf.bufpos + datalen, (iosock->writebuf.bufpos + datalen - iosock->writebuf.buflen));
+                       return;
+               }
+       }
+       memcpy(iosock->writebuf.buffer + iosock->writebuf.bufpos, data, datalen);
+       iosock->writebuf.bufpos += datalen;
+       if((iosock->socket_flags & IOSOCKETFLAG_ACTIVE))
+               engine->update(iosock);
+}
+
+void iosocket_write(struct IOSocket *iosocket, const char *line) {
+       size_t linelen = strlen(line);
+       iosocket_send(iosocket, line, linelen);
+}
+
+void iosocket_printf(struct IOSocket *iosocket, const char *text, ...) {
+       va_list arg_list;
+       char sendBuf[IOSOCKET_PRINTF_LINE_LEN];
+       int pos;
+       sendBuf[0] = '\0';
+       va_start(arg_list, text);
+       pos = vsnprintf(sendBuf, IOSOCKET_PRINTF_LINE_LEN - 1, text, arg_list);
+       va_end(arg_list);
+       if (pos < 0 || pos > (IOSOCKET_PRINTF_LINE_LEN - 1)) pos = IOSOCKET_PRINTF_LINE_LEN - 1;
+       sendBuf[pos] = '\0';
+       iosocket_send(iosocket, sendBuf, pos);
+}
+
+
+int iosocket_wants_reads(struct _IOSocket *iosock) {
+       if((iosock->socket_flags & (IOSOCKETFLAG_SSL_READHS | IOSOCKETFLAG_SSL_WRITEHS)))
+               return ((iosock->socket_flags & IOSOCKETFLAG_SSL_WANTWRITE) ? 0 : 1);
+       if(!(iosock->socket_flags & IOSOCKETFLAG_OVERRIDE_WANT_RW))
+               return 1;
+       else if((iosock->socket_flags & IOSOCKETFLAG_OVERRIDE_WANT_R))
+               return 1;
+       return 0;
+}
+int iosocket_wants_writes(struct _IOSocket *iosock) {
+       if((iosock->socket_flags & (IOSOCKETFLAG_SSL_READHS | IOSOCKETFLAG_SSL_WRITEHS)))
+               return ((iosock->socket_flags & IOSOCKETFLAG_SSL_WANTWRITE) ? 1 : 0);
+       if(!(iosock->socket_flags & IOSOCKETFLAG_OVERRIDE_WANT_RW)) {
+               if(iosock->writebuf.bufpos || (iosock->socket_flags & IOSOCKETFLAG_CONNECTING))
+                       return 1;
+               else
+                       return 0;
+       } else if((iosock->socket_flags & IOSOCKETFLAG_OVERRIDE_WANT_W))
+               return 1;
+       return 0;
+}
+
+
+static void iosocket_trigger_event(struct IOSocketEvent *event) {
+       if(!event->socket->callback) 
+               return;
+       iolog_trigger(IOLOG_DEBUG, "triggering event");
+       event->socket->callback(event);
+}
+
+void iosocket_events_callback(struct _IOSocket *iosock, int readable, int writeable) {
+       if((iosock->socket_flags & IOSOCKETFLAG_PARENT_PUBLIC)) {
+               struct IOSocket *iosocket = iosock->parent;
+               struct IOSocketEvent callback_event;
+               callback_event.type = IOSOCKETEVENT_IGNORE;
+               callback_event.socket = iosocket;
+               
+               if((iosock->socket_flags & IOSOCKETFLAG_SSL_HANDSHAKE)) {
+                       if(readable || writeable) {
+                               if((iosock->socket_flags & IOSOCKETFLAG_INCOMING)) 
+                                       iossl_server_handshake(iosock);
+                               else
+                                       iossl_client_handshake(iosock);
+                               engine->update(iosock);\r
+                       } else if((iosock->socket_flags & IOSOCKETFLAG_LISTENING)) {\r
+                               //TODO: SSL init error\r
+                       } else if((iosock->socket_flags & IOSOCKETFLAG_INCOMING)) {\r
+                               if((iosock->socket_flags & IOSOCKETFLAG_SSL_ESTABLISHED)) {\r
+                                       //incoming SSL connection accepted\r
+                                       iosock->socket_flags &= ~IOSOCKETFLAG_SSL_HANDSHAKE;\r
+                                       callback_event.type = IOSOCKETEVENT_ACCEPT;\r
+                                       callback_event.data.accept_socket = iosock->parent;
+                                       struct _IOSocket *parent_socket = iosocket->data;
+                                       callback_event.socket = parent_socket->parent;
+                                       
+                                       //initialize readbuf
+                                       iosocket_increase_buffer(&iosock->readbuf, 1024);\r
+                               } else {\r
+                                       //incoming SSL connection failed, simply drop\r
+                                       iosock->socket_flags |= IOSOCKETFLAG_DEAD;\r
+                                       iolog_trigger(IOLOG_ERROR, "SSL Handshake failed for incoming connection. Dropping fd %d", iosock->fd);\r
+                               }
+                       } else {
+                               // SSL Backend finished
+                               if((iosock->socket_flags & IOSOCKETFLAG_SSL_ESTABLISHED)) {\r
+                                       iosocket->status = IOSOCKET_CONNECTED;\r
+                                       iosock->socket_flags &= ~IOSOCKETFLAG_SSL_HANDSHAKE;\r
+                                       callback_event.type = IOSOCKETEVENT_CONNECTED;\r
+                                       engine->update(iosock);\r
+                                       \r
+                                       //initialize readbuf\r
+                                       iosocket_increase_buffer(&iosock->readbuf, 1024);
+                               } else {
+                                       callback_event.type = IOSOCKETEVENT_NOTCONNECTED;\r
+                                       iosock->socket_flags |= IOSOCKETFLAG_DEAD;
+                               }
+                       }
+               } else if((iosock->socket_flags & IOSOCKETFLAG_LISTENING)) {
+                       if(readable) {
+                               //new client connected
+                               struct _IOSocket *new_iosock = iosocket_accept_client(iosock);
+                               if(!new_iosock)
+                                       return;
+                               
+                               if(!(new_iosock->socket_flags & IOSOCKETFLAG_SSLSOCKET)) {
+                                       callback_event.type = IOSOCKETEVENT_ACCEPT;
+                                       callback_event.data.accept_socket = new_iosock->parent;
+                               }
+                       }
+                       
+               } else if((iosock->socket_flags & IOSOCKETFLAG_CONNECTING)) {
+                       if(readable) { //could not connect
+                               if((iosock->socket_flags & (IOSOCKETFLAG_IPV6SOCKET | IOSOCKETFLAG_RECONNECT_IPV4)) == (IOSOCKETFLAG_IPV6SOCKET | IOSOCKETFLAG_RECONNECT_IPV4)) {
+                                       iolog_trigger(IOLOG_DEBUG, "connecting to IPv6 host (%s) failed. trying to connect using IPv4.", iosock->dest.addrlookup->hostname);
+                                       iosocket_deactivate(iosock);
+                                       if(iosocket_lookup_apply(iosock, 1)) { //if ret=0 an error occured in iosocket_lookup_apply and we should stop here.
+                                               iosocket_connect_finish(iosock);
+                                               socket_lookup_clear(iosock);
+                                       }
+                               } else {
+                                       callback_event.type = IOSOCKETEVENT_NOTCONNECTED;
+                                       /*
+                                       socklen_t arglen = sizeof(callback_event.data.errid);
+                                       if (getsockopt(iosock->fd, SOL_SOCKET, SO_ERROR, &callback_event.data.errid, &arglen) < 0)
+                                               callback_event.data.errid = errno;
+                                       */
+                                       iosock->socket_flags |= IOSOCKETFLAG_DEAD;
+                               }
+                       } else if(writeable) { //connection established
+                               iosock->socket_flags &= ~IOSOCKETFLAG_CONNECTING;
+                               socket_lookup_clear(iosock);
+                               if((iosock->socket_flags & IOSOCKETFLAG_SSLSOCKET)) {\r
+                                       iolog_trigger(IOLOG_DEBUG, "SSL client socket connected. Stating SSL handshake...");
+                                       iossl_connect(iosock);
+                                       engine->update(iosock);
+                                       return;
+                               }
+                               iosocket->status = IOSOCKET_CONNECTED;
+                               
+                               callback_event.type = IOSOCKETEVENT_CONNECTED;
+                               engine->update(iosock);
+                               
+                               //initialize readbuf
+                               iosocket_increase_buffer(&iosock->readbuf, 1024);
+                       }
+               } else {\r
+                       int ssl_rehandshake = 0;\r
+                       if((iosock->socket_flags & IOSOCKETFLAG_SSLSOCKET)) {\r
+                               if((iosock->socket_flags & IOSOCKETFLAG_SSL_READHS))\r
+                                       ssl_rehandshake = 1;\r
+                               else if((iosock->socket_flags & IOSOCKETFLAG_SSL_WRITEHS))\r
+                                       ssl_rehandshake = 2;\r
+                       }
+                       iosocketevents_callback_retry_read:
+                       if((readable && ssl_rehandshake == 0) || ssl_rehandshake == 1) {
+                               int bytes;
+                               if(iosock->readbuf.buflen - iosock->readbuf.bufpos >= 128) {
+                                       int addsize;
+                                       if(iosock->readbuf.buflen >= 2048)
+                                               addsize = 1024;
+                                       else
+                                               addsize = iosock->readbuf.buflen;
+                                       iosocket_increase_buffer(&iosock->readbuf, iosock->readbuf.buflen + addsize);
+                               }
+                               if((iosock->socket_flags & IOSOCKETFLAG_SSLSOCKET))
+                                       bytes = iossl_read(iosock, iosock->readbuf.buffer + iosock->readbuf.bufpos, iosock->readbuf.buflen - iosock->readbuf.bufpos);
+                               else 
+                                       bytes = recv(iosock->fd, iosock->readbuf.buffer + iosock->readbuf.bufpos, iosock->readbuf.buflen - iosock->readbuf.bufpos, 0);
+                               
+                               if(bytes <= 0) {\r
+                                       if((iosock->socket_flags & (IOSOCKETFLAG_SSLSOCKET | IOSOCKETFLAG_SSL_READHS)) == (IOSOCKETFLAG_SSLSOCKET | IOSOCKETFLAG_SSL_READHS)) {\r
+                                               ssl_rehandshake = 1;\r
+                                       } else if (errno != EAGAIN || errno != EWOULDBLOCK) {
+                                               iosock->socket_flags |= IOSOCKETFLAG_DEAD;
+                                               
+                                               callback_event.type = IOSOCKETEVENT_CLOSED;
+                                               callback_event.data.errid = errno;
+                                       }
+                               } else {
+                                       int i;
+                                       iolog_trigger(IOLOG_DEBUG, "received %d bytes (fd: %d). readbuf position: %d", bytes, iosock->fd, iosock->readbuf.bufpos);
+                                       iosock->readbuf.bufpos += bytes;
+                                       int retry_read = (iosock->readbuf.bufpos == iosock->readbuf.buflen);
+                                       callback_event.type = IOSOCKETEVENT_RECV;
+                                       
+                                       if(iosocket->parse_delimiter) {
+                                               int j, used_bytes = 0;
+                                               for(i = 0; i < iosock->readbuf.bufpos; i++) {
+                                                       int is_delimiter = 0;
+                                                       for(j = 0; j < IOSOCKET_PARSE_DELIMITERS_COUNT; j++) {
+                                                               if(iosock->readbuf.buffer[i] == iosocket->delimiters[j]) {
+                                                                       is_delimiter = 1;
+                                                                       break;
+                                                               }
+                                                       }
+                                                       if(is_delimiter) {
+                                                               iosock->readbuf.buffer[i] = 0;
+                                                               callback_event.data.recv_str = iosock->readbuf.buffer + used_bytes;
+                                                               iolog_trigger(IOLOG_DEBUG, "parsed line (%d bytes): %s", i - used_bytes, iosock->readbuf.buffer + used_bytes);
+                                                               used_bytes = i+1;
+                                                               if(iosock->readbuf.buffer[i-1] != 0 || iosocket->parse_empty)
+                                                                       iosocket_trigger_event(&callback_event);
+                                                       }
+                                                       #ifdef IOSOCKET_PARSE_LINE_LIMIT
+                                                       else if(i + 1 - used_bytes >= IOSOCKET_PARSE_LINE_LIMIT) {
+                                                               iosock->readbuf.buffer[i] = 0;
+                                                               callback_event.data.recv_str = iosock->readbuf.buffer + used_bytes;
+                                                               iolog_trigger(IOLOG_DEBUG, "parsed and stripped line (%d bytes): %s", i - used_bytes, iosock->readbuf.buffer + used_bytes);
+                                                               for(; i < iosock->readbuf.bufpos; i++) { //skip the rest of the line
+                                                                       is_delimiter = 0;
+                                                                       if(iosock->readbuf.buffer[i] == iosocket->delimiters[j])
+                                                                               break;
+                                                               }
+                                                               used_bytes = i+1;
+                                                               iosocket_trigger_event(&callback_event);
+                                                       }
+                                                       #endif
+                                               }
+                                               if(used_bytes) {
+                                                       if(used_bytes == iosock->readbuf.bufpos) {
+                                                               iosock->readbuf.bufpos = 0;
+                                                               iolog_trigger(IOLOG_DEBUG, "readbuf fully processed (set buffer position to 0)");
+                                                       } else {
+                                                               iolog_trigger(IOLOG_DEBUG, "readbuf rest: %d bytes (used %d bytes)", iosock->readbuf.bufpos - used_bytes, used_bytes);
+                                                               memmove(iosock->readbuf.buffer, iosock->readbuf.buffer + used_bytes, iosock->readbuf.bufpos - used_bytes);
+                                                               iosock->readbuf.bufpos -= used_bytes;
+                                                       }
+                                               }
+                                               callback_event.type = IOSOCKETEVENT_IGNORE;
+                                       } else
+                                               callback_event.data.recv_buf = &iosock->readbuf;
+                                       if(retry_read)
+                                               goto iosocketevents_callback_retry_read;
+                               }
+                       }
+                       if((writeable && ssl_rehandshake == 0) || ssl_rehandshake == 2) {
+                               int bytes;
+                               bytes = iosocket_try_write(iosock);
+                               if(bytes < 0) {\r
+                                       if((iosock->socket_flags & (IOSOCKETFLAG_SSLSOCKET | IOSOCKETFLAG_SSL_WRITEHS)) == (IOSOCKETFLAG_SSLSOCKET | IOSOCKETFLAG_SSL_WRITEHS)) {\r
+                                               ssl_rehandshake = 1;\r
+                                       } else {
+                                               iosock->socket_flags |= IOSOCKETFLAG_DEAD;
+                                               
+                                               callback_event.type = IOSOCKETEVENT_CLOSED;
+                                               callback_event.data.errid = errno;\r
+                                       }
+                               }
+                       }\r
+                       if(ssl_rehandshake) {
+                               engine->update(iosock);\r
+                       }
+               }
+               if(callback_event.type != IOSOCKETEVENT_IGNORE)
+                       iosocket_trigger_event(&callback_event);
+               if((iosock->socket_flags & IOSOCKETFLAG_DEAD))
+                       iosocket_close(iosocket);
+               
+       } else if((iosock->socket_flags & IOSOCKETFLAG_PARENT_DNSENGINE)) {
+               iodns_socket_callback(iosock, readable, writeable);
+       }
+}
+
+void iosocket_loop(int usec) {
+       struct timeval timeout;
+       timeout.tv_sec = usec / 1000000;
+       timeout.tv_usec = usec % 1000000;
+       engine->loop(&timeout);
+}
diff --git a/src/IOHandler/IOSockets.h b/src/IOHandler/IOSockets.h
new file mode 100644 (file)
index 0000000..57bc4c7
--- /dev/null
@@ -0,0 +1,210 @@
+/* IOSockets.h - IOMultiplexer v2
+ * Copyright (C) 2014  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 _IOSockets_h
+#define _IOSockets_h
+#include <sys/time.h>
+#include <stddef.h>
+#include "IODNSAddress.struct.h"
+
+struct IOSocketBuffer {
+       char *buffer;
+       size_t bufpos, buflen;
+};
+
+#ifndef _IOHandler_internals
+#include "IOHandler.h"
+#else
+
+struct _IOSocket;
+struct IOSocket;
+struct IOSocketBuffer;
+struct IOSSLDescriptor;
+struct _IODNSQuery;
+struct IODNSEvent;
+
+struct IOEngine {
+       const char *name;
+       int (*init)(void);
+       void (*add)(struct _IOSocket *iosock);
+       void (*remove)(struct _IOSocket *iosock);
+       void (*update)(struct _IOSocket *iosock);
+       void (*loop)(struct timeval *timeout);
+       void (*cleanup)(void);
+};
+
+/* IO Engines */
+extern struct IOEngine engine_select; /* select system call (should always be useable) */
+extern struct IOEngine engine_kevent;
+extern struct IOEngine engine_epoll;
+extern struct IOEngine engine_win32;
+
+
+/* _IOSocket linked list */
+extern struct _IOSocket *iosocket_first;
+extern struct _IOSocket *iosocket_last;
+
+/* _IOSocket socket_flags */
+#define IOSOCKETFLAG_ACTIVE           0x00000001
+#define IOSOCKETFLAG_LISTENING        0x00000002
+#define IOSOCKETFLAG_IPV6SOCKET       0x00000004
+#define IOSOCKETFLAG_INCOMING         0x00000008 /* incoming (accepted) connection */
+#define IOSOCKETFLAG_CONNECTING       0x00000010
+#define IOSOCKETFLAG_SHUTDOWN         0x00000020 /* disconnect pending */
+#define IOSOCKETFLAG_DEAD             0x00000040 /* socket dead (disconnected) */
+#define IOSOCKETFLAG_RECONNECT_IPV4   0x00000080 /* possible fallback to ipv4 connect if ipv6 fails */
+
+/* DNS Flags */
+#define IOSOCKETFLAG_PENDING_BINDDNS  0x00000100
+#define IOSOCKETFLAG_PENDING_DESTDNS  0x00000200
+#define IOSOCKETFLAG_DNSDONE_BINDDNS  0x00000400
+#define IOSOCKETFLAG_DNSDONE_DESTDNS  0x00000800
+#define IOSOCKETFLAG_DNSERROR         0x00001000
+
+/* SSL Flags */
+#define IOSOCKETFLAG_SSLSOCKET        0x00002000 /* use ssl after connecting */
+#define IOSOCKETFLAG_SSL_HANDSHAKE    0x00004000 /* SSL Handshake in progress */
+#define IOSOCKETFLAG_SSL_WANTWRITE    0x00008000 /* ssl module wants write access */
+#define IOSOCKETFLAG_SSL_READHS       0x00010000 /* ssl read rehandshake */
+#define IOSOCKETFLAG_SSL_WRITEHS      0x00020000 /* ssl write rehandshake */
+#define IOSOCKETFLAG_SSL_ESTABLISHED  0x00040000 /* ssl connection established */
+
+/* WANT_READ / WANT_WRITE override */
+#define IOSOCKETFLAG_OVERRIDE_WANT_RW 0x00080000
+#define IOSOCKETFLAG_OVERRIDE_WANT_R  0x00100000
+#define IOSOCKETFLAG_OVERRIDE_WANT_W  0x00200000
+
+/* Parent descriptors */
+#define IOSOCKETFLAG_PARENT_PUBLIC    0x10000000
+#define IOSOCKETFLAG_PARENT_DNSENGINE 0x20000000
+/* reserved flags for additional descriptor types? */
+
+struct IOSocketDNSLookup {
+       unsigned int bindlookup : 1;
+       char hostname[256];
+       struct _IOSocket *iosocket;
+       struct _IODNSQuery *query;
+       struct IODNSResult *result;
+};
+
+struct _IOSocket {
+       int fd;
+       
+       unsigned int socket_flags : 32;
+       
+       struct {
+               struct IODNSAddress addr;
+               struct IOSocketDNSLookup *addrlookup;
+       } bind;
+       struct {
+               struct IODNSAddress addr;
+               struct IOSocketDNSLookup *addrlookup;
+       } dest;
+       
+       unsigned int port : 16;
+       
+       struct IOSocketBuffer readbuf;
+       struct IOSocketBuffer writebuf;
+       
+       struct IOSSLDescriptor *sslnode;
+       
+       void *parent;
+       
+       struct _IOSocket *next, *prev;
+};
+
+void _init_sockets();
+struct _IOSocket *_create_socket();
+void _free_socket(struct _IOSocket *iosock);
+void iosocket_activate(struct _IOSocket *iosock);
+void iosocket_deactivate(struct _IOSocket *iosock);
+void iosocket_update(struct _IOSocket *iosock);
+
+void iosocket_loop(int usec);
+void iosocket_lookup_callback(struct IOSocketDNSLookup *lookup, struct IODNSEvent *event);
+void iosocket_events_callback(struct _IOSocket *iosock, int readable, int writeable);
+
+int iosocket_wants_reads(struct _IOSocket *iosock);
+int iosocket_wants_writes(struct _IOSocket *iosock);
+
+#endif
+
+struct IOSocketEvent;
+
+#define IOSOCKET_CALLBACK(NAME) void NAME(struct IOSocketEvent *event)
+typedef IOSOCKET_CALLBACK(iosocket_callback);
+
+enum IOSocketStatus { 
+       IOSOCKET_CLOSED, /* descriptor is dead (socket waiting for removal or timer) */
+       IOSOCKET_LISTENING, /* descriptor is waiting for connections (server socket) */
+       IOSOCKET_CONNECTING, /* descriptor is waiting for connection approval (connecting client socket) */
+       IOSOCKET_CONNECTED, /* descriptor is connected (connected client socket) */
+       IOSOCKET_SSLHANDSHAKE /* descriptor is waiting for ssl (handshake) */
+};
+
+enum IOSocketEventType {
+       IOSOCKETEVENT_IGNORE,
+       IOSOCKETEVENT_RECV, /* client socket received something (read_lines == 1  =>  recv_str valid;  read_lines == 0  =>  recv_buf valid) */
+       IOSOCKETEVENT_CONNECTED, /* client socket connected successful */
+       IOSOCKETEVENT_NOTCONNECTED, /* client socket could not connect (errid valid) */
+       IOSOCKETEVENT_CLOSED, /* client socket lost connection (errid valid) */
+       IOSOCKETEVENT_ACCEPT, /* server socket accepted new connection (accept_socket valid) */
+       IOSOCKETEVENT_DNSFAILED /* failed to lookup DNS information (recv_str contains error message) */
+};
+
+#define IOSOCKET_ADDR_IPV4 0x01
+#define IOSOCKET_ADDR_IPV6 0x02 /* overrides IOSOCKET_ADDR_IPV4 */
+#define IOSOCKET_PROTO_UDP 0x04
+
+#if !defined IOSOCKET_CPP
+struct IOSocket {
+       void *iosocket;
+       
+       enum IOSocketStatus status;
+       int listening : 1;
+       int ssl : 1;
+       int parse_delimiter : 1;
+       int parse_empty : 1; /* parse "empty" lines (only if parse_delimiter is set) */
+       unsigned char delimiters[IOSOCKET_PARSE_DELIMITERS_COUNT];
+       
+       void *data;
+       iosocket_callback *callback;
+};
+
+struct IOSocketEvent {
+       enum IOSocketEventType type;
+       struct IOSocket *socket;
+       union {
+               char *recv_str;
+               struct IOSocketBuffer *recv_buf;
+               int errid;
+               struct IOSocket *accept_socket;
+       } data;
+};
+
+struct IOSocket *iosocket_connect(const char *hostname, unsigned int port, int ssl, const char *bindhost, iosocket_callback *callback);
+struct IOSocket *iosocket_connect_flags(const char *hostname, unsigned int port, int ssl, const char *bindhost, iosocket_callback *callback, int flags);
+struct IOSocket *iosocket_listen(const char *hostname, unsigned int port, iosocket_callback *callback);
+struct IOSocket *iosocket_listen_flags(const char *hostname, unsigned int port, iosocket_callback *callback, int flags);
+struct IOSocket *iosocket_listen_ssl(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iosocket_callback *callback);
+struct IOSocket *iosocket_listen_ssl_flags(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iosocket_callback *callback, int flags);
+void iosocket_write(struct IOSocket *iosocket, const char *line);
+void iosocket_send(struct IOSocket *iosocket, const char *data, size_t datalen);
+void iosocket_printf(struct IOSocket *iosocket, const char *text, ...);
+void iosocket_close(struct IOSocket *iosocket);
+
+#endif
+#endif
diff --git a/src/IOHandler/IOTimer.c b/src/IOHandler/IOTimer.c
new file mode 100644 (file)
index 0000000..784ab76
--- /dev/null
@@ -0,0 +1,284 @@
+/* IOTimer.c - IOMultiplexer v2
+ * Copyright (C) 2014  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/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IOTimer.h"
+#include "IOLog.h"
+
+#include <sys/time.h>
+#include <stdlib.h>
+
+static void _rearrange_timer(struct _IOTimerDescriptor *timer);
+static void _autoreload_timer(struct _IOTimerDescriptor *timer);
+
+struct _IOTimerDescriptor *iotimer_sorted_descriptors;
+
+/* public functions */
+
+struct IOTimerDescriptor *iotimer_create(struct timeval *timeout) {
+       struct IOTimerDescriptor *descriptor = calloc(1, sizeof(*descriptor));
+       if(!descriptor) {
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
+               return NULL;
+       }
+       struct _IOTimerDescriptor *timer = _create_timer(timeout);
+       if(!timer) {
+               free(descriptor);
+               return NULL;
+       }
+       timer->parent = descriptor;
+       timer->flags |= IOTIMERFLAG_PARENT_PUBLIC;
+       descriptor->iotimer = timer;
+       
+       return descriptor;
+}
+
+void iotimer_start(struct IOTimerDescriptor *descriptor) {
+       struct _IOTimerDescriptor *timer = descriptor->iotimer;
+       if(timer == NULL) {
+               iolog_trigger(IOLOG_WARNING, "called iotimer_start for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
+               return;
+       }
+       timer->flags |= IOTIMERFLAG_ACTIVE;
+       _rearrange_timer(timer);
+}
+
+void iotimer_stop(struct IOTimerDescriptor *descriptor) {
+       struct _IOTimerDescriptor *timer = descriptor->iotimer;
+       if(timer == NULL) {
+               iolog_trigger(IOLOG_WARNING, "called iotimer_stop for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
+               return;
+       }
+       timer->flags &= ~IOTIMERFLAG_ACTIVE;
+       if((timer->flags & IOTIMERFLAG_PERSISTENT)) {
+               timer->flags &= ~IOTIMERFLAG_ACTIVE;
+               _rearrange_timer(timer);
+       } else
+               iotimer_destroy(descriptor);
+}
+
+int iotimer_state(struct IOTimerDescriptor *descriptor) {
+       struct _IOTimerDescriptor *timer = descriptor->iotimer;
+       if(timer == NULL) {
+               iolog_trigger(IOLOG_WARNING, "called iotimer_state for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
+               return 0;
+       }
+       if((timer->flags & IOTIMERFLAG_ACTIVE))
+               return 1;
+       else
+               return 0;
+}
+
+void iotimer_set_autoreload(struct IOTimerDescriptor *descriptor, struct timeval *autoreload) {
+       struct _IOTimerDescriptor *timer = descriptor->iotimer;
+       if(timer == NULL) {
+               iolog_trigger(IOLOG_WARNING, "called iotimer_set_autoreload for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
+               return;
+       }
+       if(autoreload) {
+               timer->flags |= IOTIMERFLAG_PERIODIC;
+               timer->autoreload = *autoreload;
+               
+               if(timer->timeout.tv_sec == 0 && timer->timeout.tv_usec == 0) {
+                       struct timeval now;
+                       gettimeofday(&now, NULL);
+                       timer->timeout = now;
+                       _autoreload_timer(timer);
+               }
+       } else {
+               timer->flags &= ~IOTIMERFLAG_PERIODIC;
+       }
+}
+
+struct timeval iotimer_get_autoreload(struct IOTimerDescriptor *descriptor) {
+       struct _IOTimerDescriptor *timer = descriptor->iotimer;
+       if(timer == NULL) {
+               iolog_trigger(IOLOG_WARNING, "called iotimer_get_autoreload for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
+               struct timeval tout;
+               tout.tv_sec = 0;
+               tout.tv_usec = 0;
+               return tout;
+       }
+       if((timer->flags & IOTIMERFLAG_PERIODIC))
+               return timer->autoreload;
+       else {
+               struct timeval tout;
+               tout.tv_sec = 0;
+               tout.tv_usec = 0;
+               return tout;
+       }
+}
+
+void iotimer_set_timeout(struct IOTimerDescriptor *descriptor, struct timeval *timeout) {
+       struct _IOTimerDescriptor *timer = descriptor->iotimer;
+       if(timer == NULL) {
+               iolog_trigger(IOLOG_WARNING, "called iotimer_set_timeout for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
+               return;
+       }
+       if(!timeout) {
+               iolog_trigger(IOLOG_WARNING, "called iotimer_set_timeout without timeout given in %s:%d", __FILE__, __LINE__);
+               return;
+       }
+       timer->timeout = *timeout;
+       _rearrange_timer(timer);
+}
+
+struct timeval iotimer_get_timeout(struct IOTimerDescriptor *descriptor) {
+       struct _IOTimerDescriptor *timer = descriptor->iotimer;
+       if(timer == NULL) {
+               iolog_trigger(IOLOG_WARNING, "called iotimer_get_timeout for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
+               struct timeval tout;
+               tout.tv_sec = 0;
+               tout.tv_usec = 0;
+               return tout;
+       }
+       return timer->timeout;
+}
+
+void iotimer_set_callback(struct IOTimerDescriptor *descriptor, iotimer_callback *callback) {
+       descriptor->callback = callback;
+}
+
+void iotimer_set_persistent(struct IOTimerDescriptor *descriptor, int persistent) {
+       struct _IOTimerDescriptor *timer = descriptor->iotimer;
+       if(timer == NULL) {
+               iolog_trigger(IOLOG_WARNING, "called iotimer_set_persistent for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
+               return;
+       }
+       if(persistent)
+               timer->flags |= IOTIMERFLAG_PERSISTENT;
+       else
+               timer->flags &= ~IOTIMERFLAG_PERSISTENT;
+}
+
+void iotimer_destroy(struct IOTimerDescriptor *descriptor) {
+       struct _IOTimerDescriptor *timer = descriptor->iotimer;
+       if(timer == NULL) {
+               iolog_trigger(IOLOG_WARNING, "called iotimer_destroy for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
+               return;
+       }
+       descriptor->iotimer = NULL;
+       _destroy_timer(timer);
+       
+       iogc_add(descriptor);
+}
+
+/* internal functions */
+void _init_timers() {
+       //nothing in here
+}
+
+struct _IOTimerDescriptor *_create_timer(struct timeval *timeout) {
+       struct _IOTimerDescriptor *timer = calloc(1, sizeof(*timer));
+       if(!timer) {
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for _IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
+               return NULL;
+       }
+       if(timeout)
+               timer->timeout = *timeout;
+       return timer;
+}
+
+static void _rearrange_timer(struct _IOTimerDescriptor *timer) {
+       if((timer->flags & IOTIMERFLAG_IN_LIST)) {
+               if(timer->prev == NULL)
+                       iotimer_sorted_descriptors = timer->next;
+               else
+                       timer->prev->next = timer->next;
+               if(timer->next != NULL)
+                       timer->next->prev = timer->prev;
+               timer->flags &= ~IOTIMERFLAG_IN_LIST;
+       }
+       if(!(timer->flags & IOTIMERFLAG_ACTIVE))
+               return;
+       struct _IOTimerDescriptor *ctimer;
+       for(ctimer = iotimer_sorted_descriptors; ctimer; ctimer = ctimer->next) {
+               if(timeval_is_bigger(ctimer->timeout, timer->timeout)) {
+                       timer->next = ctimer;
+                       timer->prev = ctimer->prev;
+                       if(ctimer->prev)
+                               ctimer->prev->next = timer;
+                       else
+                               iotimer_sorted_descriptors = timer;
+                       ctimer->prev = timer;
+                       break;
+               }
+               else if(ctimer->next == NULL) {
+                       ctimer->next = timer;
+                       timer->prev = ctimer;
+                       break;
+               }
+       }
+       if(ctimer == NULL)
+               iotimer_sorted_descriptors = timer;
+       timer->flags |= IOTIMERFLAG_IN_LIST;
+}
+
+void _destroy_timer(struct _IOTimerDescriptor *timer) {
+       if((timer->flags & IOTIMERFLAG_IN_LIST)) {
+               if(timer->prev == NULL)
+                       iotimer_sorted_descriptors = timer->next;
+               else
+                       timer->prev->next = timer->next;
+               if(timer->next != NULL)
+                       timer->next->prev = timer->prev;
+       }
+       free(timer);
+}
+
+static void _autoreload_timer(struct _IOTimerDescriptor *timer) {
+       timer->timeout.tv_usec += timer->autoreload.tv_usec;
+       timer->timeout.tv_sec += timer->autoreload.tv_sec;
+       if(timer->timeout.tv_usec > 1000000) {
+               timer->timeout.tv_sec += (timer->timeout.tv_usec / 1000000);
+               timer->timeout.tv_usec %= 1000000;
+       }
+       _rearrange_timer(timer);
+}
+
+void _trigger_timer() {
+       struct timeval now;
+       struct _IOTimerDescriptor *timer;
+       while(iotimer_sorted_descriptors) {
+               gettimeofday(&now, NULL);
+               
+               timer = iotimer_sorted_descriptors;
+               if(timeval_is_bigger(timer->timeout, now))
+                       break;
+               
+               iotimer_sorted_descriptors = timer->next;
+               if(timer->next != NULL)
+                       timer->next->prev = timer->prev;
+               timer->flags &= ~IOTIMERFLAG_IN_LIST;
+               
+               if((timer->flags & IOTIMERFLAG_PERIODIC))
+                       _autoreload_timer(timer);
+               
+               if(timer->flags & IOTIMERFLAG_PARENT_PUBLIC) {
+                       struct IOTimerDescriptor *descriptor = timer->parent;
+                       if(descriptor->callback)
+                               descriptor->callback(descriptor);
+                       if(!(timer->flags & IOTIMERFLAG_PERIODIC))
+                               iotimer_destroy(descriptor);
+               } else {
+                       if(!(timer->flags & IOTIMERFLAG_PERIODIC))
+                               _destroy_timer(timer);
+               }
+       }
+}
+
diff --git a/src/IOHandler/IOTimer.h b/src/IOHandler/IOTimer.h
new file mode 100644 (file)
index 0000000..e67ee6f
--- /dev/null
@@ -0,0 +1,77 @@
+/* IOTimer.h - IOMultiplexer v2
+ * Copyright (C) 2014  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 _IOTimer_h
+#define _IOTimer_h
+#ifndef _IOHandler_internals
+#include "IOHandler.h"
+#else
+#include <sys/time.h>
+
+
+#define IOTIMERFLAG_PERIODIC       0x01
+#define IOTIMERFLAG_ACTIVE         0x02
+#define IOTIMERFLAG_IN_LIST        0x04
+#define IOTIMERFLAG_PARENT_PUBLIC  0x08
+#define IOTIMERFLAG_PARENT_SOCKET  0x10
+#define IOTIMERFLAG_PERSISTENT     0x20
+
+struct _IOTimerDescriptor;
+
+extern struct _IOTimerDescriptor *iotimer_sorted_descriptors;
+
+struct _IOTimerDescriptor {
+       unsigned int flags : 8;
+       void *parent; 
+       
+       struct timeval timeout;
+       struct timeval autoreload;
+       
+       struct _IOTimerDescriptor *prev, *next;
+};
+
+void _init_timers();
+struct _IOTimerDescriptor *_create_timer(struct timeval *timeout);
+void _destroy_timer(struct _IOTimerDescriptor *timer);
+void _trigger_timer();
+
+#endif
+
+struct IOTimerDescriptor;
+
+#define IOTIMER_CALLBACK(NAME) void NAME(struct IOTimerDescriptor *iotimer)
+typedef IOTIMER_CALLBACK(iotimer_callback);
+
+struct IOTimerDescriptor {
+       void *iotimer; /* struct _IOTimerDescriptor */
+       
+       iotimer_callback *callback;
+       void *data;
+};
+
+struct IOTimerDescriptor *iotimer_create(struct timeval *timeout);
+void iotimer_start(struct IOTimerDescriptor *iotimer);
+void iotimer_stop(struct IOTimerDescriptor *iotimer);
+int iotimer_state(struct IOTimerDescriptor *iotimer);
+void iotimer_set_autoreload(struct IOTimerDescriptor *iotimer, struct timeval *autoreload);
+struct timeval iotimer_get_autoreload(struct IOTimerDescriptor *iotimer);
+void iotimer_set_timeout(struct IOTimerDescriptor *iotimer, struct timeval *timeout);
+struct timeval iotimer_get_timeout(struct IOTimerDescriptor *iotimer);
+void iotimer_set_callback(struct IOTimerDescriptor *iotimer, iotimer_callback *callback);
+void iotimer_set_persistent(struct IOTimerDescriptor *iotimer, int persistent);
+void iotimer_destroy(struct IOTimerDescriptor *iotimer);
+
+#endif
diff --git a/src/IOHandler/Makefile.am b/src/IOHandler/Makefile.am
new file mode 100644 (file)
index 0000000..14edac2
--- /dev/null
@@ -0,0 +1,20 @@
+##Process this file with automake to create Makefile.in
+ACLOCAL_AMFLAGS = -I m4
+
+libiohandler_la_SOURCES = compat/utime.c \
+    compat/inet.c \
+    IOHandler.c \
+    IODNSEngine_cares.c \
+    IODNSEngine_default.c \
+    IODNSLookup.c \
+    IOEngine_epoll.c \
+    IOEngine_kevent.c \
+    IOEngine_select.c \
+    IOEngine_win32.c \
+    IOGarbageCollector.c \
+    IOLog.c \
+    IOSockets.c \
+    IOSSLBackend.c \
+    IOTimer.c
+
+noinst_LTLIBRARIES = libiohandler.la
diff --git a/src/IOHandler/compat/inet.c b/src/IOHandler/compat/inet.c
new file mode 100644 (file)
index 0000000..aa7245f
--- /dev/null
@@ -0,0 +1,86 @@
+/* inet.c - IOMultiplexer
+ * Copyright (C) 2014  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_config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+/* non-win32 systems have these functions defined... */
+#endif
+
+#ifndef HAVE_INET_PTON
+#ifdef WIN32
+int inet_pton(int af, const char *src, void *dst) {
+       struct sockaddr_storage ss;
+       int size = sizeof(ss);
+       char src_copy[INET6_ADDRSTRLEN+1];
+
+       memset(&ss, 0, sizeof(ss));
+       /* stupid non-const API */
+       strncpy (src_copy, src, INET6_ADDRSTRLEN+1);
+       src_copy[INET6_ADDRSTRLEN] = 0;
+
+       if(WSAStringToAddress(src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0) {
+               switch(af) {
+               case AF_INET:
+                       *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr;
+                       return 1;
+               case AF_INET6:
+                       *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr;
+                       return 1;
+               }
+       }
+       return 0;
+}
+#else
+
+
+#endif
+#endif
+
+#ifndef HAVE_INET_NTOP
+#ifdef WIN32
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) {
+       struct sockaddr_storage ss;
+       unsigned long s = size;
+
+       memset(&ss, 0, sizeof(ss));
+       ss.ss_family = af;
+
+       switch(af) {
+       case AF_INET:
+               ((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src;
+               break;
+       case AF_INET6:
+               ((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src;
+               break;
+       default:
+               return NULL;
+       }
+       /* cannot direclty use &size because of strict aliasing rules */
+       return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0) ? dst : NULL;
+}
+#else
+
+
+#endif
+#endif
diff --git a/src/IOHandler/compat/inet.h b/src/IOHandler/compat/inet.h
new file mode 100644 (file)
index 0000000..a61424c
--- /dev/null
@@ -0,0 +1,37 @@
+/* inet.h - IOMultiplexer
+ * Copyright (C) 2014  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 _compat_inet_h
+#define _compat_inet_h
+
+#ifndef HAVE_INET_PTON
+#ifdef WIN32
+int inet_pton(int af, const char *src, void *dst);
+#else
+
+#endif
+#endif
+
+#ifndef HAVE_INET_NTOP
+#ifdef WIN32
+#include <ws2tcpip.h>
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
+#else
+
+#endif
+#endif
+
+#endif
diff --git a/src/IOHandler/compat/utime.c b/src/IOHandler/compat/utime.c
new file mode 100644 (file)
index 0000000..193ab53
--- /dev/null
@@ -0,0 +1,84 @@
+/* utime.c - IOMultiplexer
+ * Copyright (C) 2014  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_config.h"
+#include "utime.h"
+#ifndef HAVE_USLEEP
+
+#ifdef HAVE_SELECT
+
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#else
+# include <sys/time.h>
+# include <sys/types.h>
+# include <unistd.h>
+#endif
+
+void usleep_tv(struct timeval tv) {
+       select(0, NULL, NULL, NULL, &tv);
+}
+
+void usleep(long usec)
+{
+       struct timeval tv;
+
+       tv.tv_sec = usec / 1000000;
+       tv.tv_usec = usec % 1000000;
+       usleep_tv(tv);
+}
+
+#elif defined WIN32
+
+/* usleep implementation from FreeSCI */
+
+#include <windows.h>
+
+void usleep_tv(struct timeval tv) {
+       usleep(tv.tv_sec * 1000000 + tv.tv_usec);
+}
+
+void usleep (long usec)
+{
+        LARGE_INTEGER lFrequency;
+        LARGE_INTEGER lEndTime;
+        LARGE_INTEGER lCurTime;
+
+        QueryPerformanceFrequency (&lFrequency);
+        if (lFrequency.QuadPart) {
+                QueryPerformanceCounter (&lEndTime);
+                lEndTime.QuadPart += (LONGLONG) usec *
+                                        lFrequency.QuadPart / 1000000;
+                do {
+                        QueryPerformanceCounter (&lCurTime);
+                        Sleep(0);
+                } while (lCurTime.QuadPart < lEndTime.QuadPart);
+        }
+}
+
+#endif
+
+#else
+#include <sys/time.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+void usleep_tv(struct timeval tv) {
+       usleep(tv.tv_sec * 1000000 + tv.tv_usec);
+}
+
+#endif /* !HAVE_USLEEP */
diff --git a/src/IOHandler/compat/utime.h b/src/IOHandler/compat/utime.h
new file mode 100644 (file)
index 0000000..4156880
--- /dev/null
@@ -0,0 +1,30 @@
+/* utime.h - IOMultiplexer
+ * Copyright (C) 2014  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 _compat_utime_h
+#define _compat_utime_h
+#include "../IOHandler_config.h"
+#include <sys/time.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+void usleep_tv(struct timeval tv);
+
+#ifndef HAVE_USLEEP
+void usleep(long usec);
+#endif
+#endif
diff --git a/src/IOHandler_test/Makefile.am b/src/IOHandler_test/Makefile.am
new file mode 100644 (file)
index 0000000..d8ca43d
--- /dev/null
@@ -0,0 +1,3 @@
+##Process this file with automake to create Makefile.in
+ACLOCAL_AMFLAGS = -I m4
+SUBDIRS = client client++ client_ssl server_ssl timer timer++ resolv
diff --git a/src/IOHandler_test/client++/.gitignore b/src/IOHandler_test/client++/.gitignore
new file mode 100644 (file)
index 0000000..7af2f69
--- /dev/null
@@ -0,0 +1,7 @@
+.deps
+.libs
+*.o
+*.exe
+iotest
+Makefile
+Makefile.in
diff --git a/src/IOHandler_test/client++/Makefile.am b/src/IOHandler_test/client++/Makefile.am
new file mode 100644 (file)
index 0000000..0591fea
--- /dev/null
@@ -0,0 +1,7 @@
+##Process this file with automake to create Makefile.in
+ACLOCAL_AMFLAGS = -I m4
+
+noinst_PROGRAMS = iotest
+
+iotest_LDADD = ../../IOHandler++/libiohandler.cpp.la
+iotest_SOURCES = iotest.cpp
diff --git a/src/IOHandler_test/client++/iotest.cpp b/src/IOHandler_test/client++/iotest.cpp
new file mode 100644 (file)
index 0000000..151ce0d
--- /dev/null
@@ -0,0 +1,57 @@
+/* main.c - IOMultiplexer
+ * Copyright (C) 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 <stdio.h>
+#include "../../IOHandler++/IOHandler.h"
+#include "../../IOHandler++/IOSocket.h"
+
+class IOTestSocket : public CIOSocket {
+protected:
+       virtual void connectedEvent() {
+               printf("[connect]\n");
+               this->writef("GET / HTTP/1.1\r\n");
+               this->writef("Host: test.pk910.de\r\n");
+               this->writef("\r\n");
+       };
+       virtual void notConnectedEvent(int errid) {
+               printf("[not connected]\n");
+       };
+       virtual void closedEvent(int errid) {
+               printf("[disconnect]\n");
+       };
+       virtual void acceptedEvent(CIOSocket *client) {
+               client->close(); 
+       };
+       virtual void dnsErrEvent(std::string *errormsg) {
+       
+       };
+       virtual int recvEvent(const char *data, int len) {
+               int i;
+               for(i = 0; i < len; i++)
+                       putchar(data[i]);
+               return len;
+       };
+};
+
+
+int main(int argc, char *argv[]) {
+       CIOHandler *iohandler = new CIOHandler();
+       IOTestSocket *sock = new IOTestSocket();
+       sock->connect("test.pk910.de", 443, 1, NULL);
+       
+       iohandler->start();
+}
diff --git a/src/IOHandler_test/client/.gitignore b/src/IOHandler_test/client/.gitignore
new file mode 100644 (file)
index 0000000..7af2f69
--- /dev/null
@@ -0,0 +1,7 @@
+.deps
+.libs
+*.o
+*.exe
+iotest
+Makefile
+Makefile.in
diff --git a/src/IOHandler_test/client/Makefile.am b/src/IOHandler_test/client/Makefile.am
new file mode 100644 (file)
index 0000000..feda450
--- /dev/null
@@ -0,0 +1,8 @@
+##Process this file with automake to create Makefile.in
+ACLOCAL_AMFLAGS = -I m4
+
+noinst_PROGRAMS = iotest
+iotest_LDADD = ../../IOHandler/libiohandler.la
+
+iotest_SOURCES = iotest.c
+
diff --git a/src/IOHandler_test/client/iotest.c b/src/IOHandler_test/client/iotest.c
new file mode 100644 (file)
index 0000000..2826c4e
--- /dev/null
@@ -0,0 +1,69 @@
+/* main.c - IOMultiplexer
+ * Copyright (C) 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 <stdio.h>
+#include "../../IOHandler/IOHandler.h"
+#include "../../IOHandler/IOSockets.h"
+#include "../../IOHandler/IOLog.h"
+
+static IOSOCKET_CALLBACK(io_callback);
+static IOLOG_CALLBACK(io_log);
+
+static struct IOSocket *irc_iofd = NULL;
+
+int main(int argc, char *argv[]) {
+       iohandler_init();
+       
+    iolog_register_callback(io_log);
+    
+    irc_iofd = iosocket_connect("test.pk910.de", 80, 0, NULL, io_callback);
+       
+       iohandler_run();
+       
+       return 0;
+}
+
+static IOSOCKET_CALLBACK(io_callback) {
+    switch(event->type) {
+        case IOSOCKETEVENT_CONNECTED:
+            printf("[connect]\n");
+            iosocket_printf(event->socket, "GET / HTTP/1.1\r\n");
+            iosocket_printf(event->socket, "Host: test.pk910.de\r\n");
+            iosocket_printf(event->socket, "\r\n");
+            break;
+        case IOSOCKETEVENT_CLOSED:
+            printf("[disconnect]\n");
+            break;
+        case IOSOCKETEVENT_RECV:
+                       {
+                               struct IOSocketBuffer *recv_buf = event->data.recv_buf;
+                               int i;
+                               for(i = 0; i < recv_buf->bufpos; i++)
+                                       putchar(recv_buf->buffer[i]);
+                               recv_buf->bufpos = 0;
+                               printf("\n");
+            }
+            break;
+        
+        default:
+            break;
+    }
+}
+
+static IOLOG_CALLBACK(io_log) {
+       printf("%s", message);
+}
diff --git a/src/IOHandler_test/client_ssl/.gitignore b/src/IOHandler_test/client_ssl/.gitignore
new file mode 100644 (file)
index 0000000..7af2f69
--- /dev/null
@@ -0,0 +1,7 @@
+.deps
+.libs
+*.o
+*.exe
+iotest
+Makefile
+Makefile.in
diff --git a/src/IOHandler_test/client_ssl/Makefile.am b/src/IOHandler_test/client_ssl/Makefile.am
new file mode 100644 (file)
index 0000000..feda450
--- /dev/null
@@ -0,0 +1,8 @@
+##Process this file with automake to create Makefile.in
+ACLOCAL_AMFLAGS = -I m4
+
+noinst_PROGRAMS = iotest
+iotest_LDADD = ../../IOHandler/libiohandler.la
+
+iotest_SOURCES = iotest.c
+
diff --git a/src/IOHandler_test/client_ssl/iotest.c b/src/IOHandler_test/client_ssl/iotest.c
new file mode 100644 (file)
index 0000000..867ca44
--- /dev/null
@@ -0,0 +1,69 @@
+/* main.c - IOMultiplexer
+ * Copyright (C) 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 <stdio.h>
+#include "../../IOHandler/IOHandler.h"
+#include "../../IOHandler/IOSockets.h"
+#include "../../IOHandler/IOLog.h"
+
+static IOSOCKET_CALLBACK(io_callback);
+static IOLOG_CALLBACK(io_log);
+
+static struct IOSocket *irc_iofd = NULL;
+
+int main(int argc, char *argv[]) {
+       iohandler_init();
+       
+    iolog_register_callback(io_log);
+    
+    irc_iofd = iosocket_connect("test.pk910.de", 443, 1, NULL, io_callback);
+       
+       iohandler_run();
+       
+       return 0;
+}
+
+static IOSOCKET_CALLBACK(io_callback) {
+    switch(event->type) {
+        case IOSOCKETEVENT_CONNECTED:
+            printf("[connect]\n");
+            iosocket_printf(event->socket, "GET / HTTP/1.1\r\n");
+            iosocket_printf(event->socket, "Host: test.pk910.de\r\n");
+            iosocket_printf(event->socket, "\r\n");
+            break;
+        case IOSOCKETEVENT_CLOSED:
+            printf("[disconnect]\n");
+            break;
+        case IOSOCKETEVENT_RECV:
+                       {
+                               struct IOSocketBuffer *recv_buf = event->data.recv_buf;
+                               int i;
+                               for(i = 0; i < recv_buf->bufpos; i++)
+                                       putchar(recv_buf->buffer[i]);
+                               recv_buf->bufpos = 0;
+                               printf("\n");
+            }
+            break;
+        
+        default:
+            break;
+    }
+}
+
+static IOLOG_CALLBACK(io_log) {
+       printf("%s", message);
+}
diff --git a/src/IOHandler_test/resolv/.gitignore b/src/IOHandler_test/resolv/.gitignore
new file mode 100644 (file)
index 0000000..7af2f69
--- /dev/null
@@ -0,0 +1,7 @@
+.deps
+.libs
+*.o
+*.exe
+iotest
+Makefile
+Makefile.in
diff --git a/src/IOHandler_test/resolv/Makefile.am b/src/IOHandler_test/resolv/Makefile.am
new file mode 100644 (file)
index 0000000..feda450
--- /dev/null
@@ -0,0 +1,8 @@
+##Process this file with automake to create Makefile.in
+ACLOCAL_AMFLAGS = -I m4
+
+noinst_PROGRAMS = iotest
+iotest_LDADD = ../../IOHandler/libiohandler.la
+
+iotest_SOURCES = iotest.c
+
diff --git a/src/IOHandler_test/resolv/iotest.c b/src/IOHandler_test/resolv/iotest.c
new file mode 100644 (file)
index 0000000..c2d4417
--- /dev/null
@@ -0,0 +1,88 @@
+/* main.c - IOMultiplexer
+ * Copyright (C) 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 <stdio.h>
+#include <string.h>
+#include "../../IOHandler/IOHandler.h"
+#include "../../IOHandler/IODNSLookup.h"
+#include "../../IOHandler/IOLog.h"
+
+#ifndef WIN32
+#include "arpa/inet.h"
+#endif
+#include "../../IOHandler/compat/inet.h"
+
+#define TEST_DURATION 100
+
+static IODNS_CALLBACK(io_callback);
+static IOLOG_CALLBACK(io_log);
+
+int main(int argc, char *argv[]) {
+       iohandler_init();
+       iolog_register_callback(io_log);
+       
+       iodns_getaddrinfo("google.de", (IODNS_RECORD_A | IODNS_RECORD_AAAA), io_callback, "01");
+       iodns_getaddrinfo("pk910.de", IODNS_RECORD_AAAA, io_callback, "02");
+       iodns_getaddrinfo("nonexisting.no-tld", (IODNS_RECORD_A | IODNS_RECORD_AAAA), io_callback, "03");
+       iodns_getaddrinfo("google.com", (IODNS_RECORD_A | IODNS_RECORD_AAAA), io_callback, "04");
+       iodns_getaddrinfo("test.pk910.de", IODNS_RECORD_A, io_callback, "05");
+       
+       struct sockaddr_in addr;
+       addr.sin_family = AF_INET;
+       inet_pton(AF_INET, "8.8.8.8", &addr.sin_addr);
+       iodns_getnameinfo((struct sockaddr *)&addr, sizeof(addr), io_callback, "06");
+       
+       iohandler_run();
+       return 1;
+}
+
+static IODNS_CALLBACK(io_callback) {
+       struct IODNSQuery *iodns = event->query;
+       char *id = iodns->data;
+       if(event->type == IODNSEVENT_SUCCESS) {
+               printf("Query %s succeeded:\n", id);
+               struct IODNSResult *result;
+               char str[1024];
+               for(result = event->result; result; result = result->next) {
+                       switch(result->type) {
+                       case IODNS_RECORD_A:
+                               inet_ntop(AF_INET, &((struct sockaddr_in *)result->result.addr.address)->sin_addr, str, INET_ADDRSTRLEN);
+                               printf("  A: %s\n", str);
+                               
+                               if(!strcmp(id, "05"))
+                                       iodns_getnameinfo(result->result.addr.address, result->result.addr.addresslen, io_callback, "07");
+                               break;
+                       case IODNS_RECORD_AAAA:
+                               inet_ntop(AF_INET6, &((struct sockaddr_in6 *)result->result.addr.address)->sin6_addr, str, INET6_ADDRSTRLEN);
+                               printf("  AAAA: %s\n", str);
+                               
+                               if(!strcmp(id, "05"))
+                                       iodns_getnameinfo(result->result.addr.address, result->result.addr.addresslen, io_callback, "07");
+                               break;
+                       case IODNS_RECORD_PTR:
+                               printf("  PTR: %s\n", result->result.host);
+                               break;
+                       }
+               }
+               iodns_free_result(event->result);
+       } else
+               printf("Query %s failed.\n", id);
+}
+
+static IOLOG_CALLBACK(io_log) {
+       //printf("%s", line);
+}
diff --git a/src/IOHandler_test/server_ssl/.gitignore b/src/IOHandler_test/server_ssl/.gitignore
new file mode 100644 (file)
index 0000000..7af2f69
--- /dev/null
@@ -0,0 +1,7 @@
+.deps
+.libs
+*.o
+*.exe
+iotest
+Makefile
+Makefile.in
diff --git a/src/IOHandler_test/server_ssl/Makefile.am b/src/IOHandler_test/server_ssl/Makefile.am
new file mode 100644 (file)
index 0000000..feda450
--- /dev/null
@@ -0,0 +1,8 @@
+##Process this file with automake to create Makefile.in
+ACLOCAL_AMFLAGS = -I m4
+
+noinst_PROGRAMS = iotest
+iotest_LDADD = ../../IOHandler/libiohandler.la
+
+iotest_SOURCES = iotest.c
+
diff --git a/src/IOHandler_test/server_ssl/cert.pem b/src/IOHandler_test/server_ssl/cert.pem
new file mode 100644 (file)
index 0000000..1880073
--- /dev/null
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDRTCCAi2gAwIBAgIJAI5TbbwUS+BqMA0GCSqGSIb3DQEBBQUAMDkxCzAJBgNV
+BAYTAkRFMRAwDgYDVQQIEwdHZXJtYW55MRgwFgYDVQQKEw9wazkxMCB0ZXN0IGNv
+ZGUwHhcNMTQwMzE2MTUyNzI3WhcNMjQwMzEzMTUyNzI3WjA5MQswCQYDVQQGEwJE
+RTEQMA4GA1UECBMHR2VybWFueTEYMBYGA1UEChMPcGs5MTAgdGVzdCBjb2RlMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm2OrxJhpfcJKkXDWeCSdF5KS
+zOeCNg/kehycUjhvVhwmzTC72Jk4OlRxadANhKaMooyfkqqymwhZ8+7qCL76N6e3
+Yefr0EThaG09UerwFrnGQtwoM/F2F7CnZh8+FonvsI2/4BBWCpmWisixF7uCj+e9
+rQlbsmtzQVg7Vbvv99FdBehiWEb2HoxXnC3ve2+fy7tBRvQ19qBXsTGnjZbmv6MU
++ige+9Iv/4P8ojag6Njr1cuLTbUgL5s1VVSTbSS+7hW+xmG6S5IuPz3y1IXIRsmy
+sf2+vS2CcwxkwNxSvwrFqsJYsEq/N+sQ8rV9Onlna9k4vDYjMPzBwW/xSkph8wID
+AQABo1AwTjAdBgNVHQ4EFgQUhWrijLfZ4XTPHczZ2PveFqiUp3QwHwYDVR0jBBgw
+FoAUhWrijLfZ4XTPHczZ2PveFqiUp3QwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B
+AQUFAAOCAQEAINpTUpUOH/AMEEDAUa4mZ8tRK+30PT3WEc+pcLSmaD5BsdbOJ4yL
+mNmdzmfu1yek42GNe5ASmIZH9DK8WqT9CDJxWi1PzUpYhGmm3el683biK+/CHOJf
+iYry7VSvYJJkpzCNJPpL6gr0PCTlBRJrgTs4qAliJvXJ394YPd/EtUN3VaqC+4WY
+FWlZaVhf+GV3U38F16mmX5K3vNAZEEKLWQhApOjvvAeJRVAcONI025KUp19IZjw3
+sXKMN1PuCvuWz8Wlff4xgO3VgnshXn51KhGfasK4KVD6yWFfG6noBZ9lH5EuaqJr
+VjrOdMP0cUP2vKv3bswfV4rVy1fJz3g+Sw==
+-----END CERTIFICATE-----
diff --git a/src/IOHandler_test/server_ssl/iotest.c b/src/IOHandler_test/server_ssl/iotest.c
new file mode 100644 (file)
index 0000000..6407216
--- /dev/null
@@ -0,0 +1,84 @@
+/* main.c - IOMultiplexer
+ * Copyright (C) 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 <stdio.h>
+#include <string.h>
+#include "../../IOHandler/IOHandler.h"
+#include "../../IOHandler/IOSockets.h"
+#include "../../IOHandler/IOLog.h"
+
+#define CERTFILE "cert.pem"
+#define KEYFILE "key.pem"
+
+static IOSOCKET_CALLBACK(io_callback);
+static IOLOG_CALLBACK(io_log);
+
+static struct IOSocket *irc_iofd = NULL;
+
+int main(int argc, char *argv[]) {
+       iohandler_init();
+       
+    iolog_register_callback(io_log);
+    
+    irc_iofd = iosocket_listen_ssl("0.0.0.0", 12345, CERTFILE, KEYFILE, io_callback);
+       
+       iohandler_run();
+       
+       return 0;
+}
+
+static IOSOCKET_CALLBACK(io_callback) {
+    switch(event->type) {
+        case IOSOCKETEVENT_ACCEPT:
+            printf("[client accepted]\n");
+                       struct IOSocket *client = event->data.accept_socket;
+                       client->callback = io_callback;
+                       
+                       char *html = "<html><head><title>Test Page</title></head><body><h1>IOHandler SSL Test</h1></body></html>";
+            iosocket_printf(client, "HTTP/1.1 200 OK\r\n");
+                       iosocket_printf(client, "Server: Apache\r\n");
+                       iosocket_printf(client, "Content-Length: %d\r\n", strlen(html));
+                       iosocket_printf(client, "Content-Type: text/html\r\n");
+                       iosocket_printf(client, "\r\n");
+                       iosocket_printf(client, "%s", html);
+                       
+            break;
+        case IOSOCKETEVENT_CLOSED:
+                       if(event->socket->listening)
+                               printf("[server closed]\n");
+                       else
+                               printf("[client disconnect]\n");
+            break;
+        case IOSOCKETEVENT_RECV:
+                       {
+                               struct IOSocketBuffer *recv_buf = event->data.recv_buf;
+                               int i;
+                               for(i = 0; i < recv_buf->bufpos; i++)
+                                       putchar(recv_buf->buffer[i]);
+                               recv_buf->bufpos = 0;
+                               printf("\n");
+            }
+            break;
+        
+        default:
+            break;
+    }
+}
+
+static IOLOG_CALLBACK(io_log) {
+       printf("%s", message);
+}
diff --git a/src/IOHandler_test/server_ssl/key.pem b/src/IOHandler_test/server_ssl/key.pem
new file mode 100644 (file)
index 0000000..59572e4
--- /dev/null
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCbY6vEmGl9wkqR
+cNZ4JJ0XkpLM54I2D+R6HJxSOG9WHCbNMLvYmTg6VHFp0A2EpoyijJ+SqrKbCFnz
+7uoIvvo3p7dh5+vQROFobT1R6vAWucZC3Cgz8XYXsKdmHz4Wie+wjb/gEFYKmZaK
+yLEXu4KP572tCVuya3NBWDtVu+/30V0F6GJYRvYejFecLe97b5/Lu0FG9DX2oFex
+MaeNlua/oxT6KB770i//g/yiNqDo2OvVy4tNtSAvmzVVVJNtJL7uFb7GYbpLki4/
+PfLUhchGybKx/b69LYJzDGTA3FK/CsWqwliwSr836xDytX06eWdr2Ti8NiMw/MHB
+b/FKSmHzAgMBAAECggEAHHHxdqqj3QadGeS7DgE91JvbTbEvj+/21je4kgCMuQms
+PLGoIW1i8qKUpFcWsmq+od72MyYWTfUIanQY2YMEUP3dvwlyjIyfartcl0tXqgFV
+/tVIcsHH6WxIKJSdjAiyHPLF5iF3brdQ7JTyfjKwIuG8QhnNdGrhDVw2eGpP8mBT
+DRL8qX1KrAvZLGzDq19kX4o49oPfq+eXXZVB+xgAmyT+YtD4tVi0fvDW5J6nET3s
+/qxvx5No35HD3f1V9Xjth4DRIr2eUDqTVVqZ2sTMGF4kbDcdxZl4iOORJO2VhcQC
+UUXxt038NkHAom0vT6GFJQbPLb6b8obBxz2KyYp7AQKBgQDJ3O9kEOGtaymojkq1
+y1MEyYLyk0FCdjCTMcbK8A3455ZyhQiNKNiDcZy9WUaZBppDpSJ1RjCaUL+4QX51
+BbK18W1YO2INR/DMhyZEmLnPbqubqoVLIGz/LBW44BPwz8jjvZT13yiFL0Afa4xi
+vRvdYAju+f28rmpswtvG3Im3gQKBgQDFEAxaY0PSTnwzPdDJZqQyYvpkqj/eJXxP
+FrH+LHC3Ug0SRrQ/C223kpc1m9OivaCz/7jCozjNws4Dus6hRzn6d6r4ChjatBqC
+Y97EE5SvvAJrDaA6J0GQ8paaoDe9TJ+/4qFiG4FdYwmrCuNU5cdypbWGaaXlTTBG
+OmfSyf5zcwKBgBwWeNzkmHJH6fkBK5YWH4wX+feE09zKk0G3+GA+fMM4fi+bITB7
+EX1grp2OMYuTZp2o+Z110cd3GuYpfs4Lp+03Fa7kPGV6sB/VYlbDJX+ed5Rmaruk
+XGY5HSCnVT75uost0u9PSNUXWQXGMjd+9sSb20JdWJgLcNWHW4tVHniBAoGBAJh0
+l/n9veSgT7oc4sBNpk5NAMaMaCjm+0r8leu8Wd+ZnP88sAnuP4273TveOFc5OXDI
+MBp0yGd3hIaiKWXggtxhZGXM+fmJSNEDjr5HH8rtOzmzKviSkkkfRKCPv0+2TgjX
+vl72RJkJG9u16rYtpqXtyYgZh/zkQKq0WMpc93w9AoGALWfwrj5eSDi5EDWbjgBF
+zm87FoDcIvMDpTsw4xA/o9JpDLLVdsJYf/uwLPDUkMHWy0w2dioQu+VoLu480l1K
+27a3jvIIWClV3xVulm9abLY//79eARDG+bwXC30e7z6wduw8LtJsOfWD9nFd9ggt
+rT1g4c/bHYflEb2MieXZ4UQ=
+-----END PRIVATE KEY-----
diff --git a/src/IOHandler_test/timer++/.gitignore b/src/IOHandler_test/timer++/.gitignore
new file mode 100644 (file)
index 0000000..7af2f69
--- /dev/null
@@ -0,0 +1,7 @@
+.deps
+.libs
+*.o
+*.exe
+iotest
+Makefile
+Makefile.in
diff --git a/src/IOHandler_test/timer++/Makefile.am b/src/IOHandler_test/timer++/Makefile.am
new file mode 100644 (file)
index 0000000..0591fea
--- /dev/null
@@ -0,0 +1,7 @@
+##Process this file with automake to create Makefile.in
+ACLOCAL_AMFLAGS = -I m4
+
+noinst_PROGRAMS = iotest
+
+iotest_LDADD = ../../IOHandler++/libiohandler.cpp.la
+iotest_SOURCES = iotest.cpp
diff --git a/src/IOHandler_test/timer++/iotest.cpp b/src/IOHandler_test/timer++/iotest.cpp
new file mode 100644 (file)
index 0000000..6c6fd9b
--- /dev/null
@@ -0,0 +1,66 @@
+/* main.c - IOMultiplexer
+ * Copyright (C) 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 <stdio.h>
+#include "../../IOHandler++/IOHandler.h"
+#include "../../IOHandler++/IOTimer.h"
+
+class IOTestTimer : public CIOTimer {
+public:
+       IOTestTimer(int test_duration) : CIOTimer() {
+               this->tick_count = 0;
+               this->test_duration = test_duration;
+               gettimeofday(&this->test_clock1, NULL);
+               gettimeofday(&this->test_clock2, NULL);
+               
+               this->setRelativeTimeoutSeconds(this->test_duration / 1000.0, 1);
+               this->setActive(1);
+               
+               printf("[timer 0] %ld.%ld\n", test_clock1.tv_sec, test_clock1.tv_usec);
+       };
+       
+protected:
+       virtual void timeout() {
+               this->tick_count++;
+               this->tick();
+       };
+       
+private:
+       timeval test_clock1, test_clock2;
+       int tick_count, test_duration;
+       
+       void tick() {
+               timeval curr_time;
+               int diff1;
+               double diff2;
+               
+               gettimeofday(&curr_time, NULL);
+               diff1 = (curr_time.tv_sec - this->test_clock1.tv_sec) * 1000 + ((curr_time.tv_usec - this->test_clock1.tv_usec) / 1000);
+               diff2 = (curr_time.tv_sec - this->test_clock2.tv_sec) * 1000 + ((curr_time.tv_usec - this->test_clock2.tv_usec) / 1000.0);
+               diff2 -= (this->tick_count * this->test_duration);
+               gettimeofday(&this->test_clock1, NULL);
+               printf("[timer %03d] %ld.%06ld [%d ms]  accuracy: %f ms\n", this->tick_count, curr_time.tv_sec, curr_time.tv_usec, diff1, diff2);
+       };
+};
+
+
+int main(int argc, char *argv[]) {
+       CIOHandler *iohandler = new CIOHandler();
+       IOTestTimer *timer = new IOTestTimer(100);
+       
+       iohandler->start();
+}
diff --git a/src/IOHandler_test/timer/.gitignore b/src/IOHandler_test/timer/.gitignore
new file mode 100644 (file)
index 0000000..7af2f69
--- /dev/null
@@ -0,0 +1,7 @@
+.deps
+.libs
+*.o
+*.exe
+iotest
+Makefile
+Makefile.in
diff --git a/src/IOHandler_test/timer/Makefile.am b/src/IOHandler_test/timer/Makefile.am
new file mode 100644 (file)
index 0000000..feda450
--- /dev/null
@@ -0,0 +1,8 @@
+##Process this file with automake to create Makefile.in
+ACLOCAL_AMFLAGS = -I m4
+
+noinst_PROGRAMS = iotest
+iotest_LDADD = ../../IOHandler/libiohandler.la
+
+iotest_SOURCES = iotest.c
+
diff --git a/src/IOHandler_test/timer/iotest.c b/src/IOHandler_test/timer/iotest.c
new file mode 100644 (file)
index 0000000..97d43c6
--- /dev/null
@@ -0,0 +1,71 @@
+/* main.c - IOMultiplexer
+ * Copyright (C) 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 <stdio.h>
+#include <sys/time.h>
+#include "../../IOHandler/IOHandler.h"
+#include "../../IOHandler/IOTimer.h"
+#include "../../IOHandler/IOLog.h"
+
+#define TEST_DURATION 100
+
+static IOTIMER_CALLBACK(io_callback);
+static IOLOG_CALLBACK(io_log);
+
+static struct timeval test_clock1, test_clock2;
+static int timercount;
+
+int main(int argc, char *argv[]) {
+       iohandler_init();
+       iolog_register_callback(io_log);
+       
+       gettimeofday(&test_clock1, NULL);
+       gettimeofday(&test_clock2, NULL);
+
+       struct timeval tv;
+       tv.tv_sec = TEST_DURATION / 1000;
+       tv.tv_usec = TEST_DURATION % 1000 * 1000;
+       struct IOTimerDescriptor *timer = iotimer_create(NULL);
+       iotimer_set_callback(timer, io_callback);
+       iotimer_set_autoreload(timer, &tv);
+       iotimer_start(timer);
+       
+       timercount = 0;
+       
+       printf("[timer 0] %ld.%ld\n", test_clock1.tv_sec, test_clock1.tv_usec);
+       
+       iohandler_run();
+       return 1;
+}
+
+static IOTIMER_CALLBACK(io_callback) {
+       struct timeval curr_time;
+       int diff1;
+       double diff2;
+       
+       timercount++;
+       gettimeofday(&curr_time, NULL);
+       diff1 = (curr_time.tv_sec - test_clock1.tv_sec) * 1000 + ((curr_time.tv_usec - test_clock1.tv_usec) / 1000);
+       diff2 = (curr_time.tv_sec - test_clock2.tv_sec) * 1000 + ((curr_time.tv_usec - test_clock2.tv_usec) / 1000.0);
+       diff2 -= (timercount * TEST_DURATION);
+       gettimeofday(&test_clock1, NULL);
+       printf("[timer %03d] %ld.%06ld [%d ms]  accuracy: %f ms\n", timercount, curr_time.tv_sec, curr_time.tv_usec, diff1, diff2);
+}
+
+static IOLOG_CALLBACK(io_log) {
+       //printf("%s", line);
+}
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..2f259ff
--- /dev/null
@@ -0,0 +1,3 @@
+##Process this file with automake to create Makefile.in
+ACLOCAL_AMFLAGS = -I m4
+SUBDIRS = IOHandler IOHandler++ IOHandler_test