From e18ef9b450a0704ff5c55545f70e3f1ec215a5e6 Mon Sep 17 00:00:00 2001 From: pk910 Date: Sun, 30 Jun 2013 01:54:58 +0200 Subject: [PATCH] [IOMultiplexer] Added asynchronous DNS Lookups --- src/IODNSEngine.h | 43 ++ src/IODNSEngine_cares.c | 41 ++ src/IODNSEngine_default.c | 89 ++++ src/IODNSHandler.c | 138 +++++ src/IODNSHandler.h | 70 +++ src/IOEngine.h | 54 +- src/IOEngine_epoll.c | 47 +- src/IOEngine_epoll.o | Bin 0 -> 3242 bytes src/IOEngine_kevent.c | 56 +- src/IOEngine_kevent.o | Bin 0 -> 3243 bytes src/IOEngine_select.c | 62 ++- src/IOHandler.c | 1033 ++++++++++++++++++++++++------------- src/IOHandler.h | 26 +- src/test/socket/Makefile | 2 +- 14 files changed, 1210 insertions(+), 451 deletions(-) create mode 100644 src/IODNSEngine.h create mode 100644 src/IODNSEngine_cares.c create mode 100644 src/IODNSEngine_default.c create mode 100644 src/IODNSHandler.c create mode 100644 src/IODNSHandler.h create mode 100644 src/IOEngine_epoll.o create mode 100644 src/IOEngine_kevent.o diff --git a/src/IODNSEngine.h b/src/IODNSEngine.h new file mode 100644 index 0000000..91006dc --- /dev/null +++ b/src/IODNSEngine.h @@ -0,0 +1,43 @@ +/* IODNSEngine.h - 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 . + */ +#ifndef _IODNSEngine_h +#define _IODNSEngine_h +#include "IOHandler.h" +#include "IODNSHandler.h" + +extern struct IODNSQuery *first_dnsquery; +extern struct IODNSQuery *last_dnsquery; +extern int dnsquery_count; + +extern struct IODNSEngine *dnsengine; + +struct IODNSEngine { + const char *name; + int (*init)(); + void (*add)(struct IODNSQuery *iodns); + void (*remove)(struct IODNSQuery *iodns); + void (*loop)(); +}; + +void iodns_event_callback(struct IODNSQuery *iodns, enum IODNSEventType state); + +void iohandler_log(enum IOLogType type, char *text, ...); +char *iohandler_iotype_name(enum IOType type); +char *iohandler_iostatus_name(enum IOStatus status); +char *iohandler_ioeventtype_name(enum IOEventType type); + +#endif diff --git a/src/IODNSEngine_cares.c b/src/IODNSEngine_cares.c new file mode 100644 index 0000000..c35cbc7 --- /dev/null +++ b/src/IODNSEngine_cares.c @@ -0,0 +1,41 @@ +/* IODNSEngine_cares.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 "IODNSEngine.h" + +static int dnsengine_cares_init() { + return 0; +} + +static void dnsengine_cares_add(struct IODNSQuery *iodns) { + /* unused */ +} + +static void dnsengine_cares_remove(struct IODNSQuery *iodns) { + /* unused */ +} + +static void dnsengine_cares_loop() { + /* unused */ +} + +struct IODNSEngine dnsengine_cares = { + .name = "cares", + .init = dnsengine_cares_init, + .add = dnsengine_cares_add, + .remove = dnsengine_cares_remove, + .loop = dnsengine_cares_loop, +}; diff --git a/src/IODNSEngine_default.c b/src/IODNSEngine_default.c new file mode 100644 index 0000000..89427ec --- /dev/null +++ b/src/IODNSEngine_default.c @@ -0,0 +1,89 @@ +/* IODNSEngine_default.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 "IODNSEngine.h" + +static int dnsengine_default_init() { + return 1; +} + +static void dnsengine_default_add(struct IODNSQuery *iodns) { + /* unused */ +} + +static void dnsengine_default_remove(struct IODNSQuery *iodns) { + /* unused */ +} + +static void dnsengine_default_loop() { + enum IODNSEventType querystate; + struct addrinfo hints, *res, *next_res; + struct IODNSQuery *iodns, *next_iodns; + struct IODNSResult *dnsresult; + for(iodns = first_dnsquery; iodns; iodns = next_iodns) { + next_iodns = iodns->next; + 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->hostname, 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->addresslen = res->ai_addrlen; + dnsresult->address = malloc(dnsresult->addresslen); + memcpy(dnsresult->address, res->ai_addr, dnsresult->addresslen); + dnsresult->next = iodns->addr.results; + iodns->addr.results = dnsresult; + querystate = IODNSEVENT_SUCCESS; + } + break; + case AF_INET6: + if((iodns->type & IODNS_RECORD_AAAA)) { + dnsresult = malloc(sizeof(*dnsresult)); + dnsresult->type = IODNS_RECORD_AAAA; + dnsresult->addresslen = res->ai_addrlen; + dnsresult->address = malloc(dnsresult->addresslen); + memcpy(dnsresult->address, res->ai_addr, dnsresult->addresslen); + dnsresult->next = iodns->addr.results; + iodns->addr.results = dnsresult; + querystate = IODNSEVENT_SUCCESS; + } + break; + } + next_res = res->ai_next; + freeaddrinfo(res); + res = next_res; + } + } + iodns_event_callback(iodns, querystate); + } + } +} + +struct IODNSEngine dnsengine_default = { + .name = "default", + .init = dnsengine_default_init, + .add = dnsengine_default_add, + .remove = dnsengine_default_remove, + .loop = dnsengine_default_loop, +}; diff --git a/src/IODNSHandler.c b/src/IODNSHandler.c new file mode 100644 index 0000000..1caebdd --- /dev/null +++ b/src/IODNSHandler.c @@ -0,0 +1,138 @@ +/* IODNSHandler.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 "IODNSHandler.h" +#include "IODNSEngine.h" + +struct IODNSQuery *first_dnsquery = NULL; +struct IODNSQuery *last_dnsquery = NULL; +int dnsquery_count = 0; + +extern struct IODNSEngine dnsengine_cares; +extern struct IODNSEngine dnsengine_default; + +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; + } +} + +static void iodns_append(struct IODNSQuery *query) { + IOSYNCHRONIZE(io_thread_sync); + query->next = NULL; + query->prev = last_dnsquery; + last_dnsquery = query; + if(!first_dnsquery) + first_dnsquery = query; + dnsquery_count++; + IODESYNCHRONIZE(io_thread_sync); +} + +static void iodns_remove(struct IODNSQuery *query) { + IOSYNCHRONIZE(io_thread_sync); + if(query->next) + query->next->prev = query->prev; + else + last_dnsquery = query->prev; + if(query->prev) + query->prev->next = query->next; + else + first_dnsquery = query->next; + dnsquery_count--; + IODESYNCHRONIZE(io_thread_sync); +} + +static void iodns_free(struct IODNSQuery *query) { + if(query->hostname) + free(query->hostname); + if((query->type & IODNS_REVERSE) && query->address.addr) + free(query->addr.address); + free(query); +} + +struct IODNSQuery *iodns_getaddrinfo(char *hostname, int records, iodns_callback *callback) { + if(!dnsengine) + iodns_init_engine(); + if(!(records & IODNS_FORWARD) || !hostname || !callback) + return NULL; + struct IODNSQuery *query = calloc(1, sizeof(*query)); + query->hostname = strdup(hostname); + query->type = (records & IODNS_FORWARD); + query->callback = callback; + iodns_append(query); + dnsengine->add(query); + return query; +} + +struct IODNSQuery *iodns_getnameinfo(const struct sockaddr *addr, socklen_t addrlen, iodns_callback *callback) { + if(!dnsengine) + iodns_init_engine(); + if(!addr || !callback) + return NULL; + struct IODNSQuery *query = calloc(1, sizeof(*query)); + query->address = malloc(addrlen); + memcpy(query->addr.address, addr, addrlen); + query->addresslen = addrlen; + query->type = IODNS_RECORD_PTR; + query->callback = callback; + iodns_append(query); + dnsengine->add(query); + return query; +} + +void iodns_abort(struct IODNSQuery *query) { + if(!dnsengine || !query) + return; + dnsengine->remove(query); + iodns_remove(query); + iodns_free(query); +} + +void iodns_event_callback(struct IODNSQuery *iodns, enum IODNSEventType state) { + struct IODNSEvent event; + event.type = state; + event.query = iodns; + dnsengine->remove(iodns); + iodns_remove(iodns); + if(iodns->callback) + iodns->callback(event); + iodns_free(iodns); +} + +void iodns_poll() { + if(dnsengine) + dnsengine.loop(); +} + +void iodns_free_result(struct IODNSResult *result) { + struct IODNSResult *next; + for(;result;result = next) { + next = result->next; + free(result->address); + free(result); + } +} + diff --git a/src/IODNSHandler.h b/src/IODNSHandler.h new file mode 100644 index 0000000..621afb7 --- /dev/null +++ b/src/IODNSHandler.h @@ -0,0 +1,70 @@ +/* IODNSHandler.h - 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 . + */ +#ifndef _IODNSHandler_h +#define _IODNSHandler_h + + +#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; + unsigned int type : 8; + char *hostname; + size_t addresslen; + union { + struct sockaddr *address; + struct IODNSResult *results + } addr; + iodns_callback *callback; + void *data; + + struct IODNSQuery *next, *prev; +}; + +struct IODNSResult { + unsigned int type : 8; + size_t addresslen; + struct sockaddr *address; + struct IODNSResult *next; +}; + +struct IODNSEvent { + enum IODNSEventType type; + struct IODNSQuery *query; +}; + +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_poll(); + +void iodns_free_result(struct IODNSResult *result); + +#endif diff --git a/src/IOEngine.h b/src/IOEngine.h index 5f68240..4e43a6a 100644 --- a/src/IOEngine.h +++ b/src/IOEngine.h @@ -39,23 +39,53 @@ #define IODESYNCHRONIZE(var) #endif -struct IODescriptor; -enum IOType; -enum IOStatus; -enum IOEventType; - #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)) -extern struct IODescriptor *first_descriptor; -extern struct IODescriptor *timer_priority; + +struct IODescriptor; +struct IOLowlevelDescriptor; + +extern struct IOLowlevelDescriptor *first_descriptor; +extern struct IOLowlevelDescriptor *timer_priority; +extern struct IOLowlevelDescriptor *lowlevel_descriptor; + +#define IOLOWLEVEL_CALLBACK(NAME) void NAME(struct IOLowlevelDescriptor *iold, int can_read) +typedef IOLOWLEVEL_CALLBACK(iolowlevel_callback); + +#define IOFLAGS_WANT_READ 0x01 +#define IOFLAGS_WANT_WRITE 0x02 +#define IOFLAGS_HAVE_IOFD 0x04 +#define IOFLAGS_HAVE_TIMEOUT 0x08 + +#define IOFDFLAGS_HAVE_IOLD 0x01 +#define IOFDFLAGS_FREE_LOCK 0x02 +#define IOFDFLAGS_WANT_FREE 0x04 +#define IOFDFLAGS_SSL_ACTIVE 0x08 +#define IOFDFLAGS_SSL_SERVER_HS 0x10 +#define IOFDFLAGS_SSL_HS_READ 0x20 +#define IOFDFLAGS_SSL_HS_WRITE 0x40 + +struct IOLowlevelDescriptor { + int fd; + unsigned int flags; + struct timeval timeout; + union { + struct IODescriptor *iofd; + iolowlevel_callback *callback; + } data; + struct IOLowlevelDescriptor *next, *prev; +}; + +#define IOLOWLEVEL_GET_IOFD(iold) ((iold->flags & IOFLAGS_HAVE_IOFD) ? iold->data.iofd : NULL) +#define IODESCRIPTOR_GET_IOLD(iofd) ((iofd->flags & IOFDFLAGS_HAVE_IOLD) ? iofd->fd.iold : NULL) struct IOEngine { const char *name; int (*init)(void); - void (*add)(struct IODescriptor *iofd); - void (*remove)(struct IODescriptor *iofd); - void (*update)(struct IODescriptor *iofd); + void (*add)(struct IOLowlevelDescriptor *iofd); + void (*remove)(struct IOLowlevelDescriptor *iofd); + void (*update)(struct IOLowlevelDescriptor *iofd); void (*loop)(struct timeval *timeout); void (*cleanup)(void); }; @@ -68,4 +98,8 @@ char *iohandler_iotype_name(enum IOType type); char *iohandler_iostatus_name(enum IOStatus status); char *iohandler_ioeventtype_name(enum IOEventType type); +struct IOLowlevelDescriptor *iohandler_lowlevel_add(int fd, int want_read, int want_write, iolowlevel_callback *callback); +void iohandler_lowlevel_update(struct IOLowlevelDescriptor *iolow, int want_read, int want_write); +void iohandler_lowlevel_del(struct IOLowlevelDescriptor *iolow); + #endif diff --git a/src/IOEngine_epoll.c b/src/IOEngine_epoll.c index d1c139f..e115c00 100644 --- a/src/IOEngine_epoll.c +++ b/src/IOEngine_epoll.c @@ -33,40 +33,44 @@ static int engine_epoll_init() { return 1; } -static void engine_epoll_add(struct IODescriptor *iofd) { - if(iofd->type == IOTYPE_TIMER) return; +static void engine_epoll_add(struct IOLowlevelDescriptor *iold) { + if(iold->fd != -1) return; //add descriptor to the epoll queue struct epoll_event evt; int res; - evt.events = EPOLLHUP | EPOLLIN | (iohandler_wants_writes(iofd) ? EPOLLOUT : 0); - evt.data.ptr = iofd; - res = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, iofd->fd, &evt); + evt.events = EPOLLHUP | ((iold->flags & IOFLAGS_WANT_READ) ? EPOLLIN : 0) | ((iold->flags & IOFLAGS_WANT_WRITE) ? EPOLLOUT : 0); + evt.data.ptr = iold; + res = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, iold->fd, &evt); if(res < 0) { - iohandler_log(IOLOG_ERROR, "could not add IODescriptor %d to epoll queue. (returned: %d)", iofd->fd, res); + iohandler_log(IOLOG_ERROR, "could not add IOLowlevelDescriptor %d to epoll queue. (returned: %d)", iold->fd, res); } } -static void engine_epoll_remove(struct IODescriptor *iofd) { - if(iofd->type == IOTYPE_TIMER) return; +static void engine_epoll_remove(struct IOLowlevelDescriptor *iold) { + if(iold->fd != -1) return; struct epoll_event evt; - epoll_ctl(epoll_fd, EPOLL_CTL_DEL, iofd->fd, &evt); + epoll_ctl(epoll_fd, EPOLL_CTL_DEL, iold->fd, &evt); } -static void engine_epoll_update(struct IODescriptor *iofd) { - if(iofd->type == IOTYPE_TIMER) return; - if(iofd->state == IO_CLOSED) { - engine_epoll_remove(iofd); - return; +static void engine_epoll_update(struct IOLowlevelDescriptor *iold) { + if(iold->fd != -1) return; + struct IODescriptor *iofd; + if((iofd = IOLOWLEVEL_GET_IOFD(iold))) { + if(iofd->state == IO_CLOSED) { + engine_epoll_remove(iold); + return; + } } + struct epoll_event evt; int res; - evt.events = EPOLLHUP | EPOLLIN | (iohandler_wants_writes(iofd) ? EPOLLOUT : 0); - evt.data.ptr = iofd; - res = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, iofd->fd, &evt); + evt.events = EPOLLHUP | ((iold->flags & IOFLAGS_WANT_READ) ? EPOLLIN : 0) | ((iold->flags & IOFLAGS_WANT_WRITE) ? EPOLLOUT : 0); + evt.data.ptr = iold; + res = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, iold->fd, &evt); if(res < 0) { - iohandler_log(IOLOG_ERROR, "could not update IODescriptor %d in epoll queue. (returned: %d)", iofd->fd, res); + iohandler_log(IOLOG_ERROR, "could not update IOLowlevelDescriptor %d in epoll queue. (returned: %d)", iold->fd, res); } } @@ -76,6 +80,7 @@ static void engine_epoll_loop(struct timeval *timeout) { int msec; int events; int epoll_result; + struct IOLowlevelDescriptor *iold; gettimeofday(&now, NULL); @@ -117,7 +122,11 @@ static void engine_epoll_loop(struct timeval *timeout) { int i; for(i = 0; i < epoll_result; i++) { events = evts[i].events; - iohandler_events(evts[i].data.ptr, (events & (EPOLLIN | EPOLLHUP)), (events & EPOLLOUT)); + iold = evts[i].data.ptr; + if(iold->flags & IOFLAGS_HAVE_IOFD) + iohandler_events(iold->data.iofd, (events & (EPOLLIN | EPOLLHUP)), (events & EPOLLOUT)); + else + iold->data.callback(iold, (events & (EPOLLIN | EPOLLHUP)), (events & EPOLLOUT)); } } diff --git a/src/IOEngine_epoll.o b/src/IOEngine_epoll.o new file mode 100644 index 0000000000000000000000000000000000000000..9533bba1bfdc1f44e8bd864e95778f18432715ba GIT binary patch literal 3242 zcmb7GO>o=R5nkXuh>%IjbR?%vqc)mRe%cGA+KTIOGHGNAaTwF2hotQ~k&8g!BZV3Q z832@=P7giw)Kktq_SQ?Mr(V-z51lrX$>iASA*Wn3lS?~kzkL8fGEO_~lK}31yKi^j zd;1pKGHl}U_j5$+nvzC6RgY6m|L4~h?Y>(dc&WFbvI)9x5Pbk!JV4UxwEM^UQIcrj zzr23IdhxPBj%>n476$CSzO%OgfV@^BdJMf^=YGM$Z|%m;53VjLeg*FL*>Ve9>s{F` zcqTr@^Sjk0D6_YBc?o2dU*j3PDIVyS7ClH+llZY`tI3dhkX)37tT0Aw1Rs8cT_ z+bDkllU91_CdwzH@*8%rt%0dVsu?0l1mj~b8iXo#eJ>1;JpU1i?^khfE56H@H{6>u5sgFB9Uw|%SKx!;}xJ*Rum>5?&1XtbJ6yHDnf@AVIwZ4xuK z-@FZN$>w{hmrfHnxW++3!M)?Op?ouA8m&&xInajPRns3WXa8XTM$6GYGo7x}xTk$)Z0;D2 z*l&ilJN-G#{&UqVe5(NV8x7}9pH>P0Vdhw=z<7H)cl%@)2(;eVZ?@oawZIPAJvRr> z6=1K^xaIU+r`zpxsa(J}n(c!etZ|agZtv4KnEY z{En)>=sCMJp5ulnaX6eIo0Z0M=JsAKNkS5%4ESyiptQuNC>zJnJ5Iyw@MGRe1D!Ue@0#!9&%swjytlRZ<&&UhnOfy7BBy961| zh0EUsiTp{p8dYQEqZFnwiIj7&s^U066jPp4^?YAVQa7t!r>qYHT|RNs%1W@V*2uv; z%jT6*Ugb(zk+ON8-Dnf?EjIBKtdP9LFsyCjhpVUld>}P@e*<7e-U(`7pG&DOpHYBZ zyZaeLTt2-4GWXt>nzM15Cu8iWyq^ITYdS%F1@PkxsNq{Mo{)U7R^sU!mLDoF!X=S! zKdq5Vv~KWnZOOh@44zivyaw{QMr`Pn_yvTogDw9Fd;^)h0q~+)`1XtiGuEb8=~R`FohXS;Yx_%b44&&TZmyu0!S33ekC>YgrWRsWw+C zm#gJUnZXO}4re!4MgMh?UaD{i8%$sy_KJvqR57{H_!y5#f98EK{;^iY+rWu(qh6=V_T+X~f(L8w6S`08IoBV!g&5dtmtyS$x(Utb%Gy)MTh4DNQ96&L$l zUe`C;S?F;&LSgVapByje5UM3~xWnb!vOM;4mMsW_yW1y6Cx>{pFu1dQa`680;C&ay v*BW0fAsXhtymphg$*d1h;74u{4aYOuJ38X8RxaVtype == IOTYPE_TIMER) return; +static void engine_kevent_add(struct IOLowlevelDescriptor *iold) { + if(iold->fd != -1) return; //add descriptor to the kevent queue struct kevent changes[2]; int nchanges = 0; int res; - EV_SET(&changes[nchanges++], iofd->fd, EVFILT_READ, EV_ADD, 0, 0, iofd); - if (iohandler_wants_writes(iofd)) - EV_SET(&changes[nchanges++], iofd->fd, EVFILT_WRITE, EV_ADD, 0, 0, iofd); + if (iold->flags & IOFLAGS_WANT_READ) + EV_SET(&changes[nchanges++], iold->fd, EVFILT_READ, EV_ADD, 0, 0, iold); + if (iold->flags & IOFLAGS_WANT_WRITE) + EV_SET(&changes[nchanges++], iold->fd, EVFILT_WRITE, EV_ADD, 0, 0, iold); res = kevent(kevent_fd, changes, nchanges, NULL, 0, NULL); if(res < 0) - iohandler_log(IOLOG_ERROR, "could not add IODescriptor %d to kevent queue. (returned: %d)", res); + iohandler_log(IOLOG_ERROR, "could not add IOLowlevelDescriptor %d to kevent queue. (returned: %d)", res); } -static void engine_kevent_remove(struct IODescriptor *iofd) { - if(iofd->type == IOTYPE_TIMER) return; +static void engine_kevent_remove(struct IOLowlevelDescriptor *iold) { + if(iold->fd != -1) return; struct kevent changes[2]; int nchanges = 0; - EV_SET(&changes[nchanges++], iofd->fd, EVFILT_READ, EV_DELETE, 0, 0, iofd); - EV_SET(&changes[nchanges++], iofd->fd, EVFILT_WRITE, EV_DELETE, 0, 0, iofd); + EV_SET(&changes[nchanges++], iold->fd, EVFILT_READ, EV_DELETE, 0, 0, iold); + EV_SET(&changes[nchanges++], iold->fd, EVFILT_WRITE, EV_DELETE, 0, 0, iold); kevent(kevent_fd, changes, nchanges, NULL, 0, NULL); } -static void engine_kevent_update(struct IODescriptor *iofd) { - if(iofd->type == IOTYPE_TIMER) return; - if(iofd->state == IO_CLOSED) { - engine_kevent_remove(iofd); - return; +static void engine_kevent_update(struct IOLowlevelDescriptor *iold) { + if(iold->fd != -1) return; + struct IODescriptor *iofd; + if((iofd = IOLOWLEVEL_GET_IOFD(iold))) { + if(iofd->state == IO_CLOSED) { + engine_kevent_remove(iold); + return; + } } struct kevent changes[2]; int nchanges = 0; int res; - EV_SET(&changes[nchanges++], iofd->fd, EVFILT_READ, EV_ADD, 0, 0, iofd); - EV_SET(&changes[nchanges++], iofd->fd, EVFILT_WRITE, iohandler_wants_writes(iofd) ? EV_ADD : EV_DELETE, 0, 0, iofd); + if (iold->flags & IOFLAGS_WANT_READ) + EV_SET(&changes[nchanges++], iold->fd, EVFILT_READ, EV_ADD, 0, 0, iold); + else + EV_SET(&changes[nchanges++], iold->fd, EVFILT_READ, EV_DELETE, 0, 0, iold); + if (iold->flags & IOFLAGS_WANT_WRITE) + EV_SET(&changes[nchanges++], iold->fd, EVFILT_WRITE, EV_ADD, 0, 0, iold); + else + EV_SET(&changes[nchanges++], iold->fd, EVFILT_WRITE, EV_DELETE, 0, 0, iold); res = kevent(kevent_fd, changes, nchanges, NULL, 0, NULL); if(res < 0) { - iohandler_log(IOLOG_ERROR, "could not update IODescriptor %d in kevent queue. (returned: %d)", res); + iohandler_log(IOLOG_ERROR, "could not update IOLowlevelDescriptor %d in kevent queue. (returned: %d)", res); } } @@ -82,6 +92,7 @@ static void engine_kevent_loop(struct timeval *timeout) { struct timespec ts, *pts; int msec; int kevent_result; + struct IOLowlevelDescriptor *iold; gettimeofday(&now, NULL); @@ -127,8 +138,13 @@ static void engine_kevent_loop(struct timeval *timeout) { } } else { int i; - for(i = 0; i < kevent_result; i++) - iohandler_events(events[i].udata, (events[i].filter == EVFILT_READ), (events[i].filter == EVFILT_WRITE)); + for(i = 0; i < kevent_result; i++) { + iold = events[i].udata; + if(iold->flags & IOFLAGS_HAVE_IOFD) + iohandler_events(iold->data.iofd, (events[i].filter == EVFILT_READ), (events[i].filter == EVFILT_WRITE)); + else + iold->data.callback(iold, (events[i].filter == EVFILT_READ), (events[i].filter == EVFILT_WRITE)); + } } //check timers diff --git a/src/IOEngine_kevent.o b/src/IOEngine_kevent.o new file mode 100644 index 0000000000000000000000000000000000000000..231fad6ee28b398ae4634a1ca5bbc046c80f250c GIT binary patch literal 3243 zcmb7G&2!sS5P#BpN|e}6+@__aACQ?83Ku7B)6fYFGK zq-BN~ZXCG4l`H=M!-W}6y>eiHff3z}* zMh#MAT{1E>VDJ3=;t&9Gd7QC_(EB9u3l@I!7v@hbjVL|=_j&xfDK7QSETlXUKjQgp zVg!oqEuJ0$N#%pYXJ0_}`t-AY?>jq=kK`^PNL4BbcWBr27n#&vJwOaLxQEb#VO4YTV7r9-}dFTWiVPJ1WP8e;39s9mBx^t?~T=?Um z`>ta~O#R_KI90`X&kxJ)ZvOgVyioF=A=S^NlO zKxQh-yLZ8B(W9=z_=6=RjjX$!R+I5ya*SA^$-?2g-)Y3^u+hYt2^W*9jM-aOz$Q!8 z2AL3i;ak8Nu(z%m=Jm?eO7%u%0Mw1zx=~|FU!hno8I=Z8`+U8zTB&B`yY?e#)hEX9A*)@yRN(K8$JQ?ou&=$nj>*X6O zr3TZ;i_A*$CMy@Yv~8>-M6-0MQmq+7K+RZLUAa&;q|aEVW)#<@&zMad!x8z7u}ZZu zfXRQN8i%i?z)G=bTx+ng6hN2>HlAXXp6c}m(^CXm7gtJUxSU9_)k@t=z{wP>SBqDT zhH2Dl)f&sB@P$%kH373!B^P?FmJ_fnuYHJQXqz$1>%>u^0O)o?#v2`3mx2J#@l$|` zh3X_L`0X#SCg*lMRMwqSLegPm1yQfd_{JIla#-%{dN{=1suS9Q+l@K_J0=WL*HDt; z;#N}sEzfGB8ZOBLhZ3a&)9FP_Tz)Jy^tfn6`JAlbyyjEa6>DTkojag)17{~?4wzC?@{)$4N5cnE2f^CV8mL2Xs#6bPX>X;gH>< z1ES7lHM`>j@*}e!JGbJu%TMDK7~doh6l>sEP2`nvn<`SBJFM*nZsbU)8AB(MGwOUJ z*DT~|jp?DsVRKq!Rwys3bBp;f^cZi)zzYe0))MTXZEQhzD;6E^i!3{GcM!E3ztEk|QLchGMn?$1y#0QO3Pk_IxVar9_YdL;XblnWdituP80kEjN7P7t_=qDO0TmTf!T$c($!4(Y={opxN+;ub8sHEJ-avO%X5 zOVlZ|LX>Wj8)-sZArt#yg^8;K!`gBDQ1_JI50qy8CjgpnZKL-!sFlk0>AR@4>%Txm z?bB+aa@T*CoQ~bx?jWP$Mhs-NUKjlpz|9!Q<6F?_GI48~K4RE@+jA@*heX^yB$2~x zMxoc$1^FIRXxTW`HIQE=G6B7Dnt(i(^Pj*Eppp*)Jm?I6{FwITT?iEo*~C!xDKF_W z8Ll7I_bL08JVfdVG>A6=p(dYWTwl(-o+;rrBW4t?XEMYpV6|K-JBQEq*g@rFmhf?n zk+wcfZ3e`|%aWfIBdas7VD+&aw&0L5u$dd!#Oa)Y${Sh6rh!hyQIMznKq-^SWwIFp z_mMkn-ar*Mu!HPmmO?l{1QM~BM*LP*rK`sGcvSW^y6POqyVf`a)rgyoXV<>9j!O z(^slqjz9BYv1k2S9OVW#8`{?NJ^-3w8`&8CkZL>`wt*RDf!s@}Ca5Vq8TJ@;m6y~S zmc-Oq6cLr%7%Q|~&jCf_$zP1Mm3}yb0EX|oluS<3Q)Bq9OR;c;Zo8w3!}nZzsb|_5 zy5mxWT%kj-cbrWiG)w5v4R^2JN}@xV;tJhv_l{};(Q2;H&35m&lR%Vrt~_<|O=PX) om)>1vJTbrBo(MlxL+xs;2!~ea&9Bl)C1LZA6RsaA1 literal 0 HcmV?d00001 diff --git a/src/IOEngine_select.c b/src/IOEngine_select.c index aff5fb9..bdfee33 100644 --- a/src/IOEngine_select.c +++ b/src/IOEngine_select.c @@ -33,10 +33,13 @@ static int engine_select_init() { return 1; } -static void engine_select_add(struct IODescriptor *iofd) { +static void engine_select_add(struct IOLowlevelDescriptor *iold) { #ifdef WIN32 - if(iofd->type == IOTYPE_STDIN) - SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE),ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT); + struct IODescriptor *iofd; + if((iofd = IOLOWLEVEL_GET_IOFD(iold))) { + if(iofd->type == IOTYPE_STDIN) + SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE),ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT); + } #endif /* empty */ } @@ -53,7 +56,8 @@ static void engine_select_loop(struct timeval *timeout) { fd_set read_fds; fd_set write_fds; unsigned int fds_size = 0; - struct IODescriptor *iofd, *tmp_iofd; + struct IOLowlevelDescriptor *iold, *tmp_iold; + struct IODescriptor *iofd; struct timeval now, tdiff; int select_result; int timer_fix; @@ -65,9 +69,10 @@ static void engine_select_loop(struct timeval *timeout) { FD_ZERO(&write_fds); select_result = 0; - for(iofd = first_descriptor; iofd; iofd = tmp_iofd) { - tmp_iofd = iofd->next; - if(iofd->type == IOTYPE_STDIN) { + for(iold = first_descriptor; iold; iold = tmp_iold) { + tmp_iold = iold->next; + iofd = IOLOWLEVEL_GET_IOFD(iold); + if(iofd && iofd->type == IOTYPE_STDIN) { #ifdef WIN32 //WIN32 doesn't support stdin within select //just try to read the single events from the console @@ -98,20 +103,21 @@ static void engine_select_loop(struct timeval *timeout) { timeout->tv_usec = 100000; } #else - if(iofd->fd > fds_size) - fds_size = iofd->fd; - FD_SET(iofd->fd, &read_fds); + if(iold->fd > fds_size) + fds_size = iold->fd; + FD_SET(iold->fd, &read_fds); select_result++; #endif } - else if(iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT) { - if(iofd->state == IO_CLOSED) + else if(iold->fd >= 0) { + if(iofd && iofd->state == IO_CLOSED) continue; - if(iofd->fd > fds_size) - fds_size = iofd->fd; - FD_SET(iofd->fd, &read_fds); + if(iold->fd > fds_size) + fds_size = iold->fd; select_result++; - if(iohandler_wants_writes(iofd)) + if(iold->flags & IOFLAGS_WANT_READ) + FD_SET(iold->fd, &read_fds); + if(iold->flags & IOFLAGS_WANT_WRITE) FD_SET(iofd->fd, &write_fds); } } @@ -120,10 +126,14 @@ static void engine_select_loop(struct timeval *timeout) { tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec; tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec; if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) { - if(timer_priority->constant_timeout) { + iofd = IOLOWLEVEL_GET_IOFD(iold); + if(iofd && iofd->constant_timeout) { tdiff.tv_sec = 0; - iohandler_set_timeout(timer_priority, &tdiff); - iohandler_events(timer_priority, 0, 0); + iohandler_set_timeout(iofd, &tdiff); + if(iofd) + iohandler_events(timer_priority, 0, 0); + else + timer_priority->data.callback(timer_priority, 0, 0); } else { iohandler_events(timer_priority, 0, 0); iohandler_close(timer_priority); //also sets timer_priority to the next timed element @@ -168,11 +178,15 @@ static void engine_select_loop(struct timeval *timeout) { gettimeofday(&now, NULL); //check all descriptors - for(iofd = first_descriptor; iofd; iofd = tmp_iofd) { - tmp_iofd = iofd->next; - if(iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT || iofd->type == IOTYPE_STDIN) { - if(FD_ISSET(iofd->fd, &read_fds) || FD_ISSET(iofd->fd, &write_fds)) { - iohandler_events(iofd, FD_ISSET(iofd->fd, &read_fds), FD_ISSET(iofd->fd, &write_fds)); + for(iold = first_descriptor; iold; iold = tmp_iold) { + tmp_iold = iold->next; + iofd = IOLOWLEVEL_GET_IOFD(iold); + if((iofd && iofd->type == IOTYPE_STDIN) || iold->fd >= 0) { + if(FD_ISSET(iold->fd, &read_fds) || FD_ISSET(iold->fd, &write_fds)) { + if(iofd) + iohandler_events(iofd, FD_ISSET(iofd->fd, &read_fds), FD_ISSET(iofd->fd, &write_fds)); + else + iold->data.callback(iold, FD_ISSET(iofd->fd, &read_fds), FD_ISSET(iofd->fd, &write_fds)); continue; } } diff --git a/src/IOHandler.c b/src/IOHandler.c index 06e2bff..f652295 100644 --- a/src/IOHandler.c +++ b/src/IOHandler.c @@ -15,7 +15,9 @@ * along with this program. If not, see . */ #include "IOHandler.h" +#include "IODNSHandler.h" #include "IOEngine.h" +#include "IODNSEngine.h" #include "IOHandler_SSL.h" #include #include @@ -44,14 +46,44 @@ #define MAXLOG 1024 iohandler_log_callback *iolog_backend = NULL; -struct IODescriptor *first_descriptor = NULL; -struct IODescriptor *timer_priority = NULL; +struct IOLowlevelDescriptor *first_descriptor = NULL; +struct IOLowlevelDescriptor *timer_priority = NULL; +struct IODescriptorStartup *first_iostartup = NULL; #ifdef HAVE_PTHREAD_H static pthread_mutex_t io_thread_sync; static pthread_mutex_t io_poll_sync; #endif +#define IOSTARTUP_SRCLOOKUP 0x01 +#define IOSTARTUP_DSTLOOKUP 0x02 +#define IOSTARTUP_LOOKUPS 0x03 + +struct IODescriptorStartup { + struct IODescriptor *iofd; + struct IODNSQuery *iodns; /* dns queries for src and dst address */ + struct timeval timeout; + + unsigned int flags : 6; + unsigned int port : 16; + unsigned int iptype : 8; + unsigned int listening : 1; + unsigned int ssl : 1; + + char *srchost; + struct IODNSResult *srcaddr; + struct IODNSResult *use_srcaddr; + + char *dsthost; + struct IODNSResult *dstaddr; + struct IODNSResult *use_dstaddr; + + char *ssl_certfile; + char *ssl_keyfile; + + struct IODescriptorStartup *next, *prev; +}; + void iohandler_log(enum IOLogType type, char *text, ...) { va_list arg_list; char logBuf[MAXLOG+1]; @@ -111,165 +143,260 @@ static void iohandler_init_engine() { iohandler_ssl_init(); } -static void iohandler_append(struct IODescriptor *descriptor) { +static void iohandler_initialize_iobuf(struct IODescriptor *iofd) { + if(!iofd->readbuf.buffer) { + iofd->readbuf.buffer = malloc(IO_READ_BUFLEN + 2); + iofd->readbuf.bufpos = 0; + iofd->readbuf.buflen = IO_READ_BUFLEN; + iofd->writebuf.buffer = malloc(IO_READ_BUFLEN + 2); + iofd->writebuf.bufpos = 0; + iofd->writebuf.buflen = IO_READ_BUFLEN; + } +} + +static void iohandler_trigger_event(struct IOEvent *event) { + if(!event->iofd->callback) return; + iohandler_log(IOLOG_DEBUG, "triggering event (%s) for %s (fd: %d)", iohandler_ioeventtype_name(event->type), iohandler_iotype_name(event->iofd->type), event->iofd->fd); + event->iofd->callback(event); +} + +static void iohandler_increase_iobuf(struct IOBuffer *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 void iohandler_append_iostartup(struct IODescriptorStartup *iostartup) { + iostartup->prev = NULL; + iostartup->next = first_iostartup; + first_iostartup = iostartup; +} + +static void iohandler_remove_iostartup(struct IODescriptorStartup *iostartup) { + if(iostartup->prev) + iostartup->prev->next = iostartup->next; + else + first_iostartup = iostartup->next; + if(iostartup->next) + iostartup->next->prev = iostartup->prev; + if(iostartup->iodns) + iodns_abort(iostartup->iodns); + if(iostartup->srchost) + free(iostartup->srchost); + if(iostartup->dsthost) + free(iostartup->dsthost); + if(iostartup->srcaddr) + iodns_free_result(iostartup->srcaddr); + if(iostartup->dstaddr) + iodns_free_result(iostartup->dstaddr); + if(iostartup->iofd) { + if(iostartup->iofd->readbuf.buffer) + free(iostartup->iofd->readbuf.buffer); + if(iostartup->iofd->writebuf.buffer) + free(iostartup->iofd->writebuf.buffer); + free(iostartup->iofd); + } + free(iostartup); +} + +static void iohandler_append(struct IOLowlevelDescriptor *iold) { IOSYNCHRONIZE(io_thread_sync); - struct timeval *timeout = ((descriptor->timeout.tv_sec || descriptor->timeout.tv_usec) ? &descriptor->timeout : NULL); + struct timeval *timeout = ((iold->timeout.tv_sec || iold->timeout.tv_usec) ? &iold->timeout : NULL); if(timeout) { - struct IODescriptor *iofd; + struct IOLowlevelDescriptor *iold2; int set_priority = 1; - descriptor->timeout = *timeout; if(timer_priority) - iofd = timer_priority; + iold2 = timer_priority; else - iofd = first_descriptor; - if(iofd) { - for(;;iofd = iofd->next) { - if(timeval_is_smaler(timeout, (&iofd->timeout))) { - descriptor->prev = iofd->prev; - descriptor->next = iofd; - if(iofd->prev) - iofd->prev->next = descriptor; - iofd->prev = descriptor; + iold2 = first_descriptor; + if(iold2) { + for(;;iold2 = iold2->next) { + if(timeval_is_smaler(timeout, (&iold2->timeout))) { + iold->prev = iold2->prev; + iold->next = iold2; + if(iold2->prev) + iold2->prev->next = iold; + iold2->prev = iold; if(set_priority) - timer_priority = descriptor; + timer_priority = iold; break; } - if(iofd == timer_priority) + if(iold2 == timer_priority) set_priority = 0; - if(iofd->next == NULL) { - descriptor->next = NULL; - descriptor->prev = iofd; - iofd->next = descriptor; + if(iold2->next == NULL) { + iold->next = NULL; + iold->prev = iold2; + iold2->next = iold; if(set_priority) - timer_priority = descriptor; + timer_priority = iold; break; } } } else { - descriptor->prev = NULL; - descriptor->next = NULL; - first_descriptor = descriptor; - timer_priority = descriptor; + iold->prev = NULL; + iold->next = NULL; + first_descriptor = iold; + timer_priority = iold; } - } else { - descriptor->prev = NULL; - descriptor->next = first_descriptor; + iold->prev = NULL; + iold->next = first_descriptor; if(first_descriptor) - first_descriptor->prev = descriptor; - first_descriptor = descriptor; + first_descriptor->prev = iold; + first_descriptor = iold; } IODESYNCHRONIZE(io_thread_sync); } -static void iohandler_remove(struct IODescriptor *descriptor, int engine_remove) { - //remove IODescriptor from the list +static void iohandler_remove(struct IOLowlevelDescriptor *iold) { + //remove IOLowlevelDescriptor from the list IOSYNCHRONIZE(io_thread_sync); - if(descriptor->prev) - descriptor->prev->next = descriptor->next; + if(iold->prev) + iold->prev->next = iold->next; else - first_descriptor = descriptor->next; - if(descriptor->next) - descriptor->next->prev = descriptor->prev; - if(descriptor == timer_priority) - timer_priority = descriptor->next; - - if(engine_remove) - engine->remove(descriptor); - if(descriptor->readbuf.buffer) - free(descriptor->readbuf.buffer); - if(descriptor->writebuf.buffer) - free(descriptor->writebuf.buffer); - iohandler_log(IOLOG_DEBUG, "removed IODescriptor (%d) of type `%s`", descriptor->fd, iohandler_iotype_name(descriptor->type)); - free(descriptor); + first_descriptor = iold->next; + if(iold->next) + iold->next->prev = iold->prev; + if(iold == timer_priority) + timer_priority = iold->next; + + struct IODescriptor *iofd = IOLOWLEVEL_GET_IOFD(iold); + if(iofd) { + if(iofd->readbuf.buffer) + free(iofd->readbuf.buffer); + if(iofd->writebuf.buffer) + free(iofd->writebuf.buffer); + iohandler_log(IOLOG_DEBUG, "removed IODescriptor (%d) of type `%s`", iold->fd, iohandler_iotype_name(iofd->type)); + free(iofd); + } + + free(iold); IODESYNCHRONIZE(io_thread_sync); } -struct IODescriptor *iohandler_add(int sockfd, enum IOType type, struct timeval *timeout, iohandler_callback *callback) { + +struct IODescriptor *iohandler_add_iofd(int sockfd, enum IOType type, struct timeval *timeout, iohandler_callback *callback) { //just add a new IODescriptor - struct IODescriptor *descriptor = calloc(1, sizeof(*descriptor)); - if(!descriptor) { + if(!engine) { + iohandler_init_engine(); + if(!engine) { + return NULL; + } + } + + struct IODescriptor *iofd = calloc(1, sizeof(*iofd)); + if(!iofd) { iohandler_log(IOLOG_ERROR, "could not allocate memory for IODescriptor in %s:%d", __FILE__, __LINE__); return NULL; } - descriptor->fd = (type == IOTYPE_STDIN ? fileno(stdin) : sockfd); - descriptor->type = type; - descriptor->state = (type == IOTYPE_STDIN ? IO_CONNECTED : IO_CLOSED); - descriptor->callback = callback; - if(timeout) { - descriptor->timeout = *timeout; - if(descriptor->timeout.tv_usec > 1000000) { - descriptor->timeout.tv_usec -= 1000000; - descriptor->timeout.tv_sec++; - } - } - if(type != IOTYPE_TIMER) { - descriptor->readbuf.buffer = malloc(IO_READ_BUFLEN + 2); - descriptor->readbuf.bufpos = 0; - descriptor->readbuf.buflen = IO_READ_BUFLEN; - descriptor->writebuf.buffer = malloc(IO_READ_BUFLEN + 2); - descriptor->writebuf.bufpos = 0; - descriptor->writebuf.buflen = IO_READ_BUFLEN; + + struct IOLowlevelDescriptor *iold = calloc(1, sizeof(*iofd)); + if(!iold) { + iohandler_log(IOLOG_ERROR, "could not allocate memory for IOLowlevelDescriptor in %s:%d", __FILE__, __LINE__); + free(iofd); + return NULL; } - if(!engine) { - iohandler_init_engine(); - if(!engine) { - return NULL; + iold->fd = (type == IOTYPE_STDIN ? fileno(stdin) : sockfd); + iold->flags = IOFLAGS_HAVE_IOFD | IOFLAGS_WANT_READ; + iold->data.iofd = iofd; + + iofd->fd.iold = iold; + iofd->flags = IOFDFLAGS_HAVE_IOLD; + iofd->type = type; + iofd->state = (type == IOTYPE_STDIN ? IO_CONNECTED : IO_CLOSED); + iofd->callback = callback; + + if(timeout) { + iold->timeout = *timeout; + if(iold->timeout.tv_usec > 1000000) { + iold->timeout.tv_usec -= 1000000; + iold->timeout.tv_sec++; } } - engine->add(descriptor); - //add IODescriptor to the list - iohandler_append(descriptor); + if(type != IOTYPE_TIMER) + iohandler_initialize_iobuf(iofd); + + + engine->add(iold); + + //add IOLowlevelDescriptor to the list + iohandler_append(iold); iohandler_log(IOLOG_DEBUG, "added custom socket descriptor (%d) as type `%s`", sockfd, iohandler_iotype_name(type)); - return descriptor; + return iofd; } -void iohandler_set_timeout(struct IODescriptor *descriptor, struct timeval *timeout) { - if(descriptor->prev) - descriptor->prev->next = descriptor->next; - else - first_descriptor = descriptor->next; - if(descriptor->next) - descriptor->next->prev = descriptor->prev; - if(descriptor == timer_priority) - timer_priority = descriptor->next; - if(timeout && timeout->tv_sec == 0 && descriptor->constant_timeout) { - descriptor->timeout.tv_usec += (descriptor->constant_timeout % 1000) * 1000; - descriptor->timeout.tv_sec += (descriptor->constant_timeout / 1000); - if(descriptor->timeout.tv_usec > 1000000) { - descriptor->timeout.tv_sec += (descriptor->timeout.tv_usec / 1000000); - descriptor->timeout.tv_usec %= 1000000; - } - } else if(timeout) { - descriptor->timeout = *timeout; - if(descriptor->timeout.tv_usec > 1000000) { - descriptor->timeout.tv_usec -= 1000000; - descriptor->timeout.tv_sec++; - } - } else { - descriptor->timeout.tv_sec = 0; - descriptor->timeout.tv_usec = 0; - } - iohandler_append(descriptor); +struct IOLowlevelDescriptor *iohandler_lowlevel_add(int fd, int want_read, int want_write, iolowlevel_callback *callback) { + } -static void iohandler_increase_iobuf(struct IOBuffer *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; +void iohandler_lowlevel_update(struct IOLowlevelDescriptor *iolow, int want_read, int want_write) { + +} + +void iohandler_lowlevel_del(struct IOLowlevelDescriptor *iolow) { + +} + +void iohandler_set_timeout(struct IODescriptor *iofd, struct timeval *timeout) { + struct IOLowlevelDescriptor *iold = IODESCRIPTOR_GET_IOLD(iofd); + if(iold) { + if(!timeout && !(iold->flags & IOFLAGS_HAVE_TIMEOUT)) + return; //nothing to do + + if(iold->prev) + iold->prev->next = iold->next; + else + first_descriptor = iold->next; + if(iold->next) + iold->next->prev = iold->prev; + if(iold == timer_priority) + timer_priority = iold->next; + + if(timeout && timeout->tv_sec == 0 && iofd->constant_timeout) { // autoreload timer + iold->timeout.tv_usec += (iofd->constant_timeout % 1000) * 1000; + iold->timeout.tv_sec += (iofd->constant_timeout / 1000); + if(iold->timeout.tv_usec > 1000000) { + iold->timeout.tv_sec += (iold->timeout.tv_usec / 1000000); + iold->timeout.tv_usec %= 1000000; + } + iold->flags |= IOFLAGS_HAVE_TIMEOUT; + } else if(timeout) { // normal timer + iold->timeout = *timeout; + if(iold->timeout.tv_usec > 1000000) { + iold->timeout.tv_usec -= 1000000; + iold->timeout.tv_sec++; + } + iold->flags |= IOFLAGS_HAVE_TIMEOUT; + } else + iold->flags &= ~IOFLAGS_HAVE_TIMEOUT; + iohandler_append(iold); + } else { + struct IODescriptorStartup *iostartup = iofd->fd.iostartup; + + if(timeout) { + iostartup->timeout = *timeout; + if(iostartup->timeout.tv_usec > 1000000) { + iostartup->timeout.tv_usec -= 1000000; + iostartup->timeout.tv_sec++; + } + } else { + iostartup->timeout.tv_sec = 0; + iostartup->timeout.tv_usec = 0; + } } } struct IODescriptor *iohandler_timer(struct timeval timeout, iohandler_callback *callback) { struct IODescriptor *descriptor; - descriptor = iohandler_add(-1, IOTYPE_TIMER, &timeout, callback); + descriptor = iohandler_add_iofd(-1, IOTYPE_TIMER, &timeout, callback); if(!descriptor) { - iohandler_log(IOLOG_ERROR, "could not allocate memory for IODescriptor in %s:%d", __FILE__, __LINE__); + iohandler_log(IOLOG_ERROR, "could not create IODescriptor in %s:%d", __FILE__, __LINE__); return NULL; } iohandler_log(IOLOG_DEBUG, "added timer descriptor (sec: %d; usec: %d)", timeout.tv_sec, timeout.tv_usec); @@ -286,7 +413,7 @@ struct IODescriptor *iohandler_constant_timer(int msec, iohandler_callback *call timeout.tv_sec += (timeout.tv_usec / 1000000); timeout.tv_usec %= 1000000; } - descriptor = iohandler_add(-1, IOTYPE_TIMER, &timeout, callback); + descriptor = iohandler_add_iofd(-1, IOTYPE_TIMER, &timeout, callback); if(!descriptor) { iohandler_log(IOLOG_ERROR, "could not allocate memory for IODescriptor in %s:%d", __FILE__, __LINE__); return NULL; @@ -296,142 +423,273 @@ struct IODescriptor *iohandler_constant_timer(int msec, iohandler_callback *call return descriptor; } -struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, int ssl, const char *bindhost, iohandler_callback *callback) { - return iohandler_connect_flags(hostname, port, ssl, bindhost, callback, IOHANDLER_CONNECT_IPV4 | IOHANDLER_CONNECT_IPV6); -} - -struct IODescriptor *iohandler_connect_flags(const char *hostname, unsigned int port, int ssl, const char *bindhost, iohandler_callback *callback, int flags) { - //non-blocking connect - int sockfd, result; - struct addrinfo hints, *res; - struct sockaddr_in *ip4 = NULL; - struct sockaddr_in6 *ip6 = NULL; - size_t dstaddrlen; - struct sockaddr *dstaddr = NULL; - struct IODescriptor *descriptor; +static void iohandler_process_iostartup(struct IODescriptorStartup *iostartup) { + struct IODescriptor *iofd = iostartup->iofd; + struct IOLowlevelDescriptor *iold; + struct IOEvent callback_event; + struct sockaddr_in ip4; + struct sockaddr_in6 ip6; + int addr_family, sockfd, opt; - if(!engine) { - iohandler_init_engine(); - if(!engine) return NULL; + //open socket + if(iostartup->use_srcaddr) + iostartup->iptype = iostartup->use_srcaddr->type; + else if(iostartup->use_dstaddr) + iostartup->iptype = iostartup->use_dstaddr->type; + + addr_family = ((iostartup->iptype & IODNS_RECORD_AAAA) ? AF_INET6 : AF_INET); + + sockfd = socket(addr_family, SOCK_STREAM, 0); + if(!sockfd) { + callback_event.type = IOEVENT_SOCKET_ERROR; + goto iohandler_process_iostartup_fail; } - memset (&hints, 0, sizeof (hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags |= AI_CANONNAME; - if ((result = getaddrinfo (hostname, NULL, &hints, &res))) { - iohandler_log(IOLOG_ERROR, "could not resolve %s to an IP address (%d)", hostname, result); - return NULL; + + //prevent SIGPIPE + #ifndef WIN32 + #if defined(SO_NOSIGPIPE) + opt = 1; + setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&opt, sizeof(opt)); + #else + signal(SIGPIPE, SIG_IGN); + #endif + #endif + //make sockfd unblocking + #if defined(F_GETFL) + opt = fcntl(sockfd, F_GETFL); + fcntl(sockfd, F_SETFL, opt|O_NONBLOCK); + opt = fcntl(sockfd, F_GETFD); + fcntl(sockfd, F_SETFD, opt|FD_CLOEXEC); + #endif + + if(iostartup->listening) { + opt = 1; + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt)); + } else { + if((iostartup->iptype & IODNS_RECORD_AAAA)) { + struct sockaddr_in6 *ip6ptr = iostartup->use_srcaddr->address; + ip6ptr->sin6_family = addr_family; + ip6ptr->sin6_port = htons(iostartup->port); + } else { + struct sockaddr_in *ip4ptr = iostartup->use_srcaddr->address; + ip4ptr->sin_family = addr_family; + ip4ptr->sin_port = htons(iostartup->port); + } } - while (res) { - switch (res->ai_family) { - case AF_INET: - ip4 = (struct sockaddr_in *) res->ai_addr; + + // Bind Socket to IP + if(iostartup->listening || iostartup->use_srcaddr) { + if((iostartup->iptype & IODNS_RECORD_AAAA)) { + memset(&ip6, 0, sizeof(ip6)); + if(iostartup->use_srcaddr) { + struct sockaddr_in6 *ip6ptr = iostartup->use_srcaddr->address; + ip6.sin6_addr = ip6ptr->sin6_addr; + } else + inet_pton(AF_INET6, "::1", &ip6.sin6_addr); + ip6.sin6_family = addr_family; + ip6.sin6_port = htons((iostartup->listening ? iostartup->port : 0)); + opt = bind(sockfd, (struct sockaddr*)&ip6, sizeof(ip6)); + } else { + memset(&ip6, 0, sizeof(ip6)); + if(iostartup->use_srcaddr) { + struct sockaddr_in4 *ip4ptr = iostartup->use_srcaddr->address; + ip6.sin6_addr = ip4ptr->sin_addr; + } else + ip4.sin_addr = INADDR_ANY; + ip4.sin_family = addr_family; + ip4.sin_port = htons((iostartup->listening ? iostartup->port : 0)); + opt = bind(sockfd, (struct sockaddr*)&ip4, sizeof(ip4)); + } + if(!opt) { + callback_event.type = IOEVENT_BIND_ERROR; + goto iohandler_process_iostartup_fail; + } + } + + iold = calloc(1, sizeof(*iold)); + iold->fd = sockfd; + iold->flags = IOFLAGS_HAVE_IOFD | IOFLAGS_WANT_READ; + iold->data.iofd = iofd; + + if(iostartup->listening) { + listen(sockfd, 1); + iofd->state = IO_LISTENING; + } else { + connect(sockfd, iostartup->use_dstaddr->address, iostartup->use_dstaddr->addresslen); + iofd->state = IO_CONNECTING; + } + + iofd->fd.iold = iold; + iofd->flags = IOFDFLAGS_HAVE_IOLD; + + iostartup->iofd = NULL; + iohandler_remove_iostartup(iostartup); + return; + + iohandler_process_iostartup_fail: + if(sockfd) + close(sockfd); + callback_event.iofd = iostartup->iofd; + iostartup->iofd->flags |= IOFDFLAGS_FREE_LOCK; + iohandler_trigger_event(&callback_event); + iostartup->iofd->flags &= ~IOFDFLAGS_FREE_LOCK; + if(iostartup->iofd->flags & IOFDFLAGS_WANT_FREE) + iohandler_remove_iostartup(iostartup); + else + iohandler_close(iostartup->iofd); +} + +static IODNS_CALLBACK(iohandler_dns_callback) { + struct IODNSQuery *query = event->query; + struct IODescriptorStartup *iostartup = query->data; + int trigger_dns_fail = 0; + iostartup->iodns = NULL; + + switch((iostartup->flags & IOSTARTUP_LOOKUPS)) { + case IOSTARTUP_SRCLOOKUP: + if(event->type == IODNSEVENT_FAILED) { + trigger_dns_fail = 1; break; - case AF_INET6: - ip6 = (struct sockaddr_in6 *) res->ai_addr; + } + if(iostartup->dsthost) { + iostartup->iodns = iodns_getaddrinfo(iostartup->dsthost, iostartup->iptype, iohandler_dns_callback); + iostartup->flags |= IOSTARTUP_DSTLOOKUP; + iostartup->iodns->data = iostartup; + } + break; + case IOSTARTUP_DSTLOOKUP: + if(event->type == IODNSEVENT_FAILED) { + trigger_dns_fail = 1; break; } - res = res->ai_next; - freeaddrinfo(res); + break; } - if(ip6 && (flags & IOHANDLER_CONNECT_IPV6)) { - sockfd = socket(AF_INET6, SOCK_STREAM, 0); - if(sockfd == -1) { - iohandler_log(IOLOG_ERROR, "could not create socket in %s:%d", __FILE__, __LINE__); - return NULL; + if(iostartup->iodns) + return; + + if(iostartup->iptype & IODNS_RECORD_AAAA) { + struct IODNSResult *result; + if(iostartup->srcaddr) { + for(result = iostartup->srcaddr; result; result = result->next) { + if(result->type == IODNS_RECORD_AAAA) { + iostartup->use_srcaddr = result; + break; + } + } } - - ip6->sin6_family = AF_INET6; - ip6->sin6_port = htons(port); - - struct sockaddr_in6 *ip6vhost = NULL; - if (bindhost && !getaddrinfo(bindhost, NULL, &hints, &res)) { - while (res) { - switch (res->ai_family) { - case AF_INET6: - ip6vhost = (struct sockaddr_in6 *) res->ai_addr; + if(!iostartup->srcaddr || iostartup->use_srcaddr) { + for(result = iostartup->dstaddr; result; result = result->next) { + if(result->type == IODNS_RECORD_AAAA) { + iostartup->use_dstaddr = result; break; } - res = res->ai_next; - freeaddrinfo(res); } } - if(ip6vhost) { - ip6vhost->sin6_family = AF_INET6; - ip6vhost->sin6_port = htons(0); - bind(sockfd, (struct sockaddr*)ip6vhost, sizeof(*ip6vhost)); + if((iostartup->srcaddr && !iostartup->use_srcaddr) || (iostartup->dstaddr && !iostartup->use_dstaddr)) { + iostartup->use_srcaddr = NULL; + iostartup->use_dstaddr = NULL; } - dstaddr = (struct sockaddr*)ip6; - dstaddrlen = sizeof(*ip6); - } else if(ip4 && (flags & IOHANDLER_CONNECT_IPV4)) { - sockfd = socket(AF_INET, SOCK_STREAM, 0); - if(sockfd == -1) { - iohandler_log(IOLOG_ERROR, "could not create socket in %s:%d", __FILE__, __LINE__); - return NULL; + } + if((iostartup->iptype & IODNS_RECORD_A) && !(iostartup->use_dstaddr || iostartup->use_srcaddr)) { + struct IODNSResult *result; + if(iostartup->srcaddr) { + for(result = iostartup->srcaddr; result; result = result->next) { + if(result->type == IODNS_RECORD_A) { + iostartup->use_srcaddr = result; + break; + } + } } - - ip4->sin_family = AF_INET; - ip4->sin_port = htons(port); - - struct sockaddr_in *ip4vhost = NULL; - if (bindhost && !getaddrinfo(bindhost, NULL, &hints, &res)) { - while (res) { - switch (res->ai_family) { - case AF_INET: - ip4vhost = (struct sockaddr_in *) res->ai_addr; + if(!iostartup->srcaddr || iostartup->use_srcaddr) { + for(result = iostartup->srcaddr; result; result = result->next) { + if(result->type == IODNS_RECORD_A) { + iostartup->use_dstaddr = result; break; } - res = res->ai_next; - freeaddrinfo(res); } } - if(ip4vhost) { - ip4vhost->sin_family = AF_INET; - ip4vhost->sin_port = htons(0); - bind(sockfd, (struct sockaddr*)ip4vhost, sizeof(*ip4vhost)); + if((iostartup->srcaddr && !iostartup->use_srcaddr) || (iostartup->dstaddr && !iostartup->use_dstaddr)) { + iostartup->use_srcaddr = NULL; + iostartup->use_dstaddr = NULL; } - dstaddr = (struct sockaddr*)ip4; - dstaddrlen = sizeof(*ip4); - } else - return NULL; - //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); + if(!(iostartup->use_dstaddr || iostartup->use_srcaddr)) + trigger_dns_fail = 1; + + if(trigger_dns_fail) { + struct IOEvent callback_event; + callback_event.type = IOEVENT_DNS_FAILED; + callback_event.iofd = iostartup->iofd; + iostartup->iofd->flags |= IOFDFLAGS_FREE_LOCK; + iohandler_trigger_event(&callback_event); + iostartup->iofd->flags &= ~IOFDFLAGS_FREE_LOCK; + if(iostartup->iofd->flags & IOFDFLAGS_WANT_FREE) + iohandler_remove_iostartup(iostartup); + else + iohandler_close(iostartup->iofd); + return; } - #else - /* I hope you're using the Win32 backend or something else that - * automatically marks the file descriptor non-blocking... - */ - #endif - descriptor = iohandler_add(sockfd, IOTYPE_CLIENT, NULL, callback); - if(!descriptor) { - close(sockfd); + + iohandler_process_iostartup(iostartup); +} + +struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, int ssl, const char *bindhost, iohandler_callback *callback) { + return iohandler_connect_flags(hostname, port, ssl, bindhost, callback, IOHANDLER_CONNECT_IPV4 | IOHANDLER_CONNECT_IPV6); +} + +struct IODescriptor *iohandler_connect_flags(const char *hostname, unsigned int port, int ssl, const char *bindhost, iohandler_callback *callback, int flags) { + if(!engine) { + iohandler_init_engine(); + if(!engine) return NULL; + } + + //non-blocking connect + struct IODescriptorStartup *iostartup; + struct IODescriptor *iofd; + + iofd = calloc(1, sizeof(*iofd)); + if(!iofd) { + return NULL; } - connect(sockfd, dstaddr, dstaddrlen); //returns EINPROGRESS here (nonblocking) - descriptor->state = IO_CONNECTING; - descriptor->ssl = (ssl ? 1 : 0); - descriptor->read_lines = 1; - engine->update(descriptor); - iohandler_log(IOLOG_DEBUG, "added client socket (%d) connecting to %s:%d", sockfd, hostname, port); - return descriptor; + iostartup = calloc(1, sizeof(*iostartup)); + if(!iostartup) { + + return NULL; + } + + iostartup->iofd = iofd; + iofd->fd.iostartup = iostartup; + + iofd->type = IOTYPE_CLIENT; + iofd->state = IO_PENDING; + iofd->callback = callback; + + iostartup->srchost = (bindhost ? strdup(bindhost) : NULL); + iostartup->dsthost = strdup(hostname); + iostartup->port = port; + iostartup->iptype = ((flags & IOHANDLER_CONNECT_IPV6) ? IODNS_RECORD_AAAA : 0) | ((flags & IOHANDLER_CONNECT_IPV4) ? IODNS_RECORD_A : 0); + iostartup->ssl = (ssl == 0 ? 0 : 1); + + //DNS Requests + if(bindhost) { + iostartup->iodns = iodns_getaddrinfo(bindhost, iostartup->iptype, iohandler_dns_callback); + iostartup->flags |= IOSTARTUP_SRCLOOKUP; + } else { + iostartup->iodns = iodns_getaddrinfo(hostname, iostartup->iptype, iohandler_dns_callback); + iostartup->flags |= IOSTARTUP_DSTLOOKUP; + } + iostartup->iodns->data = iostartup; + + iohandler_append_iostartup(iostartup); + + //Some Flags for later use + iofd->read_lines = 1; + + iohandler_log(IOLOG_DEBUG, "added client socket connecting to %s:%d (DNS LOOKUP)", hostname, port); + + return iofd; } struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, iohandler_callback *callback) { @@ -439,96 +697,50 @@ struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, i } struct IODescriptor *iohandler_listen_flags(const char *hostname, unsigned int port, iohandler_callback *callback, int flags) { - int sockfd; - struct addrinfo hints, *res; - struct sockaddr_in *ip4 = NULL; - struct sockaddr_in6 *ip6 = NULL; - struct IODescriptor *descriptor; - unsigned int opt; - if(!engine) { iohandler_init_engine(); if(!engine) return NULL; } - memset (&hints, 0, sizeof (hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags |= AI_CANONNAME; - if (getaddrinfo (hostname, NULL, &hints, &res)) { - return NULL; - } - while (res) { - switch (res->ai_family) { - case AF_INET: - ip4 = (struct sockaddr_in *) res->ai_addr; - break; - case AF_INET6: - ip6 = (struct sockaddr_in6 *) res->ai_addr; - break; - } - res = res->ai_next; - freeaddrinfo(res); - } - if(ip6 && (flags & IOHANDLER_LISTEN_IPV6)) { - sockfd = socket(AF_INET6, SOCK_STREAM, 0); - if(sockfd == -1) return NULL; - - opt = 1; - setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt)); - - ip6->sin6_family = AF_INET6; - ip6->sin6_port = htons(port); - - bind(sockfd, (struct sockaddr*)ip6, sizeof(*ip6)); - } else if(ip4 && (flags & IOHANDLER_LISTEN_IPV4)) { - sockfd = socket(AF_INET, SOCK_STREAM, 0); - if(sockfd == -1) return NULL; - - opt = 1; - setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt)); - - ip4->sin_family = AF_INET; - ip4->sin_port = htons(port); + //non-blocking listening socket + struct IODescriptorStartup *iostartup; + struct IODescriptor *iofd; + + iofd = calloc(1, sizeof(*iofd)); + if(!iofd) { - bind(sockfd, (struct sockaddr*)ip4, sizeof(*ip4)); - } else return NULL; - //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 flag; - flag = fcntl(sockfd, F_GETFL); - fcntl(sockfd, F_SETFL, flag|O_NONBLOCK); - flag = fcntl(sockfd, F_GETFD); - fcntl(sockfd, F_SETFD, flag|FD_CLOEXEC); } - #else - /* I hope you're using the Win32 backend or something else that - * automatically marks the file descriptor non-blocking... - */ - #endif - descriptor = iohandler_add(sockfd, IOTYPE_SERVER, NULL, callback); - if(!descriptor) { - close(sockfd); + iostartup = calloc(1, sizeof(*iostartup)); + if(!iostartup) { + return NULL; } - listen(sockfd, 1); - descriptor->state = IO_LISTENING; - engine->update(descriptor); - iohandler_log(IOLOG_DEBUG, "added server socket (%d) listening on %s:%d", sockfd, hostname, port); - return descriptor; + + iostartup->iofd = iofd; + iofd->fd.iostartup = iostartup; + + iofd->type = IOTYPE_SERVER; + iofd->state = IO_PENDING; + iofd->callback = callback; + + iostartup->srchost = (hostname ? strdup(hostname) : NULL); + iostartup->port = port; + iostartup->iptype = ((flags & IOHANDLER_LISTEN_IPV6) ? IODNS_RECORD_AAAA : 0) | ((flags & IOHANDLER_LISTEN_IPV4) ? IODNS_RECORD_A : 0); + iostartup->ssl = 0; + iostartup->listening = 1; + + //DNS Requests + if(hostname) { + iostartup->iodns = iodns_getaddrinfo(hostname, iostartup->iptype, iohandler_dns_callback); + iostartup->flags |= IOSTARTUP_SRCLOOKUP; + iostartup->iodns->data = iostartup; + iohandler_append_iostartup(iostartup); + } else + iohandler_process_iostartup(iostartup); + + iohandler_log(IOLOG_DEBUG, "added server socket listening on %s:%d", hostname, port); + return iofd; } struct IODescriptor *iohandler_listen_ssl(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iohandler_callback *callback) { @@ -536,14 +748,50 @@ struct IODescriptor *iohandler_listen_ssl(const char *hostname, unsigned int por } struct IODescriptor *iohandler_listen_ssl_flags(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iohandler_callback *callback, int flags) { - struct IODescriptor *descriptor = iohandler_listen_flags(hostname, port, callback, flags); - if(!descriptor) + if(!engine) { + iohandler_init_engine(); + if(!engine) return NULL; + } + + //non-blocking listening socket + struct IODescriptorStartup *iostartup; + struct IODescriptor *iofd; + + iofd = calloc(1, sizeof(*iofd)); + if(!iofd) { + return NULL; - //SSL Server Socket - iohandler_ssl_listen(descriptor, certfile, keyfile); - if(descriptor->sslnode) - descriptor->ssl = 1; - return descriptor; + } + iostartup = calloc(1, sizeof(*iostartup)); + if(!iostartup) { + + return NULL; + } + + iostartup->iofd = iofd; + iofd->fd.iostartup = iostartup; + + iofd->type = IOTYPE_SERVER; + iofd->state = IO_PENDING; + iofd->callback = callback; + + iostartup->srchost = (hostname ? strdup(hostname) : NULL); + iostartup->port = port; + iostartup->iptype = ((flags & IOHANDLER_LISTEN_IPV6) ? IODNS_RECORD_AAAA : 0) | ((flags & IOHANDLER_LISTEN_IPV4) ? IODNS_RECORD_A : 0); + iostartup->ssl = 1; + iostartup->ssl_certfile = strdup(certfile); + iostartup->ssl_keyfile = strdup(keyfile); + iostartup->listening = 1; + + //DNS Requests + if(hostname) { + iostartup->iodns = iodns_getaddrinfo(hostname, iostartup->iptype, iohandler_dns_callback); + iohandler_append_iostartup(iostartup); + } else + iohandler_process_iostartup(iostartup); + + iohandler_log(IOLOG_DEBUG, "added SSL server socket listening on %s:%d", hostname, port); + return iofd; } void iohandler_write(struct IODescriptor *iofd, const char *line) { @@ -567,7 +815,11 @@ void iohandler_send(struct IODescriptor *iofd, const char *data, size_t datalen) } memcpy(iofd->writebuf.buffer + iofd->writebuf.bufpos, data, datalen); iofd->writebuf.bufpos += datalen; - engine->update(iofd); + struct IOLowlevelDescriptor *iold = IODESCRIPTOR_GET_IOLD(iofd); + if(iold) { + iold->flags |= IOFLAGS_WANT_WRITE; + engine->update(iold); + } } void iohandler_printf(struct IODescriptor *iofd, const char *text, ...) { @@ -585,78 +837,94 @@ void iohandler_printf(struct IODescriptor *iofd, const char *text, ...) { } static int iohandler_try_write(struct IODescriptor *iofd) { - if(!iofd->writebuf.bufpos) return 0; + struct IOLowlevelDescriptor *iold = IODESCRIPTOR_GET_IOLD(iofd); + if(!iofd->writebuf.bufpos || !iold) return 0; iohandler_log(IOLOG_DEBUG, "write writebuf (%d bytes) to socket (fd: %d)", iofd->writebuf.bufpos, iofd->fd); int res; if(iofd->ssl_active) res = iohandler_ssl_write(iofd, iofd->writebuf.buffer, iofd->writebuf.bufpos); else - res = send(iofd->fd, iofd->writebuf.buffer, iofd->writebuf.bufpos, 0); + res = send(iofd->fd.iold->fd, iofd->writebuf.buffer, iofd->writebuf.bufpos, 0); if(res < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK) - iohandler_log(IOLOG_ERROR, "could not write to socket (fd: %d): %d - %s", iofd->fd, errno, strerror(errno)); + iohandler_log(IOLOG_ERROR, "could not write to socket (fd: %d): %d - %s", iofd->fd.iold->fd, errno, strerror(errno)); else res = 0; } else { iofd->writebuf.bufpos -= res; - if(iofd->state != IO_CLOSED) - engine->update(iofd); + if(iofd->writebuf.bufpos) + iold->flags |= IOFLAGS_WANT_WRITE; + else + iold->flags &= ~IOFLAGS_WANT_WRITE; + engine->update(iold); } return res; } void iohandler_close(struct IODescriptor *iofd) { + struct IOLowlevelDescriptor *iold = IODESCRIPTOR_GET_IOLD(iofd); int engine_remove = 1; iofd->state = IO_CLOSED; - if(iofd->writebuf.bufpos) { - //try to send everything before closing + if(iold) { + if(iofd->writebuf.bufpos) { + //try to send everything before closing #if defined(F_GETFL) - { - int flags; - flags = fcntl(iofd->fd, F_GETFL); - fcntl(iofd->fd, F_SETFL, flags & ~O_NONBLOCK); - flags = fcntl(iofd->fd, F_GETFD); - fcntl(iofd->fd, F_SETFD, flags|FD_CLOEXEC); - } + { + int flags; + flags = fcntl(iofd->fd, F_GETFL); + fcntl(iofd->fd, F_SETFL, flags & ~O_NONBLOCK); + flags = fcntl(iofd->fd, F_GETFD); + fcntl(iofd->fd, F_SETFD, flags|FD_CLOEXEC); + } #else - engine_remove = 0; - engine->remove(iofd); + engine_remove = 0; + engine->remove(iofd); #endif - iohandler_try_write(iofd); + iohandler_try_write(iofd); + } + if(iofd->ssl) + iohandler_ssl_disconnect(iofd); + if((iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT || iofd->type == IOTYPE_STDIN)) + close(iold->fd); + if(engine_remove) + engine->remove(iold); + if(iofd->flags & IOFDFLAGS_FREE_LOCK) + iofd->flags |= IOFDFLAGS_WANT_FREE; + else + iohandler_remove(iold); + } else { + if(iofd->flags & IOFDFLAGS_FREE_LOCK) + iofd->flags |= IOFDFLAGS_WANT_FREE; + else + iohandler_remove_iostartup(iofd->fd.iostartup); } - //close IODescriptor - if(iofd->ssl) - iohandler_ssl_disconnect(iofd); - if(iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT || iofd->type == IOTYPE_STDIN) - close(iofd->fd); - iohandler_remove(iofd, engine_remove); } void iohandler_update(struct IODescriptor *iofd) { - iohandler_log(IOLOG_DEBUG, "external call to iohandler_update (fd: %d)", iofd->fd); - engine->update(iofd); -} - -static void iohandler_trigger_event(struct IOEvent *event) { - if(!event->iofd->callback) return; - iohandler_log(IOLOG_DEBUG, "triggering event (%s) for %s (fd: %d)", iohandler_ioeventtype_name(event->type), iohandler_iotype_name(event->iofd->type), event->iofd->fd); - event->iofd->callback(event); + iohandler_log(IOLOG_DEBUG, "external call to iohandler_update"); + struct IOLowlevelDescriptor *iold = IODESCRIPTOR_GET_IOLD(iofd); + if(iold) + engine->update(iold); } void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { + struct IOLowlevelDescriptor *iold = IODESCRIPTOR_GET_IOLD(iofd); struct IOEvent callback_event; callback_event.type = IOEVENT_IGNORE; callback_event.iofd = iofd; + int remove_iofd = 0; + if(!iold) + return; switch(iofd->state) { case IO_SSLWAIT: if(!readable && !writeable) { - if(!iofd->ssl_server_hs) { + if(!(iofd->flags & IOFDFLAGS_SSL_SERVER_HS)) { callback_event.type = IOEVENT_SSLFAILED; iofd->state = IO_CLOSED; - engine->update(iofd); - } else - iohandler_close(iofd); - } else if(iofd->ssl_server_hs) { + engine->update(iold); + } + remove_iofd = 1; + } else if((iofd->flags & IOFDFLAGS_SSL_SERVER_HS)) { iohandler_log(IOLOG_DEBUG, "triggering iohandler_ssl_server_handshake for %s (fd: %d)", iohandler_iotype_name(iofd->type), iofd->fd); iohandler_ssl_server_handshake(iofd); } else { @@ -665,19 +933,25 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { } break; case IO_CLOSED: - if(iofd->type == IOTYPE_TIMER) + if(iofd->type == IOTYPE_TIMER) // Timers will be removed by IOEngine callback_event.type = IOEVENT_TIMEOUT; + else + remove_iofd = 1; break; case IO_LISTENING: if(readable) { - callback_event.data.accept_fd = accept(iofd->fd, NULL, 0); - if(callback_event.data.accept_fd < 0) { + int accept_fd = accept(iold->fd, NULL, 0); + if(accept_fd < 0) { iohandler_log(IOLOG_ERROR, "could not accept client (server fd: %d): %d - %s", iofd->fd, errno, strerror(errno)); - } else if(iofd->ssl) { - struct IODescriptor *client_iofd = iohandler_add(callback_event.data.accept_fd, IOTYPE_CLIENT, NULL, NULL); - iohandler_ssl_client_accepted(iofd, client_iofd); - } else - callback_event.type = IOEVENT_ACCEPT; + } else { + struct IODescriptor *client_iofd = iohandler_add(accept_fd, IOTYPE_CLIENT, NULL, NULL); + if(iofd->ssl) + iohandler_ssl_client_accepted(iofd, client_iofd); + else { + callback_event.type = IOEVENT_ACCEPT; + callback_event.data.accept_iofd = client_iofd; + } + } } break; case IO_CONNECTING: @@ -688,14 +962,15 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { //if (getsockopt(iofd->fd, SOL_SOCKET, SO_ERROR, &callback_event.data.errid, &arglen) < 0) // callback_event.data.errid = errno; iofd->state = IO_CLOSED; - engine->update(iofd); + engine->update(iold); + remove_iofd = 1; } else if(writeable) { - if(iofd->ssl && !iofd->ssl_active) { + if(iofd->ssl && !(iofd->flags & IOFDFLAGS_SSL_ACTIVE)) { iohandler_log(IOLOG_DEBUG, "triggering iohandler_ssl_connect for %s (fd: %d)", iohandler_iotype_name(iofd->type), iofd->fd); iohandler_ssl_connect(iofd); return; } - if(iofd->ssl && iofd->ssl_server_hs) { + if(iofd->ssl && (iofd->flags & IOFDFLAGS_SSL_SERVER_HS)) { callback_event.type = IOEVENT_SSLACCEPT; callback_event.iofd = iofd->data; callback_event.data.accept_iofd = iofd; @@ -704,7 +979,7 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { else callback_event.type = IOEVENT_CONNECTED; iofd->state = IO_CONNECTED; - engine->update(iofd); + engine->update(iold); } break; case IO_CONNECTED: @@ -712,28 +987,29 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { if(iofd->read_lines) { int bytes; - if(iofd->ssl_active) + if((iofd->flags & IOFDFLAGS_SSL_ACTIVE)) bytes = iohandler_ssl_read(iofd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos); else { if(iofd->type == IOTYPE_STDIN) #ifdef WIN32 bytes = readable; #else - bytes = read(iofd->fd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos); + bytes = read(iold->fd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos); #endif else - bytes = recv(iofd->fd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos, 0); + bytes = recv(iold->fd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos, 0); } if(bytes <= 0) { if (errno != EAGAIN || errno != EWOULDBLOCK) { iofd->state = IO_CLOSED; - engine->update(iofd); + engine->update(iold); callback_event.type = IOEVENT_CLOSED; callback_event.data.errid = errno; + remove_iofd = 1; } } else { int i, used_bytes = 0; - iohandler_log(IOLOG_DEBUG, "received %d bytes (fd: %d). readbuf position: %d", bytes, iofd->fd, iofd->readbuf.bufpos); + iohandler_log(IOLOG_DEBUG, "received %d bytes (fd: %d). readbuf position: %d", bytes, iold->fd, iofd->readbuf.bufpos); iofd->readbuf.bufpos += bytes; callback_event.type = IOEVENT_RECV; for(i = 0; i < iofd->readbuf.bufpos; i++) { @@ -744,7 +1020,11 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { callback_event.data.recv_str = iofd->readbuf.buffer + used_bytes; iohandler_log(IOLOG_DEBUG, "parsed line (%d bytes): %s", i - used_bytes, iofd->readbuf.buffer + used_bytes); used_bytes = i+1; + iofd->flags |= IOFDFLAGS_FREE_LOCK; iohandler_trigger_event(&callback_event); + iofd->flags &= ~IOFDFLAGS_FREE_LOCK; + if(iofd->flags & IOFDFLAGS_WANT_FREE) + break; } else if(i + 1 - used_bytes >= IO_LINE_LEN) { //512 max iofd->readbuf.buffer[i] = 0; callback_event.data.recv_str = iofd->readbuf.buffer + used_bytes; @@ -755,7 +1035,11 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { } } used_bytes = i+1; + iofd->flags |= IOFDFLAGS_FREE_LOCK; iohandler_trigger_event(&callback_event); + iofd->flags &= ~IOFDFLAGS_FREE_LOCK; + if(iofd->flags & IOFDFLAGS_WANT_FREE) + break; } } if(used_bytes) { @@ -778,17 +1062,27 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { bytes = iohandler_try_write(iofd); if(bytes < 0) { iofd->state = IO_CLOSED; - engine->update(iofd); + engine->update(iold); callback_event.type = IOEVENT_CLOSED; callback_event.data.errid = errno; + remove_iofd = 1; } } break; + case IO_PENDING: + /* nothing to do for pending descriptors */ + break; } + iofd->flags |= IOFDFLAGS_FREE_LOCK; if(callback_event.type == IOEVENT_IGNORE && !readable && !writeable) callback_event.type = IOEVENT_TIMEOUT; if(callback_event.type != IOEVENT_IGNORE) iohandler_trigger_event(&callback_event); + iofd->flags &= ~IOFDFLAGS_FREE_LOCK; + if(iofd->flags & IOFDFLAGS_WANT_FREE) + iohandler_remove(iold); + else if(remove_iofd) + iohandler_close(iofd); } void iohandler_poll() { @@ -799,6 +1093,7 @@ void iohandler_poll() { } void iohandler_poll_timeout(struct timeval timeout) { + iodns_poll(); if(engine) { IOSYNCHRONIZE(io_poll_sync); //quite senceless multithread support... better support will follow engine->loop(&timeout); diff --git a/src/IOHandler.h b/src/IOHandler.h index eb32cdc..b52f552 100644 --- a/src/IOHandler.h +++ b/src/IOHandler.h @@ -27,6 +27,8 @@ struct timeval; struct IODescriptor; struct IOEvent; struct IOSSLNode; +struct IODNSQuery; +struct IODNSEvent; enum IOLogType { IOLOG_DEBUG, @@ -52,6 +54,7 @@ enum IOType { }; enum IOStatus { + IO_PENDING, /* descriptor is initializing */ IO_CLOSED, /* descriptor is dead (socket waiting for removal or timer) */ IO_LISTENING, /* descriptor is waiting for connections (server socket) */ IO_CONNECTING, /* descriptor is waiting for connection approval (connecting client socket) */ @@ -66,10 +69,14 @@ enum IOEventType { IOEVENT_CONNECTED, /* client socket connected successful */ IOEVENT_NOTCONNECTED, /* client socket could not connect (errid valid) */ IOEVENT_CLOSED, /* client socket lost connection (errid valid) */ - IOEVENT_ACCEPT, /* server socket accepted new connection (accept_fd valid) */ + IOEVENT_ACCEPT, /* server socket accepted new connection (accept_iofd valid) */ IOEVENT_SSLACCEPT, /* SSL server socket accepted new connection (accept_iofd valid) */ IOEVENT_TIMEOUT, /* timer timed out */ - IOEVENT_SSLFAILED /* failed to initialize SSL session */ + IOEVENT_SSLFAILED, /* failed to initialize SSL session */ + IOEVENT_DNS_FAILED, + IOEVENT_SOCKET_ERROR, /* socket() call failed (errid valid) */ + IOEVENT_BIND_ERROR, /* bind() call failed (errid valid) */ + }; struct IOBuffer { @@ -77,11 +84,16 @@ struct IOBuffer { size_t bufpos, buflen; }; +struct IOLowlevelDescriptor; + struct IODescriptor { - int fd; + union { + struct IOLowlevelDescriptor *iold; + struct IODescriptorStartup *iostartup; + } fd; + unsigned int flags; /* internal flags! see IOEngine.h */ enum IOType type; enum IOStatus state; - struct timeval timeout; int constant_timeout; iohandler_callback *callback; struct IOBuffer readbuf; @@ -93,9 +105,8 @@ struct IODescriptor { int ssl_active : 1; int ssl_hs_read : 1; int ssl_hs_write : 1; - struct IOSSLNode *sslnode; - - struct IODescriptor *next, *prev; + int iofd_free_lock : 1; + int iofd_want_free : 1; }; struct IOEvent { @@ -103,7 +114,6 @@ struct IOEvent { struct IODescriptor *iofd; union { char *recv_str; - int accept_fd; int errid; struct IODescriptor *accept_iofd; } data; diff --git a/src/test/socket/Makefile b/src/test/socket/Makefile index 724e34b..01e74c9 100644 --- a/src/test/socket/Makefile +++ b/src/test/socket/Makefile @@ -3,7 +3,7 @@ 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 +OBJ = ../../IODNSEngine_cares.c ../../IODNSEngine_default.c ../../IODNSHandler.c ../../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) -- 2.20.1