From: pk910 Date: Sun, 2 Mar 2014 20:50:24 +0000 (+0100) Subject: [IOMultiplexerV2] alpha X-Git-Url: http://git.pk910.de/?p=NextIRCd.git;a=commitdiff_plain;h=9e6045c7b6d7afc774eeb59fa5f5c4e02fe1f037;hp=e69f0fb3c41956a73f05dcaa43fd66d63e5b748b [IOMultiplexerV2] alpha --- 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/IODNSEngine_cares.c b/src/IODNSEngine_cares.c deleted file mode 100644 index 0852470..0000000 --- a/src/IODNSEngine_cares.c +++ /dev/null @@ -1,50 +0,0 @@ -/* IODNSEngine_cares.c - IOMultiplexer - * Copyright (C) 2014 Philipp Kreil (pk910) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#define _IOHandler_internals -#include "IOInternal.h" -#include "IOHandler.h" -#include "IODNSHandler.h" - -static int dnsengine_cares_init() { - /* TODO */ - return 0; -} - -static void dnsengine_cares_stop() { - /* TODO */ -} - -static void dnsengine_cares_add(struct _IODNSQuery *iodns) { - /* TODO */ -} - -static void dnsengine_cares_remove(struct _IODNSQuery *iodns) { - /* TODO */ -} - -static void dnsengine_cares_loop() { - /* TODO */ -} - -struct IODNSEngine dnsengine_cares = { - .name = "c-ares", - .init = dnsengine_cares_init, - .stop = dnsengine_cares_stop, - .add = dnsengine_cares_add, - .remove = dnsengine_cares_remove, - .loop = dnsengine_cares_loop, -}; diff --git a/src/IODNSEngine_default.c b/src/IODNSEngine_default.c deleted file mode 100644 index 07e46c6..0000000 --- a/src/IODNSEngine_default.c +++ /dev/null @@ -1,182 +0,0 @@ -/* IODNSEngine_default.c - IOMultiplexer - * Copyright (C) 2014 Philipp Kreil (pk910) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#define _IOHandler_internals -#include "IOInternal.h" -#include "IOHandler.h" -#include "IODNSHandler.h" - -#ifdef HAVE_PTHREAD_H -static pthread_t *iodns_thread; -static int iodns_thread_running = 1; - -static pthread_cond_t iodns_cond; -static pthread_mutex_t iodns_sync2; -#endif -static int iodns_loop_blocking = 0; - -static void iodns_process_queries(); - -static void dnsengine_worker_main(void *arg) { - struct _IODNSQuery *query; - while(iodns_thread_running) { - IOSYNCHRONIZE(iodns_sync); - for(query = iodnsquery_first; query; query = query->next) { - if((query->flags & IODNSFLAG_RUNNING)) - break; - } - IODESYNCHRONIZE(iodns_sync); - if(!query) - pthread_cond_wait(&iodns_cond, &iodns_sync2); - - if(iodns_thread_running) - iodns_process_queries(); - } -} - -static int dnsengine_default_init() { - #ifdef HAVE_PTHREAD_H - /* create worker thread */ - pthread_cond_init(&iodns_cond, NULL); - IOTHREAD_MUTEX_INIT(iodns_sync2); - - iodns_thread_running = 1; - - int thread_err; - thread_err = pthread_create(&iodns_thread, NULL, dnsengine_worker_main, NULL); - 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 - iodns_loop_blocking = 1; - #endif - return 1; -} - -static void dnsengine_default_stop() { - #ifdef HAVE_PTHREAD_H - if(iodns_thread_running) { - iodns_thread_running = 0; - IOSYNCHRONIZE(iodns_sync2); - pthread_cond_signal(&iodns_cond); - IODESYNCHRONIZE(iodns_sync2); - pthread_join(iodns_thread, NULL); - } - #endif -} - -static void dnsengine_default_add(struct _IODNSQuery *iodns) { - #ifdef HAVE_PTHREAD_H - if(iodns_thread_running) { - IOSYNCHRONIZE(iodns_sync2); - pthread_cond_signal(&iodns_cond); - IODESYNCHRONIZE(iodns_sync2); - } - #endif -} - -static void dnsengine_default_remove(struct _IODNSQuery *iodns) { - /* unused */ -} - -static void dnsengine_default_loop() { - if(iodns_loop_blocking) - iodns_process_queries(); -} - -static void iodns_process_queries() { - enum IODNSEventType querystate; - struct addrinfo hints, *res, *next_res; - struct _IODNSQuery *iodns, *next_iodns; - struct IODNSResult *dnsresult; - iodns_process_queries_start: - IOSYNCHRONIZE(iodns_sync); - for(iodns = first_dnsquery; iodns; iodns = next_iodns) { - next_iodns = iodns->next; - - if(!(iodns->flags & IODNSFLAG_RUNNING)) - continue; - if((iodns->flags & IODNSFLAG_PROCESSING)) - continue; - - IODESYNCHRONIZE(iodns_sync); - - querystate = IODNSEVENT_FAILED; - - if((iodns->type & IODNS_FORWARD)) { - memset (&hints, 0, sizeof (hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags |= AI_CANONNAME; - if (!getaddrinfo(iodns->request.host, NULL, &hints, &res)) { - while (res) { - switch (res->ai_family) { - case AF_INET: - if((iodns->type & IODNS_RECORD_A)) { - dnsresult = malloc(sizeof(*dnsresult)); - dnsresult->type = IODNS_RECORD_A; - dnsresult->result.addr.addresslen = res->ai_addrlen; - dnsresult->result.addr.address = malloc(dnsresult->addresslen); - memcpy(dnsresult->address, res->ai_addr, dnsresult->addresslen); - dnsresult->next = iodns->result; - iodns->result = dnsresult; - querystate = IODNSEVENT_SUCCESS; - } - break; - case AF_INET6: - if((iodns->type & IODNS_RECORD_AAAA)) { - dnsresult = malloc(sizeof(*dnsresult)); - dnsresult->type = IODNS_RECORD_AAAA; - dnsresult->result.addr.addresslen = res->ai_addrlen; - dnsresult->result.addr.address = malloc(dnsresult->addresslen); - memcpy(dnsresult->address, res->ai_addr, dnsresult->addresslen); - dnsresult->next = iodns->result; - iodns->result = dnsresult; - querystate = IODNSEVENT_SUCCESS; - } - break; - } - next_res = res->ai_next; - freeaddrinfo(res); - res = next_res; - } - } - } - IOSYNCHRONIZE(iodns_sync); - if(!(iodns->flags & IODNSFLAG_RUNNING)) { - iodns_free_result(iodns->result); - _free_dnsquery(iodns); - IODESYNCHRONIZE(iodns_sync); - goto iodns_process_queries_start; - } - iodns->flags &= ~(IODNSFLAG_PROCESSING | IODNSFLAG_RUNNING); - IODESYNCHRONIZE(iodns_sync); - iodns_event_callback(iodns, querystate); - goto iodns_process_queries_start; - } -} - -struct IODNSEngine dnsengine_default = { - .name = "default", - .init = dnsengine_default_init, - .stop = dnsengine_default_stop, - .add = dnsengine_default_add, - .remove = dnsengine_default_remove, - .loop = dnsengine_default_loop, -}; diff --git a/src/IODNSLookup.c b/src/IODNSLookup.c deleted file mode 100644 index 5497553..0000000 --- a/src/IODNSLookup.c +++ /dev/null @@ -1,234 +0,0 @@ -/* IODNSLookup.c - IOMultiplexer - * Copyright (C) 2014 Philipp Kreil (pk910) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#define _IOHandler_internals -#include "IOInternal.h" -#include "IOHandler.h" -#include "IODNSLookup.h" -#include "IOLog.h" -#include "IOSockets.h" - -#ifdef HAVE_PTHREAD_H -pthread_mutex_t iodns_sync; -#endif - -struct _IODNSQuery *iodnsquery_first = NULL; -struct _IODNSQuery *iodnsquery_last = NULL; - -struct IODNSEngine *dnsengine = NULL; - -static void iodns_init_engine() { - if(dnsengine) - return; - //try DNS engines - if(dnsengine_cares.init && dnsengine_cares.init()) - dnsengine = &dnsengine_cares; - else if(dnsengine_default.init && dnsengine_default.init()) - dnsengine = &dnsengine_default; - else { - iohandler_log(IOLOG_FATAL, "found no useable IO DNS engine"); - return; - } - iohandler_log(IOLOG_DEBUG, "using %s IODNS engine", dnsengine->name); -} - -void _init_iodns() { - IOTHREAD_MUTEX_INIT(iodns_sync); - iodns_init_engine(); -} - -struct _IODNSQuery *_create_dnsquery() { - struct _IODNSQuery *query = calloc(1, sizeof(*query)); - if(!query) { - iolog_trigger(IOLOG_ERROR, "could not allocate memory for _IODNSQuery in %s:%d", __FILE__, __LINE__); - return NULL; - } - 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 - iodnsquery_first = query->next; - if(query->next) - 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) { - if((query->flags & IODNSFLAG_PARENT_PUBLIC)) { - struct IODNSQuery *descriptor = query->parent; - struct IODNSEvent event; - event.type = state; - event.query = descriptor; - event.result = query->result; - - descriptor->parent = NULL; - _stop_dnsquery(query); - - if(descriptor->callback) - descriptor->callback(&event); - - iogc_add(descriptor); - } else if((query->flags & IODNSFLAG_PARENT_SOCKET)) { - struct IODNSEvent event; - event.type = state; - event.query = NULL; - event.result = query->result - void *parent = query->parent; - - _stop_dnsquery(query); - iosocket_lookup_callback(parent, &event); - - } -} - -void iodns_poll() { - if(dnsengine) - dnsengine.loop(); -} - -/* public functions */ - -struct IODNSQuery *iodns_getaddrinfo(char *hostname, int records, iodns_callback *callback) { - if(!(records & IODNS_FORWARD) || !hostname || !callback) - return NULL; - - struct IODNSQuery *descriptor = calloc(1, sizeof(*descriptor)); - if(!descriptor) { - iolog_trigger(IOLOG_ERROR, "could not allocate memory for IODNSQuery in %s:%d", __FILE__, __LINE__); - return NULL; - } - - struct _IODNSQuery *query = _create_dnsquery(); - if(!query) { - free(descriptor); - return NULL - } - - query->parent = descriptor; - query->flags |= IODNSFLAG_PARENT_PUBLIC; - descriptor->query = query; - - query->request.host = strdup(hostname); - query->type = (records & IODNS_FORWARD); - - descriptor->callback = callback; - - _start_dnsquery(query); - return descriptor; -} - -struct IODNSQuery *iodns_getnameinfo(const struct sockaddr *addr, socklen_t addrlen, iodns_callback *callback) { - if(!addr || !callback) - return NULL; - - struct IODNSQuery *descriptor = calloc(1, sizeof(*descriptor)); - if(!descriptor) { - iolog_trigger(IOLOG_ERROR, "could not allocate memory for IODNSQuery in %s:%d", __FILE__, __LINE__); - return NULL; - } - - struct _IODNSQuery *query = _create_dnsquery(); - if(!query) { - free(descriptor); - return NULL - } - - query->parent = descriptor; - query->flags |= IODNSFLAG_PARENT_PUBLIC; - descriptor->query = query; - - query->type = IODNS_RECORD_PTR; - query->request.addr.addresslen = addrlen; - query->request.addr.address = malloc(addrlen); - if(!query->request.addr.address) { - iolog_trigger(IOLOG_ERROR, "could not allocate memory for sockaddr in %s:%d", __FILE__, __LINE__); - _free_dnsquery(query); - free(descriptor); - return NULL; - } - memcpy(query->request.addr.address, addr, addrlen); - - descriptor->callback = callback; - - _start_dnsquery(query); - return descriptor; -} - -void iodns_abort(struct IODNSQuery *descriptor) { - if(!descriptor) - return; - - struct _IODNSQuery *query = descriptor->query; - if(!query) { - iolog_trigger(IOLOG_WARNING, "called iodns_abort for destroyed IODNSQuery in %s:%d", __FILE__, __LINE__); - return; - } - - _stop_dnsquery(query) -} - -void iodns_free_result(struct IODNSResult *result) { - struct IODNSResult *next; - for(;result;result = next) { - next = result->next; - - if((result->type & IODNS_FORWARD)) { - if(result->result.addr.address) - free(result->result.addr.address); - } - if((result->type & IODNS_REVERSE)) { - if(result->result.host) - free(result->result.host); - } - free(result); - } -} - diff --git a/src/IODNSLookup.h b/src/IODNSLookup.h deleted file mode 100644 index 4235297..0000000 --- a/src/IODNSLookup.h +++ /dev/null @@ -1,125 +0,0 @@ -/* IODNSLookup.h - IOMultiplexer v2 - * Copyright (C) 2014 Philipp Kreil (pk910) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#ifndef _IODNSLookup_h -#define _IODNSLookup_h -#ifndef _IOHandler_internals -#include "IOHandler.h" -#else - -extern struct IODNSEngine dnsengine_cares; -extern struct IODNSEngine dnsengine_default; - -#ifdef HAVE_PTHREAD_H -extern pthread_mutex_t iodns_sync; -#endif - -struct IODNSAddress; - -#define IODNSFLAG_RUNNING 0x01 -#define IODNSFLAG_PROCESSING 0x02 -#define IODNSFLAG_PARENT_PUBLIC 0x04 -#define IODNSFLAG_PARENT_SOCKET 0x08 - -struct _IODNSQuery { - void *query; - - unsigned int flags : 8; - unsigned int type : 8; - union { - struct IODNSAddress addr; - char *host; - } request; - - struct IODNSResult *result; - - void *parent; - - struct _IODNSQuery *next, *prev; -}; - -struct IODNSEngine { - const char *name; - int (*init)(); - void (*stop)(); - void (*add)(struct _IODNSQuery *query); - void (*remove)(struct _IODNSQuery *query); - void (*loop)(); -}; - -void _init_iodns(); -void _stop_iodns(); -struct _IODNSQuery *_create_dnsquery(); -void _start_dnsquery(struct _IODNSQuery *query); -void _stop_dnsquery(struct _IODNSQuery *query); - -/* call only from engines! */ -void _free_dnsquery(struct _IODNSQuery *query) -void iodns_event_callback(struct _IODNSQuery *query, enum IODNSEventType state); - -void iodns_poll(); - -#endif - -#define IODNS_CALLBACK(NAME) void NAME(struct IODNSEvent *event) -typedef IODNS_CALLBACK(iodns_callback); - -enum IODNSEventType { - IODNSEVENT_SUCCESS, - IODNSEVENT_FAILED -}; - -#define IODNS_RECORD_A 0x01 -#define IODNS_RECORD_AAAA 0x02 -#define IODNS_RECORD_PTR 0x04 - -#define IODNS_FORWARD 0x03 -#define IODNS_REVERSE 0x04 - -struct IODNSAddress { - size_t addresslen; - struct sockaddr *address; -}; - -struct IODNSQuery { - void *query; - - iodns_callback *callback; - void *data; -}; - -struct IODNSResult { - unsigned int type : 8; - union { - struct IODNSAddress addr; - char *host; - } result; - struct IODNSResult *next; -}; - -struct IODNSEvent { - enum IODNSEventType type; - struct IODNSQuery *query; - struct IODNSResult *result; -}; - -struct IODNSQuery *iodns_getaddrinfo(char *hostname, int records, iodns_callback *callback); -struct IODNSQuery *iodns_getnameinfo(const struct sockaddr *addr, socklen_t addrlen, iodns_callback *callback); -void iodns_abort(struct IODNSQuery *query); - -void iodns_free_result(struct IODNSResult *result); - -#endif diff --git a/src/IOGarbageCollector.c b/src/IOGarbageCollector.c deleted file mode 100644 index 363d816..0000000 --- a/src/IOGarbageCollector.c +++ /dev/null @@ -1,100 +0,0 @@ -/* IOGarbageCollector.c - IOMultiplexer v2 - * Copyright (C) 2014 Philipp Kreil (pk910) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#define _IOHandler_internals -#include "IOInternal.h" -#include "IOHandler.h" -#include "IOGarbageCollector.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)) - -static struct IOGCObject { - void *object; - iogc_free *free_callback; - struct timeval timeout; - - 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; -} - - -void iohandler_set_gc(int enabled) { - if(enabled) - iogc_enabled = 1; - else - iogc_enabled = 0; -} - -void iogc_add(void *object) { - iogc_add_callback(object, NULL); -} - -void iogc_add_callback(void *object, iogc_free *free_callback) { - if(!iogc_enabled) { - if(free_callback) - free_callback(object); - else - free(object); - return; - } - struct IOGCObject *obj = malloc(sizeof(*obj)); - if(!obj) { - iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOGCObject in %s:%d", __FILE__, __LINE__); - if(free_callback) - free_callback(object); - else - free(object); - return; - } - obj->object = object; - obj->free_callback = free_callback; - gettimeofday(&obj->timeout, NULL); - -} - -void iogc_exec() { - struct timeval ctime; - gettimeofday(&ctime, NULL); - - struct IOGCObject *obj, *next_obj; - for(obj = objects; obj; obj = next_obj) { - if(timeval_is_smaler(obj->timeout, ctime)) { - next_obj = obj->next; - if(obj->free_callback) - obj->free_callback(obj->object); - else - free(obj->object); - free(obj); - } else { - objects = obj; - break; - } - } -} diff --git a/src/IOGarbageCollector.h b/src/IOGarbageCollector.h deleted file mode 100644 index d1e848b..0000000 --- a/src/IOGarbageCollector.h +++ /dev/null @@ -1,27 +0,0 @@ -/* IOGarbageCollector.h - IOMultiplexer v2 - * Copyright (C) 2014 Philipp Kreil (pk910) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#ifndef _IOGarbageCollector_h -#define _IOGarbageCollector_h -#ifndef _IOHandler_internals -#include "IOHandler.h" -#else - -void iogc_init(); -void iogc_exec(); - -#endif -#endif diff --git a/src/IOHandler.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.h b/src/IOHandler.h deleted file mode 100644 index 3cba91d..0000000 --- a/src/IOHandler.h +++ /dev/null @@ -1,30 +0,0 @@ -/* IOHandler.h - IOMultiplexer v2 - * Copyright (C) 2014 Philipp Kreil (pk910) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#ifndef _IOHandler_h -#define _IOHandler_h -#ifdef _IOHandler_internals - -#endif - -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/.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/IOHandler/IODNSEngine_cares.c b/src/IOHandler/IODNSEngine_cares.c new file mode 100644 index 0000000..2bbce18 --- /dev/null +++ b/src/IOHandler/IODNSEngine_cares.c @@ -0,0 +1,50 @@ +/* IODNSEngine_cares.c - IOMultiplexer + * Copyright (C) 2014 Philipp Kreil (pk910) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#define _IOHandler_internals +#include "IOInternal.h" +#include "IOHandler.h" +#include "IODNSLookup.h" + +static int dnsengine_cares_init() { + /* TODO */ + return 0; +} + +static void dnsengine_cares_stop() { + /* TODO */ +} + +static void dnsengine_cares_add(struct _IODNSQuery *iodns) { + /* TODO */ +} + +static void dnsengine_cares_remove(struct _IODNSQuery *iodns) { + /* TODO */ +} + +static void dnsengine_cares_loop() { + /* TODO */ +} + +struct IODNSEngine dnsengine_cares = { + .name = "c-ares", + .init = dnsengine_cares_init, + .stop = dnsengine_cares_stop, + .add = dnsengine_cares_add, + .remove = dnsengine_cares_remove, + .loop = dnsengine_cares_loop, +}; diff --git a/src/IOHandler/IODNSEngine_default.c b/src/IOHandler/IODNSEngine_default.c new file mode 100644 index 0000000..43f6b12 --- /dev/null +++ b/src/IOHandler/IODNSEngine_default.c @@ -0,0 +1,214 @@ +/* IODNSEngine_default.c - IOMultiplexer + * Copyright (C) 2014 Philipp Kreil (pk910) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#define _IOHandler_internals +#include "IOInternal.h" +#include "IOHandler.h" +#include "IOLog.h" +#include "IODNSLookup.h" + +#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_sync, iodns_sync2; +#endif +static int iodns_loop_blocking = 0; + +static void iodns_process_queries(); + +#ifdef IODNS_USE_THREADS +static void *dnsengine_worker_main(void *arg) { + struct _IODNSQuery *query; + while(iodns_thread_running) { + IOSYNCHRONIZE(iodns_sync); + for(query = iodnsquery_first; query; query = query->next) { + if((query->flags & IODNSFLAG_RUNNING)) + break; + } + IODESYNCHRONIZE(iodns_sync); + if(!query) + pthread_cond_wait(&iodns_cond, &iodns_sync2); + + if(iodns_thread_running) + iodns_process_queries(); + } + return NULL; +} +#endif + +static int dnsengine_default_init() { + #ifdef IODNS_USE_THREADS + /* create worker thread */ + pthread_cond_init(&iodns_cond, NULL); + IOTHREAD_MUTEX_INIT(iodns_sync); + IOTHREAD_MUTEX_INIT(iodns_sync2); + + iodns_thread_running = 1; + + int thread_err; + thread_err = pthread_create(&iodns_thread, NULL, dnsengine_worker_main, NULL); + 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_running = 0; + } + #else + iodns_loop_blocking = 1; + #endif + return 1; +} + +static void dnsengine_default_stop() { + #ifdef IODNS_USE_THREADS + if(iodns_thread_running) { + iodns_thread_running = 0; + IOSYNCHRONIZE(iodns_sync2); + pthread_cond_signal(&iodns_cond); + IODESYNCHRONIZE(iodns_sync2); + pthread_join(iodns_thread, NULL); + } + #endif +} + +static void dnsengine_default_add(struct _IODNSQuery *iodns) { + #ifdef IODNS_USE_THREADS + if(iodns_thread_running) { + IOSYNCHRONIZE(iodns_sync2); + pthread_cond_signal(&iodns_cond); + IODESYNCHRONIZE(iodns_sync2); + } + #endif +} + +static void dnsengine_default_remove(struct _IODNSQuery *iodns) { + /* unused */ +} + +static void dnsengine_default_loop() { + if(iodns_loop_blocking) + iodns_process_queries(); +} + +static void iodns_process_queries() { + enum IODNSEventType querystate; + struct addrinfo hints, *res, *allres; + struct _IODNSQuery *iodns, *next_iodns; + struct IODNSResult *dnsresult; + iodns_process_queries_start: + IOSYNCHRONIZE(iodns_sync); + for(iodns = iodnsquery_first; iodns; iodns = next_iodns) { + next_iodns = iodns->next; + + if(!(iodns->flags & IODNSFLAG_RUNNING)) + continue; + if((iodns->flags & IODNSFLAG_PROCESSING)) + continue; + + IODESYNCHRONIZE(iodns_sync); + + querystate = IODNSEVENT_FAILED; + + if((iodns->type & IODNS_FORWARD)) { + memset (&hints, 0, sizeof (hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags |= AI_CANONNAME; + int ret; + if (!(ret = getaddrinfo(iodns->request.host, NULL, &hints, &allres))) { + res = allres; + while (res) { + switch (res->ai_family) { + case AF_INET: + if((iodns->type & IODNS_RECORD_A)) { + dnsresult = malloc(sizeof(*dnsresult)); + dnsresult->type = IODNS_RECORD_A; + dnsresult->result.addr.addresslen = res->ai_addrlen; + dnsresult->result.addr.address = 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; + case AF_INET6: + if((iodns->type & IODNS_RECORD_AAAA)) { + dnsresult = malloc(sizeof(*dnsresult)); + dnsresult->type = IODNS_RECORD_AAAA; + dnsresult->result.addr.addresslen = res->ai_addrlen; + dnsresult->result.addr.address = calloc(dnsresult->result.addr.addresslen, 1); + memcpy(dnsresult->result.addr.address, res->ai_addr, dnsresult->result.addr.addresslen); + dnsresult->next = iodns->result; + iodns->result = dnsresult; + + char str[INET6_ADDRSTRLEN]; + inet_ntop( AF_INET6, &((struct sockaddr_in6 *)dnsresult->result.addr.address)->sin6_addr, str, INET6_ADDRSTRLEN ); + iolog_trigger(IOLOG_DEBUG, "Resolved %s to (AAAA): %s", iodns->request.host, str); + + querystate = IODNSEVENT_SUCCESS; + } + break; + } + res = res->ai_next; + } + freeaddrinfo(allres); + } else { + iolog_trigger(IOLOG_WARNING, "getaddrinfo returned error code: %d", ret); + } + } + IOSYNCHRONIZE(iodns_sync); + if(!(iodns->flags & IODNSFLAG_RUNNING)) { + iodns_free_result(iodns->result); + _free_dnsquery(iodns); + IODESYNCHRONIZE(iodns_sync); + goto iodns_process_queries_start; + } + iodns->flags &= ~(IODNSFLAG_PROCESSING | IODNSFLAG_RUNNING); + IODESYNCHRONIZE(iodns_sync); + iodns_event_callback(iodns, querystate); + goto iodns_process_queries_start; + } +} + +struct IODNSEngine dnsengine_default = { + .name = "default", + .init = dnsengine_default_init, + .stop = dnsengine_default_stop, + .add = dnsengine_default_add, + .remove = dnsengine_default_remove, + .loop = dnsengine_default_loop, +}; diff --git a/src/IOHandler/IODNSLookup.c b/src/IOHandler/IODNSLookup.c new file mode 100644 index 0000000..e39a9cf --- /dev/null +++ b/src/IOHandler/IODNSLookup.c @@ -0,0 +1,223 @@ +/* IODNSLookup.c - IOMultiplexer + * Copyright (C) 2014 Philipp Kreil (pk910) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#define _IOHandler_internals +#include "IOInternal.h" +#include "IOHandler.h" +#include "IODNSLookup.h" +#include "IOLog.h" +#include "IOSockets.h" + +#include + +struct _IODNSQuery *iodnsquery_first = NULL; +struct _IODNSQuery *iodnsquery_last = NULL; + +struct IODNSEngine *dnsengine = NULL; + +static void iodns_init_engine() { + if(dnsengine) + return; + //try DNS engines + if(dnsengine_cares.init && dnsengine_cares.init()) + dnsengine = &dnsengine_cares; + else if(dnsengine_default.init && dnsengine_default.init()) + dnsengine = &dnsengine_default; + else { + iolog_trigger(IOLOG_FATAL, "found no useable IO DNS engine"); + return; + } + iolog_trigger(IOLOG_DEBUG, "using %s IODNS engine", dnsengine->name); +} + +void _init_iodns() { + iodns_init_engine(); +} + +struct _IODNSQuery *_create_dnsquery() { + struct _IODNSQuery *query = calloc(1, sizeof(*query)); + if(!query) { + iolog_trigger(IOLOG_ERROR, "could not allocate memory for _IODNSQuery in %s:%d", __FILE__, __LINE__); + return NULL; + } + if(iodnsquery_last) + iodnsquery_last->next = query; + else + iodnsquery_first = query; + query->prev = iodnsquery_last; + iodnsquery_last = query; + return query; +} + +void _start_dnsquery(struct _IODNSQuery *query) { + query->flags |= IODNSFLAG_RUNNING; + dnsengine->add(query); +} + +void _free_dnsquery(struct _IODNSQuery *query) { + if(query->prev) + query->prev->next = query->next; + else + iodnsquery_first = query->next; + if(query->next) + query->next->prev = query->prev; + else + iodnsquery_last = query->prev; + if((query->type & IODNS_REVERSE) && query->request.addr.address) + free(query->request.addr.address); + free(query); +} + +void _stop_dnsquery(struct _IODNSQuery *query) { + if((query->flags & IODNSFLAG_RUNNING)) { + query->flags &= ~IODNSFLAG_RUNNING; + dnsengine->remove(query); + } + if(!(query->flags & IODNSFLAG_PROCESSING)) + _free_dnsquery(query); +} + +void iodns_event_callback(struct _IODNSQuery *query, enum IODNSEventType state) { + if((query->flags & IODNSFLAG_PARENT_PUBLIC)) { + struct IODNSQuery *descriptor = query->parent; + struct IODNSEvent event; + event.type = state; + event.query = descriptor; + event.result = query->result; + + descriptor->query = NULL; + _stop_dnsquery(query); + + if(descriptor->callback) + descriptor->callback(&event); + + iogc_add(descriptor); + } else if((query->flags & IODNSFLAG_PARENT_SOCKET)) { + struct IODNSEvent event; + event.type = state; + event.query = NULL; + event.result = query->result; + void *parent = query->parent; + + _stop_dnsquery(query); + iosocket_lookup_callback(parent, &event); + + } +} + +void iodns_poll() { + if(dnsengine) + dnsengine->loop(); +} + +/* public functions */ + +struct IODNSQuery *iodns_getaddrinfo(char *hostname, int records, iodns_callback *callback) { + if(!(records & IODNS_FORWARD) || !hostname || !callback) + return NULL; + + struct IODNSQuery *descriptor = calloc(1, sizeof(*descriptor)); + if(!descriptor) { + iolog_trigger(IOLOG_ERROR, "could not allocate memory for IODNSQuery in %s:%d", __FILE__, __LINE__); + return NULL; + } + + struct _IODNSQuery *query = _create_dnsquery(); + if(!query) { + free(descriptor); + return NULL; + } + + query->parent = descriptor; + query->flags |= IODNSFLAG_PARENT_PUBLIC; + descriptor->query = query; + + query->request.host = strdup(hostname); + query->type = (records & IODNS_FORWARD); + + descriptor->callback = callback; + + _start_dnsquery(query); + return descriptor; +} + +struct IODNSQuery *iodns_getnameinfo(const struct sockaddr *addr, size_t addrlen, iodns_callback *callback) { + if(!addr || !callback) + return NULL; + + struct IODNSQuery *descriptor = calloc(1, sizeof(*descriptor)); + if(!descriptor) { + iolog_trigger(IOLOG_ERROR, "could not allocate memory for IODNSQuery in %s:%d", __FILE__, __LINE__); + return NULL; + } + + struct _IODNSQuery *query = _create_dnsquery(); + if(!query) { + free(descriptor); + return NULL; + } + + query->parent = descriptor; + query->flags |= IODNSFLAG_PARENT_PUBLIC; + descriptor->query = query; + + query->type = IODNS_RECORD_PTR; + query->request.addr.addresslen = addrlen; + query->request.addr.address = malloc(addrlen); + if(!query->request.addr.address) { + iolog_trigger(IOLOG_ERROR, "could not allocate memory for sockaddr in %s:%d", __FILE__, __LINE__); + _free_dnsquery(query); + free(descriptor); + return NULL; + } + memcpy(query->request.addr.address, addr, addrlen); + + descriptor->callback = callback; + + _start_dnsquery(query); + return descriptor; +} + +void iodns_abort(struct IODNSQuery *descriptor) { + if(!descriptor) + return; + + struct _IODNSQuery *query = descriptor->query; + if(!query) { + iolog_trigger(IOLOG_WARNING, "called iodns_abort for destroyed IODNSQuery in %s:%d", __FILE__, __LINE__); + return; + } + + _stop_dnsquery(query); +} + +void iodns_free_result(struct IODNSResult *result) { + struct IODNSResult *next; + for(;result;result = next) { + next = result->next; + + if((result->type & IODNS_FORWARD)) { + if(result->result.addr.address) + free(result->result.addr.address); + } + if((result->type & IODNS_REVERSE)) { + if(result->result.host) + free(result->result.host); + } + free(result); + } +} + diff --git a/src/IOHandler/IODNSLookup.h b/src/IOHandler/IODNSLookup.h new file mode 100644 index 0000000..29574cb --- /dev/null +++ b/src/IOHandler/IODNSLookup.h @@ -0,0 +1,155 @@ +/* IODNSLookup.h - IOMultiplexer v2 + * Copyright (C) 2014 Philipp Kreil (pk910) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#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; + +struct _IODNSQuery; +extern struct _IODNSQuery *iodnsquery_first; +extern struct _IODNSQuery *iodnsquery_last; + +/* Multithreading */ +#ifdef IODNS_USE_THREADS +#ifndef HAVE_PTHREAD_H +#undef IODNS_USE_THREADS +#endif +#endif +#ifdef IODNS_USE_THREADS +#include +#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; + + unsigned int flags : 8; + unsigned int type : 8; + union { + struct IODNSAddress addr; + char *host; + } request; + + struct IODNSResult *result; + + void *parent; + + struct _IODNSQuery *next, *prev; +}; + +struct IODNSEngine { + const char *name; + int (*init)(); + void (*stop)(); + void (*add)(struct _IODNSQuery *query); + void (*remove)(struct _IODNSQuery *query); + void (*loop)(); +}; + +void _init_iodns(); +void _stop_iodns(); +struct _IODNSQuery *_create_dnsquery(); +void _start_dnsquery(struct _IODNSQuery *query); +void _stop_dnsquery(struct _IODNSQuery *query); + +/* call only from engines! */ +enum IODNSEventType; +void _free_dnsquery(struct _IODNSQuery *query); +void iodns_event_callback(struct _IODNSQuery *query, enum IODNSEventType state); + +void iodns_poll(); + +#endif + +struct IODNSEvent; +struct sockaddr; + +#define IODNS_CALLBACK(NAME) void NAME(struct IODNSEvent *event) +typedef IODNS_CALLBACK(iodns_callback); + +enum IODNSEventType { + IODNSEVENT_SUCCESS, + IODNSEVENT_FAILED +}; + +#define IODNS_RECORD_A 0x01 +#define IODNS_RECORD_AAAA 0x02 +#define IODNS_RECORD_PTR 0x04 + +#define IODNS_FORWARD 0x03 +#define IODNS_REVERSE 0x04 + +struct IODNSQuery { + void *query; + + iodns_callback *callback; + void *data; +}; + +struct IODNSResult { + unsigned int type : 8; + union { + struct IODNSAddress addr; + char *host; + } result; + struct IODNSResult *next; +}; + +struct IODNSEvent { + enum IODNSEventType type; + struct IODNSQuery *query; + struct IODNSResult *result; +}; + +struct IODNSQuery *iodns_getaddrinfo(char *hostname, int records, iodns_callback *callback); +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); + +#endif 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/IOHandler/IOGarbageCollector.c b/src/IOHandler/IOGarbageCollector.c new file mode 100644 index 0000000..d624396 --- /dev/null +++ b/src/IOHandler/IOGarbageCollector.c @@ -0,0 +1,102 @@ +/* IOGarbageCollector.c - IOMultiplexer v2 + * Copyright (C) 2014 Philipp Kreil (pk910) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#define _IOHandler_internals +#include "IOInternal.h" +#include "IOHandler.h" +#include "IOGarbageCollector.h" +#include "IOLog.h" + +#include +#include + +struct IOGCObject { + void *object; + iogc_free *free_callback; + struct timeval timeout; + + struct IOGCObject *next; +}; + +static int iogc_enabled = 1; +static struct timeval iogc_timeout; +static struct IOGCObject *first_object = NULL, *last_object = NULL; + +void iogc_init() { + iogc_timeout.tv_usec = 0; + iogc_timeout.tv_sec = 10; +} + + +void iohandler_set_gc(int enabled) { + if(enabled) + iogc_enabled = 1; + else + iogc_enabled = 0; +} + +void iogc_add(void *object) { + iogc_add_callback(object, NULL); +} + +void iogc_add_callback(void *object, iogc_free *free_callback) { + if(!iogc_enabled) { + if(free_callback) + free_callback(object); + else + free(object); + return; + } + struct IOGCObject *obj = malloc(sizeof(*obj)); + if(!obj) { + iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOGCObject in %s:%d", __FILE__, __LINE__); + if(free_callback) + free_callback(object); + else + free(object); + return; + } + obj->object = object; + obj->free_callback = free_callback; + gettimeofday(&obj->timeout, NULL); + obj->timeout.tv_sec += IOGC_TIMEOUT; + + obj->next = NULL; + if(last_object) + last_object->next = obj; + else + first_object = obj; + last_object = obj; +} + +void iogc_exec() { + struct timeval now; + gettimeofday(&now, NULL); + + struct IOGCObject *obj, *next_obj; + for(obj = first_object; obj; obj = next_obj) { + if(timeval_is_smaler(obj->timeout, now)) { + next_obj = obj->next; + if(obj->free_callback) + obj->free_callback(obj->object); + else + free(obj->object); + free(obj); + } else + break; + } + first_object = obj; +} diff --git a/src/IOHandler/IOGarbageCollector.h b/src/IOHandler/IOGarbageCollector.h new file mode 100644 index 0000000..d1e848b --- /dev/null +++ b/src/IOHandler/IOGarbageCollector.h @@ -0,0 +1,27 @@ +/* IOGarbageCollector.h - IOMultiplexer v2 + * Copyright (C) 2014 Philipp Kreil (pk910) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef _IOGarbageCollector_h +#define _IOGarbageCollector_h +#ifndef _IOHandler_internals +#include "IOHandler.h" +#else + +void iogc_init(); +void iogc_exec(); + +#endif +#endif diff --git a/src/IOHandler/IOHandler.c b/src/IOHandler/IOHandler.c new file mode 100644 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/IOHandler.h b/src/IOHandler/IOHandler.h new file mode 100644 index 0000000..1424eb5 --- /dev/null +++ b/src/IOHandler/IOHandler.h @@ -0,0 +1,30 @@ +/* IOHandler.h - IOMultiplexer v2 + * Copyright (C) 2014 Philipp Kreil (pk910) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef _IOHandler_h +#define _IOHandler_h +#include "IOHandler_config.h" +#ifdef _IOHandler_internals + +#endif + +void iohandler_init(); +void iohandler_run(); +void iohandler_stop(); + +void iohandler_set_gc(int enabled); /* default: enabled */ + +#endif diff --git a/src/IOHandler/IOHandler_config.h b/src/IOHandler/IOHandler_config.h new file mode 100644 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/IOHandler/IOInternal.h b/src/IOHandler/IOInternal.h new file mode 100644 index 0000000..e5dc207 --- /dev/null +++ b/src/IOHandler/IOInternal.h @@ -0,0 +1,32 @@ +/* IOInternal.h - IOMultiplexer v2 + * Copyright (C) 2014 Philipp Kreil (pk910) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef _IOInternal_h +#define _IOInternal_h +#ifndef _IOHandler_internals +#include "IOHandler.h" +#else + +#define timeval_is_bigger(x,y) ((x.tv_sec > y.tv_sec) || (x.tv_sec == y.tv_sec && x.tv_usec > y.tv_usec)) +#define timeval_is_smaler(x,y) ((x.tv_sec < y.tv_sec) || (x.tv_sec == y.tv_sec && x.tv_usec < y.tv_usec)) + +#define IOGC_FREE(NAME) void NAME(void *object) +typedef IOGC_FREE(iogc_free); +void iogc_add(void *object); +void iogc_add_callback(void *object, iogc_free *free_callback); + +#endif +#endif diff --git a/src/IOHandler/IOLog.c b/src/IOHandler/IOLog.c new file mode 100644 index 0000000..20637c1 --- /dev/null +++ b/src/IOHandler/IOLog.c @@ -0,0 +1,48 @@ +/* IOLog.c - IOMultiplexer v2 + * Copyright (C) 2014 Philipp Kreil (pk910) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#define _IOHandler_internals +#include "IOInternal.h" +#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]; + int pos; + logBuf[0] = '\0'; + va_start(arg_list, text); + pos = vsnprintf(logBuf, MAXLOG - 1, text, arg_list); + va_end(arg_list); + if (pos < 0 || pos > (MAXLOG - 1)) pos = MAXLOG - 1; + logBuf[pos] = '\n'; + logBuf[pos+1] = '\0'; + + printf("%s", logBuf); +} + +void iolog_register_callback(iolog_callback *callback) { + +} diff --git a/src/IOHandler/IOLog.h b/src/IOHandler/IOLog.h new file mode 100644 index 0000000..451155e --- /dev/null +++ b/src/IOHandler/IOLog.h @@ -0,0 +1,41 @@ +/* IOLog.h - IOMultiplexer v2 + * Copyright (C) 2014 Philipp Kreil (pk910) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef _IOLog_h +#define _IOLog_h +#ifndef _IOHandler_internals +#include "IOHandler.h" +#else +enum IOLogType; + +void iolog_init(); +void iolog_trigger(enum IOLogType type, char *text, ...); + +#endif + +enum IOLogType { + IOLOG_DEBUG, + IOLOG_WARNING, + IOLOG_ERROR, + IOLOG_FATAL +}; + +#define IOLOG_CALLBACK(NAME) void NAME(enum IOLogType type, char *message) +typedef IOLOG_CALLBACK(iolog_callback); + +void iolog_register_callback(iolog_callback *callback); + +#endif diff --git a/src/IOHandler/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/IOHandler/IOSockets.h b/src/IOHandler/IOSockets.h new file mode 100644 index 0000000..ab36225 --- /dev/null +++ b/src/IOHandler/IOSockets.h @@ -0,0 +1,186 @@ +/* IOSockets.h - IOMultiplexer v2 + * Copyright (C) 2014 Philipp Kreil (pk910) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#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 + +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 +#define IOSOCKETFLAG_PENDING_DESTDNS 0x0008 +#define IOSOCKETFLAG_DNSDONE_BINDDNS 0x0010 +#define IOSOCKETFLAG_DNSDONE_DESTDNS 0x0020 +#define IOSOCKETFLAG_DNSERROR 0x0040 +#define IOSOCKETFLAG_IPV6SOCKET 0x0080 +#define IOSOCKETFLAG_PARENT_PUBLIC 0x0100 +#define IOSOCKETFLAG_PARENT_DNSENGINE 0x0200 +#define IOSOCKETFLAG_SSLSOCKET 0x0400 /* use ssl after connecting */ +#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; + char hostname[256]; + struct _IOSocket *iosocket; + struct _IODNSQuery *query; + struct IODNSResult *result; +}; + +struct _IOSocket { + int fd; + + unsigned int socket_flags : 24; + + union { + struct IODNSAddress addr; + struct IOSocketDNSLookup *addrlookup; + } bind; + union { + struct IODNSAddress addr; + struct IOSocketDNSLookup *addrlookup; + } dest; + + unsigned int port : 16; + + struct IOSocketBuffer readbuf; + struct IOSocketBuffer writebuf; + + struct IOSSLDescriptor *sslnode; + + void *parent; + + struct _IOSocket *next, *prev; +}; + +void _init_sockets(); +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 + +struct IOSocketEvent; + +#define IOSOCKET_CALLBACK(NAME) void NAME(struct IOSocketEvent *event) +typedef IOSOCKET_CALLBACK(iosocket_callback); + +enum IOSocketStatus { + IOSOCKET_CLOSED, /* descriptor is dead (socket waiting for removal or timer) */ + IOSOCKET_LISTENING, /* descriptor is waiting for connections (server socket) */ + IOSOCKET_CONNECTING, /* descriptor is waiting for connection approval (connecting client socket) */ + IOSOCKET_CONNECTED /* descriptor is connected (connected client socket) */ +}; + +enum IOSocketEventType { + IOSOCKETEVENT_IGNORE, + IOSOCKETEVENT_RECV, /* client socket received something (read_lines == 1 => recv_str valid; read_lines == 0 => recv_buf valid) */ + IOSOCKETEVENT_CONNECTED, /* client socket connected successful */ + IOSOCKETEVENT_NOTCONNECTED, /* client socket could not connect (errid valid) */ + IOSOCKETEVENT_CLOSED, /* client socket lost connection (errid valid) */ + IOSOCKETEVENT_ACCEPT, /* server socket accepted new connection (accept_socket valid) */ + IOSOCKETEVENT_SSLFAILED, /* failed to initialize SSL session */ + IOSOCKETEVENT_DNSFAILED /* failed to lookup DNS information */ +}; + +struct IOSocket { + void *iosocket; + + enum IOSocketStatus status; + int listening : 1; + int ssl : 1; + int parse_delimiter : 1; + int parse_empty : 1; /* parse "empty" lines (only if parse_delimiter is set) */ + unsigned char delimiters[IOSOCKET_PARSE_DELIMITERS_COUNT]; + + void *data; + iosocket_callback *callback; +}; + +struct IOSocketEvent { + enum IOSocketEventType type; + struct IOSocket *socket; + union { + char *recv_str; + struct IOSocketBuffer *recv_buf; + int errid; + struct IOSocket *accept_socket; + } data; +}; + + +#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); +struct IOSocket *iosocket_listen(const char *hostname, unsigned int port, iosocket_callback *callback); +struct IOSocket *iosocket_listen_flags(const char *hostname, unsigned int port, iosocket_callback *callback, int flags); +struct IOSocket *iosocket_listen_ssl(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iosocket_callback *callback); +struct IOSocket *iosocket_listen_ssl_flags(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iosocket_callback *callback, int flags); +void iosocket_write(struct IOSocket *iosocket, const char *line); +void iosocket_send(struct IOSocket *iosocket, const char *data, size_t datalen); +void iosocket_printf(struct IOSocket *iosocket, const char *text, ...); +void iohandler_close(struct IOSocket *iosocket); + +#endif diff --git a/src/IOHandler/IOTimer.c b/src/IOHandler/IOTimer.c new file mode 100644 index 0000000..ec90d39 --- /dev/null +++ b/src/IOHandler/IOTimer.c @@ -0,0 +1,201 @@ +/* IOTimer.c - IOMultiplexer v2 + * Copyright (C) 2014 Philipp Kreil (pk910) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#define _IOHandler_internals +#include "IOInternal.h" +#include "IOHandler.h" +#include "IOTimer.h" +#include "IOLog.h" + +#include +#include + +static void _rearrange_timer(struct _IOTimerDescriptor *timer); +static void _autoreload_timer(struct _IOTimerDescriptor *timer); + +struct _IOTimerDescriptor *iotimer_sorted_descriptors; + +/* public functions */ + +struct IOTimerDescriptor *iotimer_create(struct timeval *timeout) { + struct IOTimerDescriptor *descriptor = calloc(1, sizeof(*descriptor)); + if(!descriptor) { + iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOTimerDescriptor in %s:%d", __FILE__, __LINE__); + return NULL; + } + struct _IOTimerDescriptor *timer = _create_timer(timeout); + if(!timer) { + free(descriptor); + return NULL; + } + timer->parent = descriptor; + timer->flags |= IOTIMERFLAG_PARENT_PUBLIC; + descriptor->iotimer = timer; + + return descriptor; +} + +void iotimer_start(struct IOTimerDescriptor *descriptor) { + struct _IOTimerDescriptor *timer = descriptor->iotimer; + if(timer == NULL) { + iolog_trigger(IOLOG_WARNING, "called iotimer_set_autoreload for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__); + return; + } + timer->flags |= IOTIMERFLAG_ACTIVE; + if(!(timer->flags & IOTIMERFLAG_IN_LIST)) + _trigger_timer(timer); +} + +void iotimer_set_autoreload(struct IOTimerDescriptor *descriptor, struct timeval *autoreload) { + struct _IOTimerDescriptor *timer = descriptor->iotimer; + if(timer == NULL) { + iolog_trigger(IOLOG_WARNING, "called iotimer_set_autoreload for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__); + return; + } + if(autoreload) { + timer->flags |= IOTIMERFLAG_PERIODIC; + timer->autoreload = *autoreload; + + if(!(timer->flags & IOTIMERFLAG_IN_LIST)) { + struct timeval now; + gettimeofday(&now, NULL); + timer->timeout = now; + _autoreload_timer(timer); + } + } else { + timer->flags &= ~IOTIMERFLAG_PERIODIC; + } +} + +void iotimer_set_callback(struct IOTimerDescriptor *descriptor, iotimer_callback *callback) { + descriptor->callback = callback; +} + +void iotimer_destroy(struct IOTimerDescriptor *descriptor) { + struct _IOTimerDescriptor *timer = descriptor->iotimer; + if(timer == NULL) { + iolog_trigger(IOLOG_WARNING, "called iotimer_destroy for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__); + return; + } + descriptor->iotimer = NULL; + _destroy_timer(timer); + + iogc_add(descriptor); +} + +/* internal functions */ +void _init_timers() { + //nothing in here +} + +struct _IOTimerDescriptor *_create_timer(struct timeval *timeout) { + struct _IOTimerDescriptor *timer = calloc(1, sizeof(*timer)); + if(!timer) { + iolog_trigger(IOLOG_ERROR, "could not allocate memory for _IOTimerDescriptor in %s:%d", __FILE__, __LINE__); + return NULL; + } + if(timeout) { + timer->timeout = *timeout; + _rearrange_timer(timer); + } + return timer; +} + +static void _rearrange_timer(struct _IOTimerDescriptor *timer) { + if((timer->flags & IOTIMERFLAG_IN_LIST)) { + if(timer->prev == NULL) + iotimer_sorted_descriptors = timer->next; + else + timer->prev->next = timer->next; + if(timer->next != NULL) + timer->next->prev = timer->prev; + } + struct _IOTimerDescriptor *ctimer; + for(ctimer = iotimer_sorted_descriptors; ctimer; ctimer = ctimer->next) { + if(timeval_is_bigger(ctimer->timeout, timer->timeout)) { + timer->next = ctimer; + timer->prev = ctimer->prev; + if(ctimer->prev) + ctimer->prev->next = timer; + else + iotimer_sorted_descriptors = timer; + ctimer->prev = timer; + break; + } + else if(ctimer->next == NULL) { + ctimer->next = timer; + timer->prev = ctimer; + break; + } + } + if(ctimer == NULL) + iotimer_sorted_descriptors = timer; + timer->flags |= IOTIMERFLAG_IN_LIST; +} + +void _destroy_timer(struct _IOTimerDescriptor *timer) { + if((timer->flags & IOTIMERFLAG_IN_LIST)) { + if(timer->prev == NULL) + iotimer_sorted_descriptors = timer->next; + else + timer->prev->next = timer->next; + if(timer->next != NULL) + timer->next->prev = timer->prev; + } + free(timer); +} + +static void _autoreload_timer(struct _IOTimerDescriptor *timer) { + timer->timeout.tv_usec += timer->autoreload.tv_usec; + timer->timeout.tv_sec += timer->autoreload.tv_sec; + if(timer->timeout.tv_usec > 1000000) { + timer->timeout.tv_sec += (timer->timeout.tv_usec / 1000000); + timer->timeout.tv_usec %= 1000000; + } + _rearrange_timer(timer); +} + +void _trigger_timer() { + struct timeval now; + _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/IOHandler/IOTimer.h b/src/IOHandler/IOTimer.h new file mode 100644 index 0000000..c173282 --- /dev/null +++ b/src/IOHandler/IOTimer.h @@ -0,0 +1,70 @@ +/* IOTimer.h - IOMultiplexer v2 + * Copyright (C) 2014 Philipp Kreil (pk910) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef _IOTimer_h +#define _IOTimer_h +#ifndef _IOHandler_internals +#include "IOHandler.h" +#else +#include + + +#define IOTIMERFLAG_PERIODIC 0x01 +#define IOTIMERFLAG_ACTIVE 0x02 +#define IOTIMERFLAG_IN_LIST 0x04 +#define IOTIMERFLAG_PARENT_PUBLIC 0x08 +#define IOTIMERFLAG_PARENT_SOCKET 0x10 + +struct _IOTimerDescriptor; + +extern struct _IOTimerDescriptor *iotimer_sorted_descriptors; + +struct _IOTimerDescriptor { + unsigned int flags : 8; + void *parent; + + struct timeval timeout; + struct timeval autoreload; + + struct _IOTimerDescriptor *prev, *next; +}; + +void _init_timers(); +struct _IOTimerDescriptor *_create_timer(struct timeval *timeout); +void _destroy_timer(struct _IOTimerDescriptor *timer); +void _trigger_timer(); + +#endif + +struct IOTimerDescriptor; + +#define IOTIMER_CALLBACK(NAME) void NAME(struct IOTimerDescriptor *iotimer) +typedef IOTIMER_CALLBACK(iotimer_callback); + +struct IOTimerDescriptor { + void *iotimer; /* struct _IOTimerDescriptor */ + + iotimer_callback *callback; + void *data; +}; + +struct IOTimerDescriptor *iotimer_create(struct timeval *timeout); +void iotimer_start(struct IOTimerDescriptor *iotimer); +void iotimer_set_autoreload(struct IOTimerDescriptor *iotimer, struct timeval *autoreload); +void iotimer_set_callback(struct IOTimerDescriptor *iotimer, iotimer_callback *callback); +void iotimer_destroy(struct IOTimerDescriptor *iotimer); + +#endif 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/IOHandler_test/socket/iotest.c b/src/IOHandler_test/socket/iotest.c new file mode 100644 index 0000000..ec9a330 --- /dev/null +++ b/src/IOHandler_test/socket/iotest.c @@ -0,0 +1,62 @@ +/* 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/IOHandler.h" +#include "../../IOHandler/IOSockets.h" +#include "../../IOHandler/IOLog.h" + +static IOSOCKET_CALLBACK(io_callback); +static IOLOG_CALLBACK(io_log); + +static struct IOSocket *irc_iofd = NULL; + +int main(int argc, char *argv[]) { + iohandler_init(); + + iolog_register_callback(io_log); + + irc_iofd = iosocket_connect_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 IOSOCKET_CALLBACK(io_callback) { + switch(event->type) { + case IOSOCKETEVENT_CONNECTED: + printf("[connect]\n"); + break; + case IOSOCKETEVENT_CLOSED: + printf("[disconnect]\n"); + break; + case IOSOCKETEVENT_RECV: + printf("[in] %s\n", event->data.recv_str); + break; + + default: + break; + } +} + +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/IOInternal.h b/src/IOInternal.h deleted file mode 100644 index 2dd5950..0000000 --- a/src/IOInternal.h +++ /dev/null @@ -1,52 +0,0 @@ -/* IOInternal.h - IOMultiplexer v2 - * Copyright (C) 2014 Philipp Kreil (pk910) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#ifndef _IOInternal_h -#define _IOInternal_h -#ifndef _IOHandler_internals -#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 IOGC_FREE(NAME) void NAME(void *object) -typedef IOGC_FREE(iogc_free); -void iogc_add(void *object); -void iogc_add_callback(void *object, iogc_free *free_callback); - -#endif -#endif diff --git a/src/IOLog.c b/src/IOLog.c deleted file mode 100644 index 04af476..0000000 --- a/src/IOLog.c +++ /dev/null @@ -1,39 +0,0 @@ -/* IOLog.c - IOMultiplexer v2 - * Copyright (C) 2014 Philipp Kreil (pk910) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#define _IOHandler_internals -#include "IOInternal.h" -#include "IOHandler.h" -#include "IOLog.h" - -void iolog_init() { - -} - -void iolog_trigger(enum IOLogType type, char *text, ...) { - va_list arg_list; - char logBuf[MAXLOG+1]; - int pos; - logBuf[0] = '\0'; - va_start(arg_list, text); - pos = vsnprintf(logBuf, MAXLOG - 1, text, arg_list); - va_end(arg_list); - if (pos < 0 || pos > (MAXLOG - 1)) pos = MAXLOG - 1; - logBuf[pos] = '\n'; - logBuf[pos+1] = '\0'; - - -} diff --git a/src/IOLog.h b/src/IOLog.h deleted file mode 100644 index 0296b3d..0000000 --- a/src/IOLog.h +++ /dev/null @@ -1,38 +0,0 @@ -/* IOLog.h - IOMultiplexer v2 - * Copyright (C) 2014 Philipp Kreil (pk910) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#ifndef _IOInternal_h -#define _IOInternal_h -#ifndef _IOHandler_internals -#include "IOHandler.h" -#else -enum IOLogType; - -void iolog_init(); -void iolog_trigger(enum IOLogType type, char *text, ...); - -#endif - -enum IOLogType { - IOLOG_DEBUG, - IOLOG_WARNING, - IOLOG_ERROR, - IOLOG_FATAL -}; - -/* TODO: Functions to get messages from IOLog */ - -#endif 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/IOSockets.h b/src/IOSockets.h deleted file mode 100644 index 79f945e..0000000 --- a/src/IOSockets.h +++ /dev/null @@ -1,148 +0,0 @@ -/* IOSockets.h - IOMultiplexer v2 - * Copyright (C) 2014 Philipp Kreil (pk910) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#ifndef _IOSockets_h -#define _IOSockets_h -#ifndef _IOHandler_internals -#include "IOHandler.h" -#else - -#define IOSOCKET_LINE_LEN 1024 - -#define IOSOCKETFLAG_ACTIVE 0x0001 -#define IOSOCKETFLAG_LISTENING 0x0002 -#define IOSOCKETFLAG_PENDING_BINDDNS 0x0004 -#define IOSOCKETFLAG_PENDING_DESTDNS 0x0008 -#define IOSOCKETFLAG_DNSDONE_BINDDNS 0x0010 -#define IOSOCKETFLAG_DNSDONE_DESTDNS 0x0020 -#define IOSOCKETFLAG_DNSERROR 0x0040 -#define IOSOCKETFLAG_IPV6SOCKET 0x0080 -#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; - -struct IOSocketDNSLookup { - unsigned int bindlookup : 1; - char hostname[256]; - struct _IOSocket *iosocket; - struct _IODNSQuery *query; - struct IODNSResult *result; -}; - -struct _IOSocket { - int fd; - - unsigned int socket_flags : 16; - - union { - struct IODNSAddress addr; - struct IOSocketDNSLookup *addrlookup; - } bind; - union { - struct IODNSAddress addr; - struct IOSocketDNSLookup *addrlookup; - } dest; - - unsigned int port : 16; - - struct IOSocketBuffer readbuf; - struct IOSocketBuffer writebuf; - - struct IOSSLDescriptor *sslnode; - - void *parent; - - struct _IOSocket *next, *prev; -}; - -void _init_sockets(); -void iosocket_lookup_callback(struct IOSocketDNSLookup *lookup, struct IODNSEvent *event); - -#endif - -struct IOSocketEvent; - -#define IOSOCKET_CALLBACK(NAME) void NAME(struct IOSocketEvent *event) -typedef IOSOCKET_CALLBACK(iosocket_callback); - -enum IOSocketStatus { - IOSOCKET_CLOSED, /* descriptor is dead (socket waiting for removal or timer) */ - IOSOCKET_LISTENING, /* descriptor is waiting for connections (server socket) */ - IOSOCKET_CONNECTING, /* descriptor is waiting for connection approval (connecting client socket) */ - IOSOCKET_CONNECTED /* descriptor is connected (connected client socket) */ -}; - -enum IOSocketEventType { - IOSOCKETEVENT_IGNORE, - IOSOCKETEVENT_RECV, /* client socket received something (read_lines == 1 => recv_str valid; read_lines == 0 => recv_buf valid) */ - IOSOCKETEVENT_CONNECTED, /* client socket connected successful */ - IOSOCKETEVENT_NOTCONNECTED, /* client socket could not connect (errid valid) */ - IOSOCKETEVENT_CLOSED, /* client socket lost connection (errid valid) */ - IOSOCKETEVENT_ACCEPT, /* server socket accepted new connection (accept_socket valid) */ - IOSOCKETEVENT_SSLFAILED, /* failed to initialize SSL session */ - IOSOCKETEVENT_DNSFAILED /* failed to lookup DNS information */ -}; - -struct IOSocket { - void *iosocket; - - enum IOSocketStatus status; - int listening : 1; - int ssl : 1; - int read_lines : 1; - - void *data; - iosocket_callback *callback; -}; - -struct IOSocketBuffer { - char *buffer; - size_t bufpos, buflen; -}; - -struct IOSocketEvent { - enum IOSocketEventType type; - struct IOSocket *socket; - union { - char *recv_str; - struct IOSocketBuffer *recv_buf; - int errid; - struct IOSocket *accept_socket; - } data; -}; - - -#define IOSOCKET_ADDR_IPV4 0x01 -#define IOSOCKET_ADDR_IPV6 0x02 /* overrides IOSOCKET_ADDR_IPV4 */ - -struct IOSocket *iosocket_connect(const char *hostname, unsigned int port, int ssl, const char *bindhost, iosocket_callback *callback); -struct IOSocket *iosocket_connect_flags(const char *hostname, unsigned int port, int ssl, const char *bindhost, iosocket_callback *callback, int flags); -struct IOSocket *iosocket_listen(const char *hostname, unsigned int port, iosocket_callback *callback); -struct IOSocket *iosocket_listen_flags(const char *hostname, unsigned int port, iosocket_callback *callback, int flags); -struct IOSocket *iosocket_listen_ssl(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iosocket_callback *callback); -struct IOSocket *iosocket_listen_ssl_flags(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iosocket_callback *callback, int flags); -void iosocket_write(struct IOSocket *iosocket, const char *line); -void iosocket_send(struct IOSocket *iosocket, const char *data, size_t datalen); -void iosocket_printf(struct IOSocket *iosocket, const char *text, ...); -void iohandler_close(struct IOSocket *iosocket); - -#endif diff --git a/src/IOTimer.c b/src/IOTimer.c deleted file mode 100644 index 24545cd..0000000 --- a/src/IOTimer.c +++ /dev/null @@ -1,178 +0,0 @@ -/* IOTimer.c - IOMultiplexer v2 - * Copyright (C) 2014 Philipp Kreil (pk910) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#define _IOHandler_internals -#include "IOInternal.h" -#include "IOHandler.h" -#include "IOTimer.h" -#include "IOLog.h" - -#ifdef HAVE_PTHREAD_H -static pthread_mutex_t iotimer_sync; -#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)) - -static void _rearrange_timer(struct IOTimerDescriptor *timer); -static void _autoreload_timer(struct IOTimerDescriptor *timer); - -struct _IOTimerDescriptor *iotimer_sorted_descriptors; - -/* public functions */ - -struct IOTimerDescriptor iotimer_create(struct timeval *timeout) { - struct IOTimerDescriptor *descriptor = calloc(1, sizeof(*descriptor)); - if(!descriptor) { - iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOTimerDescriptor in %s:%d", __FILE__, __LINE__); - return NULL; - } - struct _IOTimerDescriptor *timer = _create_timer(timeout); - if(!timer) { - free(descriptor); - return NULL; - } - timer->parent = descriptor; - timer->flags |= IOTIMERFLAG_PARENT_PUBLIC; - descriptor->iotimer = timer; - - return descriptor; -} - -void iotimer_start(struct IOTimerDescriptor *descriptor) { - struct _IOTimerDescriptor *timer = descriptor->iotimer; - if(timer == NULL) { - iolog_trigger(IOLOG_WARNING, "called iotimer_set_autoreload for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__); - return; - } - timer->flags |= IOTIMERFLAG_ACTIVE; - if(!(timer->flags & IOTIMERFLAG_IN_LIST)) - _trigger_timer(timer); -} - -void iotimer_set_autoreload(struct IOTimerDescriptor *descriptor, struct timeval *autoreload) { - struct _IOTimerDescriptor *timer = descriptor->iotimer; - if(timer == NULL) { - iolog_trigger(IOLOG_WARNING, "called iotimer_set_autoreload for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__); - return; - } - if(autoreload) { - timer->flags |= IOTIMERFLAG_PERIODIC; - timer->autoreload = *autoreload; - - if(!(timer->flags & IOTIMERFLAG_IN_LIST)) { - struct timeval ctime; - gettimeofday(&ctime, NULL); - timer->timeout = ctime; - _autoreload_timer(timer); - } - } else { - timer->flags &= ~IOTIMERFLAG_PERIODIC; - } -} - -void iotimer_set_callback(struct IOTimerDescriptor *descriptor, iotimer_callback *callback) { - descriptor->callback = callback; -} - -void iotimer_destroy(struct IOTimerDescriptor *descriptor) { - struct _IOTimerDescriptor *timer = descriptor->iotimer; - if(timer == NULL) { - iolog_trigger(IOLOG_WARNING, "called iotimer_destroy for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__); - return; - } - descriptor->iotimer = NULL; - _destroy_timer(timer); - - iogc_add(descriptor); -} - -/* internal functions */ -void _init_timers() { - IOTHREAD_MUTEX_INIT(iotimer_sync); -} - -struct _IOTimerDescriptor _create_timer(struct timeval *timeout) { - struct _IOTimerDescriptor *timer = calloc(1, sizeof(*timer)); - if(!timer) { - iolog_trigger(IOLOG_ERROR, "could not allocate memory for _IOTimerDescriptor in %s:%d", __FILE__, __LINE__); - return NULL; - } - if(timeout) { - timer->timeout = *timeout; - _rearrange_timer(timer); - } - return timer; -} - -static void _rearrange_timer(struct IOTimerDescriptor *timer) { - IOSYNCHRONIZE(iotimer_sync); - if((timer->flags & IOTIMERFLAG_IN_LIST)) { - if(timer->prev == NULL) - iotimer_sorted_descriptors = timer->next; - else - timer->prev->next = timer->next; - if(timer->next != NULL) - timer->next->prev = timer->prev; - } - struct _IOTimerDescriptor *ctimer; - for(ctimer = iotimer_sorted_descriptors; ctimer;) { - if(timeval_is_bigger(&ctimer->timeout, &timer->timeout)) { - timer->next = ctimer; - timer->prev = ctimer->prev; - if(ctimer->prev) - ctimer->prev->next = timer; - else - iotimer_sorted_descriptors = timer; - ctimer->prev = timer; - break - } - else if(ctimer->next == NULL) { - ctimer->next = timer; - timer->prev = ctimer; - break; - } - } - if(ctimer == NULL) - iotimer_sorted_descriptors = timer; - timer->flags |= IOTIMERFLAG_IN_LIST; - 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) { - timer->timeout.tv_usec += timer->autoreload.tv_usec; - timer->timeout.tv_sec += timer->autoreload.tv_sec; - if(timer->timeout.tv_usec > 1000000) { - timer->timeout.tv_sec += (timer->timeout.tv_usec / 1000000); - timer->timeout.tv_usec %= 1000000; - } - _rearrange_timer(timer); -} - diff --git a/src/IOTimer.h b/src/IOTimer.h deleted file mode 100644 index b7bd71a..0000000 --- a/src/IOTimer.h +++ /dev/null @@ -1,65 +0,0 @@ -/* IOTimer.h - IOMultiplexer v2 - * Copyright (C) 2014 Philipp Kreil (pk910) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#ifndef _IOTimer_h -#define _IOTimer_h -#ifndef _IOHandler_internals -#include "IOHandler.h" -#else - -#define IOTIMERFLAG_PERIODIC 0x01 -#define IOTIMERFLAG_ACTIVE 0x02 -#define IOTIMERFLAG_IN_LIST 0x04 -#define IOTIMERFLAG_PARENT_PUBLIC 0x08 -#define IOTIMERFLAG_PARENT_SOCKET 0x10 - -struct _IOTimerDescriptor; - -extern struct _IOTimerDescriptor *iotimer_sorted_descriptors; - -struct _IOTimerDescriptor { - unsigned int flags : 8; - void *parent; - - struct timeval timeout; - struct timeval autoreload; - - struct _IOTimerDescriptor *prev, *next; -}; - -void _init_timers(); -struct _IOTimerDescriptor _create_timer(struct timeval timeout); -void _destroy_timer(struct _IOTimerDescriptor *timer); - -#endif - -#define IOTIMER_CALLBACK(NAME) void NAME(struct IOTimerDescriptor *iotimer) -typedef IOTIMER_CALLBACK(iotimer_callback); - -struct IOTimerDescriptor { - void *iotimer; /* struct _IOTimerDescriptor */ - - iotimer_callback *callback; - void *data; -}; - -struct IOTimerDescriptor iotimer_create(struct timeval *timeout); -void iotimer_start(struct IOTimerDescriptor *iotimer); -void iotimer_set_autoreload(struct IOTimerDescriptor *iotimer, struct timeval *autoreload); -void iotimer_set_callback(struct IOTimerDescriptor *iotimer, iotimer_callback *callback); -void iotimer_destroy(struct IOTimerDescriptor *iotimer); - -#endif 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/socket/iotest.c b/src/test/socket/iotest.c deleted file mode 100644 index 15f32f4..0000000 --- a/src/test/socket/iotest.c +++ /dev/null @@ -1,63 +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" - -static IOHANDLER_CALLBACK(io_callback); -static IOHANDLER_LOG_BACKEND(io_log); - -static struct IODescriptor *irc_iofd = NULL; - -int main(int argc, char *argv[]) { - iolog_backend = 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(); - } -} - -static IOHANDLER_CALLBACK(io_callback) { - switch(event->type) { - case IOEVENT_CONNECTED: - printf("[connect]\n"); - break; - case IOEVENT_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); - break; - - default: - break; - } -} - -static IOHANDLER_LOG_BACKEND(io_log) { - //printf("%s", line); -} 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); -}