--- /dev/null
+/* IODNSEngine_cares.c - IOMultiplexer
+ * Copyright (C) 2014 Philipp Kreil (pk910)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "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,
+};
--- /dev/null
+/* IODNSEngine_default.c - IOMultiplexer
+ * Copyright (C) 2014 Philipp Kreil (pk910)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "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,
+};
--- /dev/null
+/* IODNSLookup.c - IOMultiplexer
+ * Copyright (C) 2014 Philipp Kreil (pk910)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IODNSLookup.h"
+#include "IOLog.h"
+
+#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;
+ }
+}
+
+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;
+
+ descriptor->parent = NULL;
+ _stop_dnsquery(query);
+
+ if(descriptor->callback)
+ descriptor->callback(&event);
+
+ iogc_add(descriptor);
+ }
+}
+
+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);
+ }
+}
+
--- /dev/null
+/* IODNSLookup.h - IOMultiplexer v2
+ * Copyright (C) 2014 Philipp Kreil (pk910)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _IODNSLookup_h
+#define _IODNSLookup_h
+#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
+
+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
--- /dev/null
+/* IOGarbageCollector.c - IOMultiplexer v2
+ * Copyright (C) 2014 Philipp Kreil (pk910)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IOGarbageCollector.h"
+
+#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;
+ }
+ }
+}
--- /dev/null
+/* IOGarbageCollector.h - IOMultiplexer v2
+ * Copyright (C) 2014 Philipp Kreil (pk910)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _IOGarbageCollector_h
+#define _IOGarbageCollector_h
+#ifndef _IOHandler_internals
+#include "IOHandler.h"
+#else
+
+void iogc_init();
+void iogc_exec();
+
+#endif
+#endif
--- /dev/null
+/* IOHandler.c - IOMultiplexer v2
+ * Copyright (C) 2014 Philipp Kreil (pk910)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+
+#include "IOLog.h"
+#include "IOGarbageCollector.h"
+#include "IOTimer.h"
+#include "IODNSLookup.h"
+
+/* 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();
+
+ 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 */
+}
+
--- /dev/null
+/* IOHandler.h - IOMultiplexer v2
+ * Copyright (C) 2014 Philipp Kreil (pk910)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _IOHandler_h
+#define _IOHandler_h
+#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
--- /dev/null
+/* IOInternal.h - IOMultiplexer v2
+ * Copyright (C) 2014 Philipp Kreil (pk910)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _IOInternal_h
+#define _IOInternal_h
+#ifndef _IOHandler_internals
+#include "IOHandler.h"
+#else
+
+/* Multithreading */
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#ifdef PTHREAD_MUTEX_RECURSIVE_NP
+#define PTHREAD_MUTEX_RECURSIVE_VAL PTHREAD_MUTEX_RECURSIVE_NP
+#else
+#define PTHREAD_MUTEX_RECURSIVE_VAL PTHREAD_MUTEX_RECURSIVE
+#endif
+#define IOTHREAD_MUTEX_INIT(var) { \
+ pthread_mutexattr_t mutex_attr; \
+ pthread_mutexattr_init(&mutex_attr);\
+ pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE_VAL);\
+ pthread_mutex_init(&var, &mutex_attr); \
+}
+#define IOSYNCHRONIZE(var) pthread_mutex_lock(&var)
+#define IODESYNCHRONIZE(var) pthread_mutex_unlock(&var)
+#else
+#define IOTHREAD_MUTEX_INIT(var)
+#define IOSYNCHRONIZE(var)
+#define IODESYNCHRONIZE(var)
+#endif
+
+
+#define 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
--- /dev/null
+/* IOLog.c - IOMultiplexer v2
+ * Copyright (C) 2014 Philipp Kreil (pk910)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IOLog.h"
+
+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';
+
+
+}
--- /dev/null
+/* IOLog.h - IOMultiplexer v2
+ * Copyright (C) 2014 Philipp Kreil (pk910)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _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
--- /dev/null
+/* IOSockets.h - IOMultiplexer v2
+ * Copyright (C) 2014 Philipp Kreil (pk910)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _IOSockets_h
+#define _IOSockets_h
+#ifndef _IOHandler_internals
+#include "IOHandler.h"
+#else
+
+
+
+#endif
+
+
+
+#endif
--- /dev/null
+/* IOTimer.c - IOMultiplexer v2
+ * Copyright (C) 2014 Philipp Kreil (pk910)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IOHandlerTimer.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);
+}
+
--- /dev/null
+/* IOTimer.h - IOMultiplexer v2
+ * Copyright (C) 2014 Philipp Kreil (pk910)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _IOTimer_h
+#define _IOTimer_h
+#ifndef _IOHandler_internals
+#include "IOHandler.h"
+#else
+
+#define IOTIMERFLAG_PERIODIC 0x01
+#define IOTIMERFLAG_ACTIVE 0x02
+#define IOTIMERFLAG_IN_LIST 0x04
+#define IOTIMERFLAG_PARENT_PUBLIC 0x08
+
+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
--- /dev/null
+#ifndef HAVE_USLEEP
+
+#ifdef HAVE_SELECT
+
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#else
+# include <sys/time.h>
+# include <sys/types.h>
+# include <unistd.h>
+#endif
+
+void usleep(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 <windows.h>
+
+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