From 9e6045c7b6d7afc774eeb59fa5f5c4e02fe1f037 Mon Sep 17 00:00:00 2001 From: pk910 Date: Sun, 2 Mar 2014 21:50:24 +0100 Subject: [PATCH] [IOMultiplexerV2] alpha --- .gitignore | 18 + Makefile.am | 3 + autogen.sh | 9 + configure.ac | 52 + src/.gitignore | 2 + src/IOHandler.c | 196 ---- src/IOHandler/.gitignore | 5 + src/IOHandler/IODNSAddress.struct.h | 27 + src/{ => IOHandler}/IODNSEngine_cares.c | 2 +- src/{ => IOHandler}/IODNSEngine_default.c | 72 +- src/{ => IOHandler}/IODNSLookup.c | 31 +- src/{ => IOHandler}/IODNSLookup.h | 52 +- src/IOHandler/IOEngine_epoll.c | 141 +++ src/IOHandler/IOEngine_kevent.c | 153 +++ src/IOHandler/IOEngine_select.c | 146 +++ src/IOHandler/IOEngine_win32.c | 205 ++++ src/{ => IOHandler}/IOGarbageCollector.c | 32 +- src/{ => IOHandler}/IOGarbageCollector.h | 0 src/IOHandler/IOHandler.c | 69 ++ src/{ => IOHandler}/IOHandler.h | 2 +- src/IOHandler/IOHandler_config.h | 50 + src/{ => IOHandler}/IOInternal.h | 24 +- src/{ => IOHandler}/IOLog.c | 11 +- src/{ => IOHandler}/IOLog.h | 9 +- src/IOHandler/IOSockets.c | 978 +++++++++++++++++++ src/{ => IOHandler}/IOSockets.h | 66 +- src/{ => IOHandler}/IOTimer.c | 69 +- src/{ => IOHandler}/IOTimer.h | 9 +- src/IOHandler/Makefile.am | 19 + src/IOHandler/compat/inet.c | 86 ++ src/IOHandler/compat/inet.h | 37 + src/IOHandler/compat/utime.c | 84 ++ src/IOHandler/compat/utime.h | 30 + src/IOHandler_test/Makefile.am | 3 + src/IOHandler_test/socket/.gitignore | 3 + src/IOHandler_test/socket/Makefile.am | 8 + src/{test => IOHandler_test}/socket/iotest.c | 47 +- src/IOHandler_test/timer/Makefile.am | 8 + src/IOHandler_test/timer/iotest.c | 71 ++ src/IOSockets.c | 748 -------------- src/Makefile.am | 3 + src/compat/utime.c | 48 - src/test/socket/Makefile | 12 - src/test/timer/Makefile | 12 - src/test/timer/iotest.c | 81 -- 45 files changed, 2478 insertions(+), 1255 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile.am create mode 100644 autogen.sh create mode 100644 configure.ac create mode 100644 src/.gitignore delete mode 100644 src/IOHandler.c create mode 100644 src/IOHandler/.gitignore create mode 100644 src/IOHandler/IODNSAddress.struct.h rename src/{ => IOHandler}/IODNSEngine_cares.c (98%) rename src/{ => IOHandler}/IODNSEngine_default.c (70%) rename src/{ => IOHandler}/IODNSLookup.c (89%) rename src/{ => IOHandler}/IODNSLookup.h (71%) create mode 100644 src/IOHandler/IOEngine_epoll.c create mode 100644 src/IOHandler/IOEngine_kevent.c create mode 100644 src/IOHandler/IOEngine_select.c create mode 100644 src/IOHandler/IOEngine_win32.c rename src/{ => IOHandler}/IOGarbageCollector.c (80%) rename src/{ => IOHandler}/IOGarbageCollector.h (100%) create mode 100644 src/IOHandler/IOHandler.c rename src/{ => IOHandler}/IOHandler.h (94%) create mode 100644 src/IOHandler/IOHandler_config.h rename src/{ => IOHandler}/IOInternal.h (60%) rename src/{ => IOHandler}/IOLog.c (89%) rename src/{ => IOHandler}/IOLog.h (82%) create mode 100644 src/IOHandler/IOSockets.c rename src/{ => IOHandler}/IOSockets.h (75%) rename src/{ => IOHandler}/IOTimer.c (74%) rename src/{ => IOHandler}/IOTimer.h (90%) create mode 100644 src/IOHandler/Makefile.am create mode 100644 src/IOHandler/compat/inet.c create mode 100644 src/IOHandler/compat/inet.h create mode 100644 src/IOHandler/compat/utime.c create mode 100644 src/IOHandler/compat/utime.h create mode 100644 src/IOHandler_test/Makefile.am create mode 100644 src/IOHandler_test/socket/.gitignore create mode 100644 src/IOHandler_test/socket/Makefile.am rename src/{test => IOHandler_test}/socket/iotest.c (54%) create mode 100644 src/IOHandler_test/timer/Makefile.am create mode 100644 src/IOHandler_test/timer/iotest.c delete mode 100644 src/IOSockets.c create mode 100644 src/Makefile.am delete mode 100644 src/compat/utime.c delete mode 100644 src/test/socket/Makefile delete mode 100644 src/test/timer/Makefile delete mode 100644 src/test/timer/iotest.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f134593 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +autom4te.cache +m4 +aclocal.m4 +config.guess +config.h +config.h.in +config.log +config.status +config.sub +configure +depcomp +install-sh +libtool +ltmain.sh +Makefile +Makefile.in +missing +stamp-h1 diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..92ae850 --- /dev/null +++ b/Makefile.am @@ -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 index 0000000..01c075c --- /dev/null +++ b/autogen.sh @@ -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 index 0000000..bd831bc --- /dev/null +++ b/configure.ac @@ -0,0 +1,52 @@ +# 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_INSTALL +AC_PROG_MAKE_SET +AC_PROG_RANLIB + +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" ], []) +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" + ]) + ]) +]) +AC_CHECK_LIB(pthread, pthread_create, [ + AC_CHECK_HEADERS(pthread.h, [ + LIBS="$LIBS -lpthread" + ]) +]) + +AC_CONFIG_FILES([Makefile src/Makefile src/IOHandler/Makefile src/IOHandler_test/Makefile src/IOHandler_test/socket/Makefile src/IOHandler_test/timer/Makefile]) +AC_OUTPUT diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..3dda729 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/src/IOHandler.c b/src/IOHandler.c deleted file mode 100644 index 87e5739..0000000 --- a/src/IOHandler.c +++ /dev/null @@ -1,196 +0,0 @@ -/* 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 . - */ -#define _IOHandler_internals -#include "IOInternal.h" -#include "IOHandler.h" - -#include "IOLog.h" -#include "IOGarbageCollector.h" -#include "IOTimer.h" -#include "IODNSLookup.h" - -/* compat */ -#include "compat/usleep.c" - -#ifdef HAVE_PTHREAD_H -static pthread_mutex_t iothread_sync; -#ifdef WIN32 -#define pthread_self_tid() pthread_self().p -#else -#define pthread_self_tid() pthread_self() -#endif - -#endif - -static struct IOHandlerThread { - unsigned int id; - unsigned int main : 1; - unsigned int run : 1; - unsigned int shutdown : 1; - #ifdef HAVE_PTHREAD_H - static pthread_t *thread; - #endif - struct IOHandlerThread *next; -} - -static int iohandler_running = 0; -static int iohandler_treads = 1; -static struct IOHandlerThread *threads; - -void iohandler_init() { - IOTHREAD_MUTEX_INIT(iothread_sync); - - iolog_init(); - iogc_init(); - - _init_timers(); - _init_iodns(); - _init_sockets(); - - iohandler_running = 1; -} - -void iohandler_set_threads(int threadcount) { - iohandler_treads = threadcount; -} - -void iohandler_stop() { - iohandler_treads = 0; -} - -#ifdef HAVE_PTHREAD_H -static void iohandler_start_worker() { - struct IOHandlerThread *thread = calloc(1, sizeof(*thread)); - if(!thread) { - iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOHandlerThread in %s:%d", __FILE__, __LINE__); - return; - } - struct IOHandlerThread *cthread; - for(cthread = threads; cthread; cthread = cthread->next) { - if(cthread->next == NULL) { - cthread->next = thread; - break; - } - } - - thread->run = 1; - - int thread_err; - thread_err = pthread_create(&thread->thread, NULL, iohandler_worker, thread); - if(thread_err) { - cthread->next = NULL; - iolog_trigger(IOLOG_ERROR, "could not create pthread in %s:%d (Returned: %i)", __FILE__, __LINE__, thread_err); - } -} -#endif - -static void iohandler_worker(void *tptr) { - struct IOHandlerThread *thread = tptr; - - #ifdef HAVE_PTHREAD_H - if(!thread->main) { - thread->id = pthread_self_tid(); - } - #endif - - while(!thread->shutdown) { // endless loop - if(thread->main && iohandler_treads != iohandler_running) { - IOSYNCHRONIZE(iothread_sync); - #ifdef HAVE_PTHREAD_H - int i; - if(iohandler_treads > iohandler_running) { - for(i = 0; i < (iohandler_treads - iohandler_running); i++) - iohandler_start_worker(); - } - if(iohandler_treads < iohandler_running) { - struct IOHandlerThread *cthread; - for(i = 0; i < (iohandler_running - iohandler_treads); i++) { - for(cthread = threads; cthread; cthread = cthread->next) { - if(cthread->main) - continue; - cthread->shutdown = 1; - iolog_trigger(IOLOG_ERROR, "Thread %d marked for shutdown.", cthread->id); - } - if(cthread) - iohandler_running--; - } - } - #endif - if(iohandler_treads == 0) { - #ifdef HAVE_PTHREAD_H - struct IOHandlerThread *cthread; - for(cthread = threads; cthread; cthread = cthread->next) { - if(cthread->main) - continue; - cthread->shutdown = 1; - pthread_join(cthread->thread, NULL); - } - #endif - thread->shutdown = 1; - IODESYNCHRONIZE(iothread_sync); - break; - } - IODESYNCHRONIZE(iothread_sync); - } - if(!thread->run) { - usleep(500000); // 500ms - continue; - } - - // iohandler calls - iogc_exec(); - iodns_poll(); - - } - IOSYNCHRONIZE(iothread_sync); - if(thread == threads) { - threads = thread->next; - } else { - struct IOHandlerThread *cthread; - for(cthread = threads; cthread; cthread = cthread->next) { - if(cthread->next == thread) { - cthread->next = thread->next; - break; - } - } - } - iolog_trigger(IOLOG_DEBUG, "Thread %d stopped.", thread->id); - free(thread); - IODESYNCHRONIZE(iothread_sync); -} - -void iohandler_run() { - if(!iohandler_running) - return; - iohandler_running = 1; - - struct IOHandlerThread *mainthread = calloc(1, sizeof(*mainthread)); - if(!mainthread) { - iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOHandlerThread in %s:%d", __FILE__, __LINE__); - return; - } - threads = mainthread; - - mainthread->main = 1; - mainthread->run = 1; - mainthread->shutdown = 0; - - iohandler_worker(1); - - _stop_iodns(); /* possible worker thread */ -} - diff --git a/src/IOHandler/.gitignore b/src/IOHandler/.gitignore new file mode 100644 index 0000000..95c9342 --- /dev/null +++ b/src/IOHandler/.gitignore @@ -0,0 +1,5 @@ +.deps +Makefile.in +Makefile +*.o +*.a diff --git a/src/IOHandler/IODNSAddress.struct.h b/src/IOHandler/IODNSAddress.struct.h new file mode 100644 index 0000000..64f6f31 --- /dev/null +++ b/src/IOHandler/IODNSAddress.struct.h @@ -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 . + */ +#ifndef _IODNSAddress_struct_h +#define _IODNSAddress_struct_h +#include +#include + +struct IODNSAddress { + size_t addresslen; + struct sockaddr *address; +}; + +#endif diff --git a/src/IODNSEngine_cares.c b/src/IOHandler/IODNSEngine_cares.c similarity index 98% rename from src/IODNSEngine_cares.c rename to src/IOHandler/IODNSEngine_cares.c index 0852470..2bbce18 100644 --- a/src/IODNSEngine_cares.c +++ b/src/IOHandler/IODNSEngine_cares.c @@ -17,7 +17,7 @@ #define _IOHandler_internals #include "IOInternal.h" #include "IOHandler.h" -#include "IODNSHandler.h" +#include "IODNSLookup.h" static int dnsengine_cares_init() { /* TODO */ diff --git a/src/IODNSEngine_default.c b/src/IOHandler/IODNSEngine_default.c similarity index 70% rename from src/IODNSEngine_default.c rename to src/IOHandler/IODNSEngine_default.c index 07e46c6..43f6b12 100644 --- a/src/IODNSEngine_default.c +++ b/src/IOHandler/IODNSEngine_default.c @@ -17,20 +17,37 @@ #define _IOHandler_internals #include "IOInternal.h" #include "IOHandler.h" -#include "IODNSHandler.h" +#include "IOLog.h" +#include "IODNSLookup.h" -#ifdef HAVE_PTHREAD_H -static pthread_t *iodns_thread; +#ifdef WIN32 +#define _WIN32_WINNT 0x501 +#include +#include +#include +#else +#include +#include +#include +#endif +#include "compat/inet.h" +#include +#include + + +#ifdef IODNS_USE_THREADS +static pthread_t iodns_thread; static int iodns_thread_running = 1; static pthread_cond_t iodns_cond; -static pthread_mutex_t iodns_sync2; +static pthread_mutex_t iodns_sync, iodns_sync2; #endif static int iodns_loop_blocking = 0; static void iodns_process_queries(); -static void dnsengine_worker_main(void *arg) { +#ifdef IODNS_USE_THREADS +static void *dnsengine_worker_main(void *arg) { struct _IODNSQuery *query; while(iodns_thread_running) { IOSYNCHRONIZE(iodns_sync); @@ -45,12 +62,15 @@ static void dnsengine_worker_main(void *arg) { if(iodns_thread_running) iodns_process_queries(); } + return NULL; } +#endif static int dnsengine_default_init() { - #ifdef HAVE_PTHREAD_H + #ifdef IODNS_USE_THREADS /* create worker thread */ pthread_cond_init(&iodns_cond, NULL); + IOTHREAD_MUTEX_INIT(iodns_sync); IOTHREAD_MUTEX_INIT(iodns_sync2); iodns_thread_running = 1; @@ -60,7 +80,6 @@ static int dnsengine_default_init() { if(thread_err) { iolog_trigger(IOLOG_ERROR, "could not create pthread in %s:%d (Returned: %i)", __FILE__, __LINE__, thread_err); iodns_loop_blocking = 1; - iodns_thread = NULL; iodns_thread_running = 0; } #else @@ -70,7 +89,7 @@ static int dnsengine_default_init() { } static void dnsengine_default_stop() { - #ifdef HAVE_PTHREAD_H + #ifdef IODNS_USE_THREADS if(iodns_thread_running) { iodns_thread_running = 0; IOSYNCHRONIZE(iodns_sync2); @@ -82,7 +101,7 @@ static void dnsengine_default_stop() { } static void dnsengine_default_add(struct _IODNSQuery *iodns) { - #ifdef HAVE_PTHREAD_H + #ifdef IODNS_USE_THREADS if(iodns_thread_running) { IOSYNCHRONIZE(iodns_sync2); pthread_cond_signal(&iodns_cond); @@ -102,12 +121,12 @@ static void dnsengine_default_loop() { static void iodns_process_queries() { enum IODNSEventType querystate; - struct addrinfo hints, *res, *next_res; + struct addrinfo hints, *res, *allres; struct _IODNSQuery *iodns, *next_iodns; struct IODNSResult *dnsresult; iodns_process_queries_start: IOSYNCHRONIZE(iodns_sync); - for(iodns = first_dnsquery; iodns; iodns = next_iodns) { + for(iodns = iodnsquery_first; iodns; iodns = next_iodns) { next_iodns = iodns->next; if(!(iodns->flags & IODNSFLAG_RUNNING)) @@ -124,7 +143,9 @@ static void iodns_process_queries() { hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags |= AI_CANONNAME; - if (!getaddrinfo(iodns->request.host, NULL, &hints, &res)) { + int ret; + if (!(ret = getaddrinfo(iodns->request.host, NULL, &hints, &allres))) { + res = allres; while (res) { switch (res->ai_family) { case AF_INET: @@ -132,10 +153,15 @@ static void iodns_process_queries() { dnsresult = malloc(sizeof(*dnsresult)); dnsresult->type = IODNS_RECORD_A; dnsresult->result.addr.addresslen = res->ai_addrlen; - dnsresult->result.addr.address = malloc(dnsresult->addresslen); - memcpy(dnsresult->address, res->ai_addr, dnsresult->addresslen); + dnsresult->result.addr.address = malloc(dnsresult->result.addr.addresslen); + 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; @@ -144,19 +170,25 @@ static void iodns_process_queries() { dnsresult = malloc(sizeof(*dnsresult)); dnsresult->type = IODNS_RECORD_AAAA; dnsresult->result.addr.addresslen = res->ai_addrlen; - dnsresult->result.addr.address = malloc(dnsresult->addresslen); - memcpy(dnsresult->address, res->ai_addr, dnsresult->addresslen); + dnsresult->result.addr.address = calloc(dnsresult->result.addr.addresslen, 1); + 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; } - next_res = res->ai_next; - freeaddrinfo(res); - res = next_res; + res = res->ai_next; } - } + freeaddrinfo(allres); + } else { + iolog_trigger(IOLOG_WARNING, "getaddrinfo returned error code: %d", ret); + } } IOSYNCHRONIZE(iodns_sync); if(!(iodns->flags & IODNSFLAG_RUNNING)) { diff --git a/src/IODNSLookup.c b/src/IOHandler/IODNSLookup.c similarity index 89% rename from src/IODNSLookup.c rename to src/IOHandler/IODNSLookup.c index 5497553..e39a9cf 100644 --- a/src/IODNSLookup.c +++ b/src/IOHandler/IODNSLookup.c @@ -21,9 +21,7 @@ #include "IOLog.h" #include "IOSockets.h" -#ifdef HAVE_PTHREAD_H -pthread_mutex_t iodns_sync; -#endif +#include struct _IODNSQuery *iodnsquery_first = NULL; struct _IODNSQuery *iodnsquery_last = NULL; @@ -39,14 +37,13 @@ static void iodns_init_engine() { else if(dnsengine_default.init && dnsengine_default.init()) dnsengine = &dnsengine_default; else { - iohandler_log(IOLOG_FATAL, "found no useable IO DNS engine"); + iolog_trigger(IOLOG_FATAL, "found no useable IO DNS engine"); return; } - iohandler_log(IOLOG_DEBUG, "using %s IODNS engine", dnsengine->name); + iolog_trigger(IOLOG_DEBUG, "using %s IODNS engine", dnsengine->name); } void _init_iodns() { - IOTHREAD_MUTEX_INIT(iodns_sync); iodns_init_engine(); } @@ -56,26 +53,21 @@ struct _IODNSQuery *_create_dnsquery() { iolog_trigger(IOLOG_ERROR, "could not allocate memory for _IODNSQuery in %s:%d", __FILE__, __LINE__); return NULL; } - IOSYNCHRONIZE(iodns_sync); if(iodnsquery_last) iodnsquery_last->next = query; else iodnsquery_first = query; query->prev = iodnsquery_last; iodnsquery_last = query; - IODESYNCHRONIZE(iodns_sync); return query; } void _start_dnsquery(struct _IODNSQuery *query) { - IOSYNCHRONIZE(iodns_sync); query->flags |= IODNSFLAG_RUNNING; dnsengine->add(query); - IODESYNCHRONIZE(iodns_sync); } void _free_dnsquery(struct _IODNSQuery *query) { - IOSYNCHRONIZE(iodns_sync); if(query->prev) query->prev->next = query->next; else @@ -84,21 +76,18 @@ void _free_dnsquery(struct _IODNSQuery *query) { query->next->prev = query->prev; else iodnsquery_last = query->prev; - IODESYNCHRONIZE(iodns_sync); if((query->type & IODNS_REVERSE) && query->request.addr.address) free(query->request.addr.address); free(query); } void _stop_dnsquery(struct _IODNSQuery *query) { - IOSYNCHRONIZE(iodns_sync); if((query->flags & IODNSFLAG_RUNNING)) { query->flags &= ~IODNSFLAG_RUNNING; dnsengine->remove(query); } if(!(query->flags & IODNSFLAG_PROCESSING)) _free_dnsquery(query); - IODESYNCHRONIZE(iodns_sync); } void iodns_event_callback(struct _IODNSQuery *query, enum IODNSEventType state) { @@ -109,7 +98,7 @@ void iodns_event_callback(struct _IODNSQuery *query, enum IODNSEventType state) event.query = descriptor; event.result = query->result; - descriptor->parent = NULL; + descriptor->query = NULL; _stop_dnsquery(query); if(descriptor->callback) @@ -120,7 +109,7 @@ void iodns_event_callback(struct _IODNSQuery *query, enum IODNSEventType state) struct IODNSEvent event; event.type = state; event.query = NULL; - event.result = query->result + event.result = query->result; void *parent = query->parent; _stop_dnsquery(query); @@ -131,7 +120,7 @@ void iodns_event_callback(struct _IODNSQuery *query, enum IODNSEventType state) void iodns_poll() { if(dnsengine) - dnsengine.loop(); + dnsengine->loop(); } /* public functions */ @@ -149,7 +138,7 @@ struct IODNSQuery *iodns_getaddrinfo(char *hostname, int records, iodns_callback struct _IODNSQuery *query = _create_dnsquery(); if(!query) { free(descriptor); - return NULL + return NULL; } query->parent = descriptor; @@ -165,7 +154,7 @@ struct IODNSQuery *iodns_getaddrinfo(char *hostname, int records, iodns_callback return descriptor; } -struct IODNSQuery *iodns_getnameinfo(const struct sockaddr *addr, socklen_t addrlen, iodns_callback *callback) { +struct IODNSQuery *iodns_getnameinfo(const struct sockaddr *addr, size_t addrlen, iodns_callback *callback) { if(!addr || !callback) return NULL; @@ -178,7 +167,7 @@ struct IODNSQuery *iodns_getnameinfo(const struct sockaddr *addr, socklen_t addr struct _IODNSQuery *query = _create_dnsquery(); if(!query) { free(descriptor); - return NULL + return NULL; } query->parent = descriptor; @@ -212,7 +201,7 @@ void iodns_abort(struct IODNSQuery *descriptor) { return; } - _stop_dnsquery(query) + _stop_dnsquery(query); } void iodns_free_result(struct IODNSResult *result) { diff --git a/src/IODNSLookup.h b/src/IOHandler/IODNSLookup.h similarity index 71% rename from src/IODNSLookup.h rename to src/IOHandler/IODNSLookup.h index 4235297..29574cb 100644 --- a/src/IODNSLookup.h +++ b/src/IOHandler/IODNSLookup.h @@ -16,24 +16,55 @@ */ #ifndef _IODNSLookup_h #define _IODNSLookup_h +#include +#include "IODNSAddress.struct.h" + #ifndef _IOHandler_internals #include "IOHandler.h" #else +struct IODNSEngine; extern struct IODNSEngine dnsengine_cares; extern struct IODNSEngine dnsengine_default; -#ifdef HAVE_PTHREAD_H -extern pthread_mutex_t iodns_sync; -#endif +struct _IODNSQuery; +extern struct _IODNSQuery *iodnsquery_first; +extern struct _IODNSQuery *iodnsquery_last; -struct IODNSAddress; +/* Multithreading */ +#ifdef IODNS_USE_THREADS +#ifndef HAVE_PTHREAD_H +#undef IODNS_USE_THREADS +#endif +#endif +#ifdef IODNS_USE_THREADS +#include +#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 _IODNSQuery { void *query; @@ -67,13 +98,17 @@ void _start_dnsquery(struct _IODNSQuery *query); void _stop_dnsquery(struct _IODNSQuery *query); /* call only from engines! */ -void _free_dnsquery(struct _IODNSQuery *query) +enum IODNSEventType; +void _free_dnsquery(struct _IODNSQuery *query); 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); @@ -89,11 +124,6 @@ enum IODNSEventType { #define IODNS_FORWARD 0x03 #define IODNS_REVERSE 0x04 -struct IODNSAddress { - size_t addresslen; - struct sockaddr *address; -}; - struct IODNSQuery { void *query; @@ -117,7 +147,7 @@ struct IODNSEvent { }; struct IODNSQuery *iodns_getaddrinfo(char *hostname, int records, iodns_callback *callback); -struct IODNSQuery *iodns_getnameinfo(const struct sockaddr *addr, socklen_t addrlen, iodns_callback *callback); +struct IODNSQuery *iodns_getnameinfo(const struct sockaddr *addr, size_t addrlen, iodns_callback *callback); void iodns_abort(struct IODNSQuery *query); void iodns_free_result(struct IODNSResult *result); diff --git a/src/IOHandler/IOEngine_epoll.c b/src/IOHandler/IOEngine_epoll.c new file mode 100644 index 0000000..060f87f --- /dev/null +++ b/src/IOHandler/IOEngine_epoll.c @@ -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 . + */ +#define _IOHandler_internals +#include "IOInternal.h" +#include "IOHandler.h" +#include "IOLog.h" +#include "IOSockets.h" +#include "IOTimer.h" + +#ifdef HAVE_SYS_EPOLL_H +#include +#include +#include +#include + +#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 | EPOLLIN | (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 | EPOLLIN | (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 index 0000000..1c39cc6 --- /dev/null +++ b/src/IOHandler/IOEngine_kevent.c @@ -0,0 +1,153 @@ +/* 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 . + */ +#define _IOHandler_internals +#include "IOInternal.h" +#include "IOHandler.h" +#include "IOLog.h" +#include "IOSockets.h" + +#ifdef HAVE_SYS_EVENT_H +#include +#include + +#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; + + 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, EV_ADD, 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 index 0000000..c0405f4 --- /dev/null +++ b/src/IOHandler/IOEngine_select.c @@ -0,0 +1,146 @@ +/* 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 . + */ +#define _IOHandler_internals +#include "IOInternal.h" +#include "IOHandler.h" +#include "IOLog.h" +#include "IOSockets.h" +#include "IOTimer.h" + +#include +#include +#ifdef WIN32 +#define _WIN32_WINNT 0x501 +#include +#include +#else +#include +#include +#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; + FD_SET(iosock->fd, &read_fds); + select_result++; + 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 index 0000000..6f755c2 --- /dev/null +++ b/src/IOHandler/IOEngine_win32.c @@ -0,0 +1,205 @@ +/* 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 . + */ +#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 +#include + +/* 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); + + iosocket_events_callback(iosock, (events & (FD_READ | FD_ACCEPT | FD_CLOSE)) != 0, (events & (FD_WRITE | FD_CONNECT)) != 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_READ | FD_CLOSE | (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/IOGarbageCollector.c b/src/IOHandler/IOGarbageCollector.c similarity index 80% rename from src/IOGarbageCollector.c rename to src/IOHandler/IOGarbageCollector.c index 363d816..d624396 100644 --- a/src/IOGarbageCollector.c +++ b/src/IOHandler/IOGarbageCollector.c @@ -18,11 +18,12 @@ #include "IOInternal.h" #include "IOHandler.h" #include "IOGarbageCollector.h" +#include "IOLog.h" -#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)) +#include +#include -static struct IOGCObject { +struct IOGCObject { void *object; iogc_free *free_callback; struct timeval timeout; @@ -30,16 +31,11 @@ static struct IOGCObject { struct IOGCObject *next; }; -#ifdef HAVE_PTHREAD_H -static pthread_mutex_t iogc_sync; -#endif - static int iogc_enabled = 1; static struct timeval iogc_timeout; static struct IOGCObject *first_object = NULL, *last_object = NULL; void iogc_init() { - IOTHREAD_MUTEX_INIT(iogc_sync); iogc_timeout.tv_usec = 0; iogc_timeout.tv_sec = 10; } @@ -76,25 +72,31 @@ void iogc_add_callback(void *object, iogc_free *free_callback) { 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 ctime; - gettimeofday(&ctime, NULL); + struct timeval now; + gettimeofday(&now, NULL); struct IOGCObject *obj, *next_obj; - for(obj = objects; obj; obj = next_obj) { - if(timeval_is_smaler(obj->timeout, ctime)) { + 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 { - objects = obj; + } else break; - } } + first_object = obj; } diff --git a/src/IOGarbageCollector.h b/src/IOHandler/IOGarbageCollector.h similarity index 100% rename from src/IOGarbageCollector.h rename to src/IOHandler/IOGarbageCollector.h diff --git a/src/IOHandler/IOHandler.c b/src/IOHandler/IOHandler.c new file mode 100644 index 0000000..ef7d654 --- /dev/null +++ b/src/IOHandler/IOHandler.c @@ -0,0 +1,69 @@ +/* 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 . + */ +#define _IOHandler_internals +#include "IOInternal.h" +#include "IOHandler.h" + +#include "IOLog.h" +#include "IOGarbageCollector.h" +#include "IOTimer.h" +#include "IODNSLookup.h" +#include "IOSockets.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; + + 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.h b/src/IOHandler/IOHandler.h similarity index 94% rename from src/IOHandler.h rename to src/IOHandler/IOHandler.h index 3cba91d..1424eb5 100644 --- a/src/IOHandler.h +++ b/src/IOHandler/IOHandler.h @@ -16,6 +16,7 @@ */ #ifndef _IOHandler_h #define _IOHandler_h +#include "IOHandler_config.h" #ifdef _IOHandler_internals #endif @@ -24,7 +25,6 @@ void iohandler_init(); void iohandler_run(); void iohandler_stop(); -void iohandler_set_threads(int threads); /* default: 1 */ 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 index 0000000..bffedc9 --- /dev/null +++ b/src/IOHandler/IOHandler_config.h @@ -0,0 +1,50 @@ +/* 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 . + */ + +/* required configure script checks + AC_FUNC_MALLOC + AC_FUNC_CALLOC + 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" ], []) + 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" + ]) + ]) + ]) + AC_CHECK_LIB(pthread, pthread_create, [ + AC_CHECK_HEADERS(pthread.h, [ + LIBS="$LIBS -lpthread" + ]) + ]) +*/ +// 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/IOInternal.h b/src/IOHandler/IOInternal.h similarity index 60% rename from src/IOInternal.h rename to src/IOHandler/IOInternal.h index 2dd5950..e5dc207 100644 --- a/src/IOInternal.h +++ b/src/IOHandler/IOInternal.h @@ -20,28 +20,8 @@ #include "IOHandler.h" #else -/* Multithreading */ -#ifdef HAVE_PTHREAD_H -#include -#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 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); diff --git a/src/IOLog.c b/src/IOHandler/IOLog.c similarity index 89% rename from src/IOLog.c rename to src/IOHandler/IOLog.c index 04af476..20637c1 100644 --- a/src/IOLog.c +++ b/src/IOHandler/IOLog.c @@ -19,10 +19,15 @@ #include "IOHandler.h" #include "IOLog.h" +#include +#include + void iolog_init() { } +#define MAXLOG 1024 + void iolog_trigger(enum IOLogType type, char *text, ...) { va_list arg_list; char logBuf[MAXLOG+1]; @@ -35,5 +40,9 @@ void iolog_trigger(enum IOLogType type, char *text, ...) { logBuf[pos] = '\n'; logBuf[pos+1] = '\0'; - + printf("%s", logBuf); +} + +void iolog_register_callback(iolog_callback *callback) { + } diff --git a/src/IOLog.h b/src/IOHandler/IOLog.h similarity index 82% rename from src/IOLog.h rename to src/IOHandler/IOLog.h index 0296b3d..451155e 100644 --- a/src/IOLog.h +++ b/src/IOHandler/IOLog.h @@ -14,8 +14,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _IOInternal_h -#define _IOInternal_h +#ifndef _IOLog_h +#define _IOLog_h #ifndef _IOHandler_internals #include "IOHandler.h" #else @@ -33,6 +33,9 @@ enum IOLogType { IOLOG_FATAL }; -/* TODO: Functions to get messages from IOLog */ +#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/IOSockets.c b/src/IOHandler/IOSockets.c new file mode 100644 index 0000000..1973ea4 --- /dev/null +++ b/src/IOHandler/IOSockets.c @@ -0,0 +1,978 @@ +/* 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 . + */ +#define _IOHandler_internals +#include "IOInternal.h" +#include "IOHandler.h" +#include "IOSockets.h" +#include "IOLog.h" +#include "IODNSLookup.h" + +#ifdef WIN32 +#define _WIN32_WINNT 0x501 +#include +#include +#include +#else +#include +#include +#include +#include +#include +#endif +#include "compat/inet.h" +#include +#include +#include +#include +#include +#include + +#ifndef EWOULDBLOCK +#define EWOULDBLOCK EAGAIN +#endif + +struct _IOSocket *iosocket_first = NULL; +struct _IOSocket *iosocket_last = NULL; + +struct IOEngine *engine = NULL; + +static void iosocket_activate(struct _IOSocket *iosock); +static void iosocket_deactivate(struct _IOSocket *iosock); +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 iosocket_connect_finish(struct _IOSocket *iosock); +static void iosocket_listen_finish(struct _IOSocket *iosock); +static int iosocket_try_write(struct _IOSocket *iosock); + +#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(); +} + + +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->readbuf.buffer) + free(iosock->readbuf.buffer); + if(iosock->writebuf.buffer) + free(iosock->writebuf.buffer); + + free(iosock); +} + +static void iosocket_activate(struct _IOSocket *iosock) { + if((iosock->socket_flags & IOSOCKETFLAG_ACTIVE)) + return; + iosock->socket_flags |= IOSOCKETFLAG_ACTIVE; + engine->add(iosock); +} + +static void iosocket_deactivate(struct _IOSocket *iosock) { + if(!(iosock->socket_flags & IOSOCKETFLAG_ACTIVE)) + return; + iosock->socket_flags &= ~IOSOCKETFLAG_ACTIVE; + engine->remove(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)) + 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_clear; + } + + 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_clear; + } 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_clear; + } else if(bind_lookup && dest_lookup) { + if(bind_numip6 > 0 && dest_numip6 > 0) + useip6 = 1; + else if(bind_numip4 > 0 && dest_numip4 > 0) + useip4 = 1; + 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_clear; + } + } else if(bind_lookup) { + if(bind_numip6) + useip6 = 1; + else if(bind_numip4) + useip4 = 1; + } else if(dest_lookup) { + if(dest_numip6) + useip6 = 1; + else if(dest_numip4) + useip4 = 1; + } + + int usetype = 0; + if(useip6) { + usetype = IODNS_RECORD_AAAA; + iosock->socket_flags |= IOSOCKETFLAG_IPV6SOCKET; + } else if(useip4) { + usetype = IODNS_RECORD_A; + } else { + //should already be handled + } + + #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_clear; \ + } \ + memcpy(iosock->type.addr.address, result->result.addr.address, result->result.addr.addresslen); + + + if(bind_lookup) { + int usenum = (useip6 ? 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((useip6 ? AF_INET6 : AF_INET), (useip6 ? (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", (useip6 ? "6" : "4"), errbuf); + IOSOCKET_APPLY_COPYADDR(bind) + break; + } + usenum--; + } + } + } else + iosock->bind.addr.addresslen = 0; + + if(dest_lookup) { + int usenum = (useip6 ? 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((useip6 ? AF_INET6 : AF_INET), (useip6 ? (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", (useip6 ? "6" : "4"), errbuf); + IOSOCKET_APPLY_COPYADDR(dest) + break; + } + usenum--; + } + } + } else + iosock->dest.addr.addresslen = 0; + + iosocket_lookup_clear: + if(bind_lookup) { + if(bind_lookup->result) + iodns_free_result(bind_lookup->result); + free(bind_lookup); + } + if(dest_lookup) { + if(dest_lookup->result) + iodns_free_result(dest_lookup->result); + free(dest_lookup); + } + + if((iosock->socket_flags & IOSOCKETFLAG_DNSERROR)) { + // TODO: trigger error + + return 0; + } else + return 1; +} + +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); + } + #else + /* I hope you're using the Win32 backend or something else that + * automatically marks the file descriptor non-blocking... + */ + #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); + + connect(sockfd, iosock->dest.addr.address, iosock->dest.addr.addresslen); //returns EINPROGRESS here (nonblocking) + 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(0); + + 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(0); + + 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_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; + //TODO: SSL Handshake + } + + 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; + /* + if(ssl) { + iodescriptor->ssl = 1; + iosock->socket_flags |= IOSOCKETFLAG_SSLSOCKET; + } + */ + + 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) { + //TODO: SSL + return NULL; +} + +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->sslnode) { + //TODO: SSL + } + 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) return 0; + iolog_trigger(IOLOG_DEBUG, "write writebuf (%d bytes) to socket (fd: %d)", iosock->writebuf.bufpos, iosock->fd); + int res; + if(iosock->sslnode) { + /* res = iohandler_ssl_write(iofd, iofd->writebuf.buffer, iofd->writebuf.bufpos); */ + // TODO + } 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 - 2, text, arg_list); + va_end(arg_list); + if (pos < 0 || pos > (IOSOCKET_PRINTF_LINE_LEN - 2)) pos = IOSOCKET_PRINTF_LINE_LEN - 2; + sendBuf[pos] = '\n'; + sendBuf[pos+1] = '\0'; + iosocket_send(iosocket, sendBuf, pos+1); +} + + + +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)) { + //TODO: SSL + } 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 + 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 + iosocket->status = IOSOCKET_CONNECTED; + if((iosock->socket_flags & IOSOCKETFLAG_SSLSOCKET)) { + //TODO: SSL Handshake + return; + } + + callback_event.type = IOSOCKETEVENT_CONNECTED; + iosock->socket_flags &= ~IOSOCKETFLAG_CONNECTING; + engine->update(iosock); + + //initialize readbuf + iosocket_increase_buffer(&iosock->readbuf, 1024); + } + } else { + if(readable) { + int bytes; + if(iosock->readbuf.buflen - iosock->readbuf.bufpos >= 128) + iosocket_increase_buffer(&iosock->readbuf, iosock->readbuf.buflen + 1024); + if((iosock->socket_flags & IOSOCKETFLAG_SSLSOCKET)) { + //TODO: SSL read + } else + bytes = recv(iosock->fd, iosock->readbuf.buffer + iosock->readbuf.bufpos, iosock->readbuf.buflen - iosock->readbuf.bufpos, 0); + + if(bytes <= 0) { + 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; + 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(writeable) { + int bytes; + bytes = iosocket_try_write(iosock); + if(bytes < 0) { + iosock->socket_flags |= IOSOCKETFLAG_DEAD; + + callback_event.type = IOSOCKETEVENT_CLOSED; + callback_event.data.errid = errno; + } + } + + } + 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)) { + //TODO: IODNS callback + } +} + +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/IOSockets.h b/src/IOHandler/IOSockets.h similarity index 75% rename from src/IOSockets.h rename to src/IOHandler/IOSockets.h index 79f945e..ab36225 100644 --- a/src/IOSockets.h +++ b/src/IOHandler/IOSockets.h @@ -16,12 +16,48 @@ */ #ifndef _IOSockets_h #define _IOSockets_h +#include +#include +#include "IODNSAddress.struct.h" + +struct IOSocketBuffer { + char *buffer; + size_t bufpos, buflen; +}; + #ifndef _IOHandler_internals #include "IOHandler.h" #else -#define IOSOCKET_LINE_LEN 1024 +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 0x0001 #define IOSOCKETFLAG_LISTENING 0x0002 #define IOSOCKETFLAG_PENDING_BINDDNS 0x0004 @@ -33,12 +69,12 @@ #define IOSOCKETFLAG_PARENT_PUBLIC 0x0100 #define IOSOCKETFLAG_PARENT_DNSENGINE 0x0200 #define IOSOCKETFLAG_SSLSOCKET 0x0400 /* use ssl after connecting */ -#define IOSOCKETFLAG_SHUTDOWN 0x0800 /* disconnect pending */ - -struct IODNSAddress; -struct IOSocketBuffer; -struct IOSSLDescriptor; -struct _IODNSQuery; +#define IOSOCKETFLAG_SSL_HANDSHAKE 0x0800 /* SSL Handshake in progress */ +#define IOSOCKETFLAG_SSL_WANTWRITE 0x1000 +#define IOSOCKETFLAG_SHUTDOWN 0x2000 /* disconnect pending */ +#define IOSOCKETFLAG_CONNECTING 0x4000 +#define IOSOCKETFLAG_INCOMING 0x8000 /* incoming (accepted) connection */ +#define IOSOCKETFLAG_DEAD 0x10000 struct IOSocketDNSLookup { unsigned int bindlookup : 1; @@ -51,7 +87,7 @@ struct IOSocketDNSLookup { struct _IOSocket { int fd; - unsigned int socket_flags : 16; + unsigned int socket_flags : 24; union { struct IODNSAddress addr; @@ -75,7 +111,11 @@ struct _IOSocket { }; void _init_sockets(); +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); + +#define iosocket_wants_writes(IOSOCK) (IOSOCK->writebuf.bufpos || (IOSOCK->socket_flags & (IOSOCKETFLAG_CONNECTING | IOSOCKETFLAG_SSL_WANTWRITE))) #endif @@ -108,17 +148,14 @@ struct IOSocket { enum IOSocketStatus status; int listening : 1; int ssl : 1; - int read_lines : 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 IOSocketBuffer { - char *buffer; - size_t bufpos, buflen; -}; - struct IOSocketEvent { enum IOSocketEventType type; struct IOSocket *socket; @@ -133,6 +170,7 @@ struct IOSocketEvent { #define IOSOCKET_ADDR_IPV4 0x01 #define IOSOCKET_ADDR_IPV6 0x02 /* overrides IOSOCKET_ADDR_IPV4 */ +#define IOSOCKET_PROTO_UDP 0x04 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); diff --git a/src/IOTimer.c b/src/IOHandler/IOTimer.c similarity index 74% rename from src/IOTimer.c rename to src/IOHandler/IOTimer.c index 24545cd..ec90d39 100644 --- a/src/IOTimer.c +++ b/src/IOHandler/IOTimer.c @@ -20,21 +20,17 @@ #include "IOTimer.h" #include "IOLog.h" -#ifdef HAVE_PTHREAD_H -static pthread_mutex_t iotimer_sync; -#endif +#include +#include -#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)) - -static void _rearrange_timer(struct IOTimerDescriptor *timer); -static void _autoreload_timer(struct IOTimerDescriptor *timer); +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 *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__); @@ -74,9 +70,9 @@ void iotimer_set_autoreload(struct IOTimerDescriptor *descriptor, struct timeval timer->autoreload = *autoreload; if(!(timer->flags & IOTIMERFLAG_IN_LIST)) { - struct timeval ctime; - gettimeofday(&ctime, NULL); - timer->timeout = ctime; + struct timeval now; + gettimeofday(&now, NULL); + timer->timeout = now; _autoreload_timer(timer); } } else { @@ -102,10 +98,10 @@ void iotimer_destroy(struct IOTimerDescriptor *descriptor) { /* internal functions */ void _init_timers() { - IOTHREAD_MUTEX_INIT(iotimer_sync); + //nothing in here } -struct _IOTimerDescriptor _create_timer(struct timeval *timeout) { +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__); @@ -118,8 +114,7 @@ struct _IOTimerDescriptor _create_timer(struct timeval *timeout) { return timer; } -static void _rearrange_timer(struct IOTimerDescriptor *timer) { - IOSYNCHRONIZE(iotimer_sync); +static void _rearrange_timer(struct _IOTimerDescriptor *timer) { if((timer->flags & IOTIMERFLAG_IN_LIST)) { if(timer->prev == NULL) iotimer_sorted_descriptors = timer->next; @@ -129,8 +124,8 @@ static void _rearrange_timer(struct IOTimerDescriptor *timer) { timer->next->prev = timer->prev; } struct _IOTimerDescriptor *ctimer; - for(ctimer = iotimer_sorted_descriptors; ctimer;) { - if(timeval_is_bigger(&ctimer->timeout, &timer->timeout)) { + 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) @@ -138,7 +133,7 @@ static void _rearrange_timer(struct IOTimerDescriptor *timer) { else iotimer_sorted_descriptors = timer; ctimer->prev = timer; - break + break; } else if(ctimer->next == NULL) { ctimer->next = timer; @@ -149,24 +144,21 @@ static void _rearrange_timer(struct IOTimerDescriptor *timer) { if(ctimer == NULL) iotimer_sorted_descriptors = timer; timer->flags |= IOTIMERFLAG_IN_LIST; - IODESYNCHRONIZE(iotimer_sync); } void _destroy_timer(struct _IOTimerDescriptor *timer) { if((timer->flags & IOTIMERFLAG_IN_LIST)) { - IOSYNCHRONIZE(iotimer_sync); if(timer->prev == NULL) iotimer_sorted_descriptors = timer->next; else timer->prev->next = timer->next; if(timer->next != NULL) timer->next->prev = timer->prev; - IODESYNCHRONIZE(iotimer_sync); } free(timer); } -static void _autoreload_timer(struct IOTimerDescriptor *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) { @@ -176,3 +168,34 @@ static void _autoreload_timer(struct IOTimerDescriptor *timer) { _rearrange_timer(timer); } +void _trigger_timer() { + struct timeval now; + _trigger_timer_start: + gettimeofday(&now, NULL); + + struct _IOTimerDescriptor *timer = iotimer_sorted_descriptors; + if(!timer || timeval_is_bigger(timer->timeout, now)) { + return; + } + 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); + } + + goto _trigger_timer_start; +} + diff --git a/src/IOTimer.h b/src/IOHandler/IOTimer.h similarity index 90% rename from src/IOTimer.h rename to src/IOHandler/IOTimer.h index b7bd71a..c173282 100644 --- a/src/IOTimer.h +++ b/src/IOHandler/IOTimer.h @@ -19,6 +19,8 @@ #ifndef _IOHandler_internals #include "IOHandler.h" #else +#include + #define IOTIMERFLAG_PERIODIC 0x01 #define IOTIMERFLAG_ACTIVE 0x02 @@ -41,11 +43,14 @@ struct _IOTimerDescriptor { }; void _init_timers(); -struct _IOTimerDescriptor _create_timer(struct timeval timeout); +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); @@ -56,7 +61,7 @@ struct IOTimerDescriptor { void *data; }; -struct IOTimerDescriptor iotimer_create(struct timeval *timeout); +struct IOTimerDescriptor *iotimer_create(struct timeval *timeout); void iotimer_start(struct IOTimerDescriptor *iotimer); void iotimer_set_autoreload(struct IOTimerDescriptor *iotimer, struct timeval *autoreload); void iotimer_set_callback(struct IOTimerDescriptor *iotimer, iotimer_callback *callback); diff --git a/src/IOHandler/Makefile.am b/src/IOHandler/Makefile.am new file mode 100644 index 0000000..7cbb02c --- /dev/null +++ b/src/IOHandler/Makefile.am @@ -0,0 +1,19 @@ +##Process this file with automake to create Makefile.in +ACLOCAL_AMFLAGS = -I m4 + +libiohandler_a_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 \ + IOTimer.c + +noinst_LIBRARIES = libiohandler.a diff --git a/src/IOHandler/compat/inet.c b/src/IOHandler/compat/inet.c new file mode 100644 index 0000000..aa7245f --- /dev/null +++ b/src/IOHandler/compat/inet.c @@ -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 . + */ +#include "../IOHandler_config.h" + +#include +#include +#include + +#ifdef WIN32 +#include +#include +#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 index 0000000..a61424c --- /dev/null +++ b/src/IOHandler/compat/inet.h @@ -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 . + */ +#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 +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 index 0000000..193ab53 --- /dev/null +++ b/src/IOHandler/compat/utime.c @@ -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 . + */ +#include "../IOHandler_config.h" +#include "utime.h" +#ifndef HAVE_USLEEP + +#ifdef HAVE_SELECT + +#ifdef HAVE_SYS_SELECT_H +# include +#else +# include +# include +# include +#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 + +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 +#ifdef HAVE_UNISTD_H +#include +#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 index 0000000..4156880 --- /dev/null +++ b/src/IOHandler/compat/utime.h @@ -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 . + */ +#ifndef _compat_utime_h +#define _compat_utime_h +#include "../IOHandler_config.h" +#include +#ifdef HAVE_UNISTD_H +#include +#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 index 0000000..5fc8fac --- /dev/null +++ b/src/IOHandler_test/Makefile.am @@ -0,0 +1,3 @@ +##Process this file with automake to create Makefile.in +ACLOCAL_AMFLAGS = -I m4 +SUBDIRS = socket timer diff --git a/src/IOHandler_test/socket/.gitignore b/src/IOHandler_test/socket/.gitignore new file mode 100644 index 0000000..d6a00c5 --- /dev/null +++ b/src/IOHandler_test/socket/.gitignore @@ -0,0 +1,3 @@ +.deps +*.o +iotest diff --git a/src/IOHandler_test/socket/Makefile.am b/src/IOHandler_test/socket/Makefile.am new file mode 100644 index 0000000..1dac3a5 --- /dev/null +++ b/src/IOHandler_test/socket/Makefile.am @@ -0,0 +1,8 @@ +##Process this file with automake to create Makefile.in +ACLOCAL_AMFLAGS = -I m4 + +noinst_PROGRAMS = iotest +iotest_LDADD = ../../IOHandler/libiohandler.a + +iotest_SOURCES = iotest.c + diff --git a/src/test/socket/iotest.c b/src/IOHandler_test/socket/iotest.c similarity index 54% rename from src/test/socket/iotest.c rename to src/IOHandler_test/socket/iotest.c index 15f32f4..ec9a330 100644 --- a/src/test/socket/iotest.c +++ b/src/IOHandler_test/socket/iotest.c @@ -16,41 +16,40 @@ */ #include -#include "../IOHandler.h" +#include "../../IOHandler/IOHandler.h" +#include "../../IOHandler/IOSockets.h" +#include "../../IOHandler/IOLog.h" -static IOHANDLER_CALLBACK(io_callback); -static IOHANDLER_LOG_BACKEND(io_log); +static IOSOCKET_CALLBACK(io_callback); +static IOLOG_CALLBACK(io_log); -static struct IODescriptor *irc_iofd = NULL; +static struct IOSocket *irc_iofd = NULL; int main(int argc, char *argv[]) { - iolog_backend = io_log; + iohandler_init(); + + iolog_register_callback(io_log); - irc_iofd = iohandler_connect("pk910.de", 6667, 0, NULL, io_callback); - - struct IODescriptor *iofd; - iofd = iohandler_add(0, IOTYPE_STDIN, NULL, io_callback); - iofd->read_lines = 1; - - while(1) { - iohandler_poll(); - } + irc_iofd = iosocket_connect_flags("irc.nextirc.net", 6667, 0, NULL, io_callback, IOSOCKET_ADDR_IPV4); + irc_iofd->parse_delimiter = 1; + irc_iofd->delimiters[0] = '\n'; + irc_iofd->delimiters[1] = '\r'; + + iohandler_run(); + + return 0; } -static IOHANDLER_CALLBACK(io_callback) { +static IOSOCKET_CALLBACK(io_callback) { switch(event->type) { - case IOEVENT_CONNECTED: + case IOSOCKETEVENT_CONNECTED: printf("[connect]\n"); break; - case IOEVENT_CLOSED: + case IOSOCKETEVENT_CLOSED: printf("[disconnect]\n"); break; - case IOEVENT_RECV: - if(event->iofd->type == IOTYPE_STDIN) { - iohandler_printf(irc_iofd, "%s\n", event->data.recv_str); - printf("[out] %s\n", event->data.recv_str); - } else - printf("[in] %s\n", event->data.recv_str); + case IOSOCKETEVENT_RECV: + printf("[in] %s\n", event->data.recv_str); break; default: @@ -58,6 +57,6 @@ static IOHANDLER_CALLBACK(io_callback) { } } -static IOHANDLER_LOG_BACKEND(io_log) { +static IOLOG_CALLBACK(io_log) { //printf("%s", line); } diff --git a/src/IOHandler_test/timer/Makefile.am b/src/IOHandler_test/timer/Makefile.am new file mode 100644 index 0000000..1dac3a5 --- /dev/null +++ b/src/IOHandler_test/timer/Makefile.am @@ -0,0 +1,8 @@ +##Process this file with automake to create Makefile.in +ACLOCAL_AMFLAGS = -I m4 + +noinst_PROGRAMS = iotest +iotest_LDADD = ../../IOHandler/libiohandler.a + +iotest_SOURCES = iotest.c + diff --git a/src/IOHandler_test/timer/iotest.c b/src/IOHandler_test/timer/iotest.c new file mode 100644 index 0000000..97d43c6 --- /dev/null +++ b/src/IOHandler_test/timer/iotest.c @@ -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 . + */ + +#include +#include +#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/IOSockets.c b/src/IOSockets.c deleted file mode 100644 index 7e912f0..0000000 --- a/src/IOSockets.c +++ /dev/null @@ -1,748 +0,0 @@ -/* 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 . - */ -#define _IOHandler_internals -#include "IOInternal.h" -#include "IOHandler.h" -#include "IOSockets.h" -#include "IOLog.h" -#include "IODNSLookup.h" - -#ifdef HAVE_PTHREAD_H -static pthread_mutex_t iosocket_sync, iosocket_dns_sync; -#endif - -static struct _IOSocket *iosocket_first = NULL; -static struct _IOSocket *iosocket_last = NULL; - -/* 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; - -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 void iosocket_lookup_apply(struct _IOSocket *iosock, struct IODNSAddress *addr, int bindlookup); -static void iosocket_connect_finish(struct _IOSocket *iosock); -static void iosocket_listen_finish(struct _IOSocket *iosock); - - - - -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() { - IOTHREAD_MUTEX_INIT(iosocket_sync); - IOTHREAD_MUTEX_INIT(iosocket_dns_sync); - iosockets_init_engine(); -} - - -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; - } - IOSYNCHRONIZE(iosocket_sync); - if(iosocket_last) - iosocket_last->next = iosock; - else - iosocket_first = iosock; - iosock->prev = iosocket_last; - iosocket_last = iosock; - IODESYNCHRONIZE(iosocket_sync); - return iosock; -} - -void _free_socket(struct _IOSocket *iosock) { - iosocket_deactivate(iosock); - IOSYNCHRONIZE(iosocket_sync); - 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; - IODESYNCHRONIZE(iosocket_sync); - - if(iosock->bind.addr.addresslen) - free(iosock->bind.addr.address); - if(iosock->dest.addr.addresslen) - free(iosock->dest.addr.address); - if(iosock->readbuf.buffer) - free(iosock->readbuf.buffer); - if(iosock->writebuf.buffer) - free(iosock->writebuf.buffer); - - free(iosock); -} - -static void iosocket_activate(struct _IOSocket *iosock) { - if((iosock->flags & IOSOCKETFLAG_ACTIVE)) - return; - iosock->flags |= IOSOCKETFLAG_ACTIVE; - engine->add(iosock); -} - -static void iosocket_deactivate(struct _IOSocket *iosock) { - if(!(iosock->flags & IOSOCKETFLAG_ACTIVE)) - return; - iosock->flags &= ~IOSOCKETFLAG_ACTIVE; - engine->remove(iosock); -} - -static void iosocket_increase_buffer(struct IOSocketBuffer *iobuf, size_t required) { - if(iobuf->buflen >= required) return; - char *new_buf = realloc(iobuf->buffer, 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((flags & 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, addr->addresslen); - return 1; - } - } - if((flags & 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, addr->addresslen); - 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 = iosocket; - 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; - IOSYNCHRONIZE(iosocket_dns_sync); - struct _IOSocket *iosock = lookup->iosock; - if(iosock == NULL) { - IODESYNCHRONIZE(iosocket_dns_sync); - return; - } - if(event->type == IODNSEVENT_SUCCESS) - lookup->result = event->result; - else - lookup->result = NULL; - - if(lookup->bindlookup) { - iosock->flags &= ~IOSOCKETFLAG_PENDING_BINDDNS; - iosock->flags |= IOSOCKETFLAG_DNSDONE_BINDDNS; - } else { - iosock->flags &= ~IOSOCKETFLAG_PENDING_DESTDNS; - iosock->flags |= IOSOCKETFLAG_DNSDONE_DESTDNS; - } - - int dns_finished = 0; - if((iosock->flags & (IOSOCKETFLAG_PENDING_BINDDNS | IOSOCKETFLAG_PENDING_DESTDNS)) == 0) - dns_finished = 1; - IODESYNCHRONIZE(iosocket_dns_sync); - if(dns_finished) { - int ret; - ret = iosocket_lookup_apply(iosock); - if(ret) { //if ret=0 an error occured in iosocket_lookup_apply and we should stop here. - if((iosock->flags & IOSOCKETFLAG_LISTENING)) - iosocket_listen_finish(iosock); - else - iosocket_connect_finish(iosock); - } - } -} - -static void iosocket_lookup_apply(struct _IOSocket *iosock) { - char errbuf[512]; - struct IOSocketDNSLookup *bind_lookup = ((iosock->flags & IOSOCKETFLAG_DNSDONE_BINDDNS) ? iosock->bind.addrlookup : NULL); - struct IOSocketDNSLookup *dest_lookup = ((iosock->flags & IOSOCKETFLAG_DNSDONE_DESTDNS) ? iosock->dest.addrlookup : NULL); - if(!bind_lookup && !dest_lookup) { - iosock->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_clear; - } - - 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->flags |= IOSOCKETFLAG_DNSERROR; - sprintf(errbuf, "could not lookup bind address (%s)", bind_lookup->hostname); - goto iosocket_lookup_clear; - } else if(dest_lookup && (dest_numip6 == 0 && dest_numip4 == 0)) { - iosock->flags |= IOSOCKETFLAG_DNSERROR; - sprintf(errbuf, "could not lookup destination address (%s)", dest_lookup->hostname); - goto iosocket_lookup_clear; - } else if(bind_lookup && dest_lookup) { - if(bind_numip6 > 0 && dest_numip6 > 0) - useip6 = 1; - else if(bind_numip4 > 0 && dest_numip4 > 0) - useip4 = 1; - else { - iosock->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_clear; - } - } else if(bind_lookup) { - if(bind_numip6) - useip6 = 1; - else if(bind_numip4) - useip4 = 1; - } else if(dest_lookup) { - if(dest_numip6) - useip6 = 1; - else if(dest_numip4) - useip4 = 1; - } - - int usetype = 0; - if(useip6) { - usetype = IODNS_RECORD_AAAA; - iosock->flags |= IOSOCKETFLAG_IPV6SOCKET; - } else { - usetype = IODNS_RECORD_A; - } - - #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->flags |= IOSOCKETFLAG_DNSERROR; \ - sprintf(errbuf, "could not allocate memory for dns information"); \ - goto iosocket_lookup_clear; \ - } \ - memcpy(iosock->type.addr.address, result->result.addr.address, result->result.addr.addresslen); - - - if(bind_lookup) { - int usenum = (useip6 ? bind_numip6 : bind_numip4); - usenum = rand() % usenum; - for(result = bind_lookup->result; result; result = result->next) { - if((result->type & usetype)) { - if(usenum == 0) { - IOSOCKET_APPLY_COPYADDR(bind) - break; - } - usenum--; - } - } - } else - iosock->bind.addr.addresslen = 0; - - if(dest_lookup) { - int usenum = (useip6 ? dest_numip6 : dest_numip4); - usenum = rand() % usenum; - for(result = dest_lookup->result; result; result = result->next) { - if((result->type & usetype)) { - if(usenum == 0) { - IOSOCKET_APPLY_COPYADDR(dest) - break; - } - usenum--; - } - } - } else - iosock->dest.addr.addresslen = 0; - - iosocket_lookup_clear: - if(bind_lookup) { - if(bind_lookup->result) - iodns_free_result(bind_lookup->result); - free(bind_lookup); - } - if(dest_lookup) { - if(bind_lookup->result) - iodns_free_result(dest_lookup->result); - free(dest_lookup); - } - - if((iosock->flags & IOSOCKETFLAG_DNSERROR)) { - // TODO: trigger error - - return 0; - } else - return 1; -} - -static void iosocket_connect_finish(struct _IOSocket *iosock) { - int sockfd; - if((iosock->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->flags & IOSOCKETFLAG_IPV6SOCKET)) { - struct sockaddr_in6 *ip6 = iosock->dest.addr.address; - ip6->sin6_family = AF_INET6; - ip6->sin6_port = htons(iosock->port); - - if(iosock->bind.addr.addresslen) { - struct sockaddr_in6 *ip6bind = 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 = iosock->dest.addr.address; - ip->sin_family = AF_INET; - ip->sin_port = htons(iosock->port); - - if(iosock->bind.addr.addresslen) { - struct sockaddr_in *ip4bind = iosock->bind.addr.address; - ip4bind->sin_family = AF_INET; - ip4bind->sin6_port = htons(0); - - bind(sockfd, (struct sockaddr*)ip4bind, sizeof(*ip4bind)); - } - } - - // 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); - } - #else - /* I hope you're using the Win32 backend or something else that - * automatically marks the file descriptor non-blocking... - */ - #endif - - connect(sockfd, iosock->dest.addr.address, iosock->dest.addr.addresslen); //returns EINPROGRESS here (nonblocking) - iosock->fd = sockfd; - - iosocket_activate(iosock); -} - -static void iosocket_listen_finish(struct _IOSocket *iosock) { - int sockfd; - if((iosock->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->flags & IOSOCKETFLAG_IPV6SOCKET)) { - struct sockaddr_in6 *ip6bind = iosock->bind.addr.address; - ip6bind->sin6_family = AF_INET6; - ip6bind->sin6_port = htons(0); - - 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 = iosock->bind.addr.address; - ip4bind->sin_family = AF_INET; - ip4bind->sin6_port = htons(0); - - int opt = 1; - setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt)); - - bind(sockfd, (struct sockaddr*)ip4bind, sizeof(*ip4bind)); - } - - // 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); - } - #else - /* I hope you're using the Win32 backend or something else that - * automatically marks the file descriptor non-blocking... - */ - #endif - - listen(sockfd, 1); - iosock->fd = sockfd; - - iosocket_activate(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->flags |= IOSOCKETFLAG_PARENT_PUBLIC; - if(ssl) - iosock->flags |= IOSOCKETFLAG_SSLSOCKET; - - IOSYNCHRONIZE(iosocket_dns_sync); - if(bindhost) { - switch(iosocket_parse_address(bindhost, &iosock->bind.addr, flags)) { - case -1: - free(iosock); - return NULL; - case 0: - /* start dns lookup */ - iosock->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->flags |= IOSOCKETFLAG_PENDING_DESTDNS; - iosocket_lookup_hostname(iosock, hostname, flags, 0); - break; - case 1: - /* valid address */ - break; - } - IODESYNCHRONIZE(iosocket_dns_sync); - if((iosock->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_CONNECTING; - iodescriptor->callback = callback; - iosock->parent = iodescriptor; - iosock->flags |= IOSOCKETFLAG_PARENT_PUBLIC | IOSOCKETFLAG_LISTENING; - if(ssl) - iosock->flags |= IOSOCKETFLAG_SSLSOCKET; - - IOSYNCHRONIZE(iosocket_dns_sync); - switch(iosocket_parse_address(hostname, &iosock->bind.addr, flags)) { - case -1: - free(iosock); - return NULL; - case 0: - /* start dns lookup */ - iosock->flags |= IOSOCKETFLAG_PENDING_BINDDNS; - iosocket_lookup_hostname(iosock, hostname, flags, 1); - break; - case 1: - /* valid address */ - break; - } - IODESYNCHRONIZE(iosocket_dns_sync); - if((iosock->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) { - //TODO: SSL - return NULL; -} - -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 NULL; - } - - iosock->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->sslnode) { - //TODO: SSL - } - close(iosock->fd); - _free_socket(iosock); - iosocket->iosocket = NULL; - iosocket->status = IOSOCKET_CLOSED; - iogc_add(iosocket); -} - -static int iohandler_try_write(struct _IOSocket *iosock) { - if(!iosock->writebuf.bufpos) return 0; - iolog_trigger(IOLOG_DEBUG, "write writebuf (%d bytes) to socket (fd: %d)", iosock->writebuf.bufpos, iosock->fd); - int res; - if(iosock->sslnode) { - /* res = iohandler_ssl_write(iofd, iofd->writebuf.buffer, iofd->writebuf.bufpos); */ - // TODO - } 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->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->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->flags & IOSOCKETFLAG_ACTIVE)) - engine->update(iosock); -} - -void iosocket_write(struct IOSocket *iosocket, const char *line) { - size_t linelen = strlen(line); - iohandler_send(iosocket, line, linelen); -} - -void iosocket_printf(struct IOSocket *iosocket, const char *text, ...) { - va_list arg_list; - char sendBuf[IOSOCKET_LINE_LEN]; - int pos; - sendBuf[0] = '\0'; - va_start(arg_list, text); - pos = vsnprintf(sendBuf, IOSOCKET_LINE_LEN - 2, text, arg_list); - va_end(arg_list); - if (pos < 0 || pos > (IOSOCKET_LINE_LEN - 2)) pos = IOSOCKET_LINE_LEN - 2; - sendBuf[pos] = '\n'; - sendBuf[pos+1] = '\0'; - iohandler_send(iosocket, sendBuf, pos+1); -} - - - - - diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..58909b1 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,3 @@ +##Process this file with automake to create Makefile.in +ACLOCAL_AMFLAGS = -I m4 +SUBDIRS = IOHandler IOHandler_test diff --git a/src/compat/utime.c b/src/compat/utime.c deleted file mode 100644 index 2f919fc..0000000 --- a/src/compat/utime.c +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef HAVE_USLEEP - -#ifdef HAVE_SELECT - -#ifdef HAVE_SYS_SELECT_H -# include -#else -# include -# include -# include -#endif - -void usleep(long usec) -{ - struct timeval tv; - - tv.tv_sec = usec / 1000000; - tv.tv_usec = usec % 1000000; - select(0, NULL, NULL, NULL, &tv); -} - -#elif defined WIN32 - -/* usleep implementation from FreeSCI */ - -#include - -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 - -#endif /* !HAVE_USLEEP */ \ No newline at end of file diff --git a/src/test/socket/Makefile b/src/test/socket/Makefile deleted file mode 100644 index 724e34b..0000000 --- a/src/test/socket/Makefile +++ /dev/null @@ -1,12 +0,0 @@ - -CC = gcc -CFLAGS = -g -O0 -Wall -Wshadow -Werror -DHAVE_PTHREAD_H -LDFLAGS = -lws2_32 -lpthread - -OBJ = ../../IOEngine_epoll.o ../../IOEngine_kevent.o ../../IOEngine_select.o ../../IOEngine_win32.o ../../IOHandler.o ../../IOHandler_SSL.o iotest.o - -all: $(OBJ) - $(CC) $(CFLAGS) -oiotest $(OBJ) $(LDFLAGS) - -%.o: %.c - $(CC) $(CFLAGS) -o$@ -c $< diff --git a/src/test/timer/Makefile b/src/test/timer/Makefile deleted file mode 100644 index 724e34b..0000000 --- a/src/test/timer/Makefile +++ /dev/null @@ -1,12 +0,0 @@ - -CC = gcc -CFLAGS = -g -O0 -Wall -Wshadow -Werror -DHAVE_PTHREAD_H -LDFLAGS = -lws2_32 -lpthread - -OBJ = ../../IOEngine_epoll.o ../../IOEngine_kevent.o ../../IOEngine_select.o ../../IOEngine_win32.o ../../IOHandler.o ../../IOHandler_SSL.o iotest.o - -all: $(OBJ) - $(CC) $(CFLAGS) -oiotest $(OBJ) $(LDFLAGS) - -%.o: %.c - $(CC) $(CFLAGS) -o$@ -c $< diff --git a/src/test/timer/iotest.c b/src/test/timer/iotest.c deleted file mode 100644 index 17a91ec..0000000 --- a/src/test/timer/iotest.c +++ /dev/null @@ -1,81 +0,0 @@ -/* 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 . - */ - -#include -#include "../../IOHandler.h" - -#define TEST_DURATION 100 - -static IOHANDLER_CALLBACK(io_callback); -static IOHANDLER_LOG_BACKEND(io_log); - -static struct timeval test_clock1, test_clock2; -static int timercount; - -void add_timer(int ms) { - struct timeval timeout; - gettimeofday(&timeout, NULL); - timeout.tv_usec += (ms % 1000) * 1000; - timeout.tv_sec += (ms / 1000); - if(timeout.tv_usec > 1000000) { - timeout.tv_usec -= 1000000; - timeout.tv_sec++; - } - iohandler_timer(timeout, io_callback); -} - -int main(int argc, char *argv[]) { - iolog_backend = io_log; - - iohandler_set(IOHANDLER_SETTING_HIGH_PRECISION_TIMER, 1); - - gettimeofday(&test_clock1, NULL); - gettimeofday(&test_clock2, NULL); - //add_timer(TEST_DURATION); - iohandler_constant_timer(TEST_DURATION, io_callback); - timercount = 0; - - printf("[timer 0] %ld.%ld\n", test_clock1.tv_sec, test_clock1.tv_usec); - - while(1) { - iohandler_poll(); - } -} - -static IOHANDLER_CALLBACK(io_callback) { - struct timeval curr_time; - int diff1; - double diff2; - switch(event->type) { - case IOEVENT_TIMEOUT: - //add_timer(TEST_DURATION); - 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); - break; - default: - break; - } -} - -static IOHANDLER_LOG_BACKEND(io_log) { - //printf("%s", line); -} -- 2.20.1