[IOMultiplexer] Added asynchronous DNS Lookups
[IOMultiplexer.git] / src / IOHandler.c
1 /* IOHandler.c - IOMultiplexer
2  * Copyright (C) 2012  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17 #include "IOHandler.h"
18 #include "IODNSHandler.h"
19 #include "IOEngine.h"
20 #include "IODNSEngine.h"
21 #include "IOHandler_SSL.h"
22 #include <errno.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 #ifdef WIN32
26 #define _WIN32_WINNT 0x501
27 #include <windows.h>
28 #include <winsock2.h>
29 #include <ws2tcpip.h>
30 #else
31 #include <signal.h>
32 #include <stdarg.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <netdb.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <netinet/tcp.h>
39 #include <fcntl.h>
40 #endif
41
42 #ifndef EWOULDBLOCK
43 #define EWOULDBLOCK EAGAIN
44 #endif
45
46 #define MAXLOG 1024
47 iohandler_log_callback *iolog_backend = NULL;
48
49 struct IOLowlevelDescriptor *first_descriptor = NULL;
50 struct IOLowlevelDescriptor *timer_priority = NULL;
51 struct IODescriptorStartup *first_iostartup = NULL;
52
53 #ifdef HAVE_PTHREAD_H
54 static pthread_mutex_t io_thread_sync;
55 static pthread_mutex_t io_poll_sync;
56 #endif
57
58 #define IOSTARTUP_SRCLOOKUP  0x01
59 #define IOSTARTUP_DSTLOOKUP  0x02
60 #define IOSTARTUP_LOOKUPS    0x03
61
62 struct IODescriptorStartup {
63     struct IODescriptor *iofd;
64     struct IODNSQuery *iodns; /* dns queries for src and dst address */
65     struct timeval timeout;
66     
67     unsigned int flags : 6;
68     unsigned int port : 16;
69     unsigned int iptype : 8;
70     unsigned int listening : 1;
71     unsigned int ssl : 1;
72     
73     char *srchost;
74     struct IODNSResult *srcaddr;
75     struct IODNSResult *use_srcaddr;
76     
77     char *dsthost;
78     struct IODNSResult *dstaddr;
79     struct IODNSResult *use_dstaddr;
80     
81     char *ssl_certfile;
82     char *ssl_keyfile;
83     
84     struct IODescriptorStartup *next, *prev;
85 };
86
87 void iohandler_log(enum IOLogType type, char *text, ...) {
88     va_list arg_list;
89     char logBuf[MAXLOG+1];
90     int pos;
91     logBuf[0] = '\0';
92     va_start(arg_list, text);
93     pos = vsnprintf(logBuf, MAXLOG - 1, text, arg_list);
94     va_end(arg_list);
95     if (pos < 0 || pos > (MAXLOG - 1)) pos = MAXLOG - 1;
96     logBuf[pos] = '\n';
97     logBuf[pos+1] = '\0';
98     
99     if(iolog_backend)
100         iolog_backend(type, logBuf);
101 }
102
103 /* IO Engines */
104 extern struct IOEngine engine_select; /* select system call (should always be useable) */
105 extern struct IOEngine engine_kevent;
106 extern struct IOEngine engine_epoll;
107 extern struct IOEngine engine_win32;
108
109 int iohandler_settings = 0;
110 struct IOEngine *engine = NULL;
111
112 void iohandler_set(int setting, int value) {
113     if(value)
114         iohandler_settings |= setting;
115     else
116         iohandler_settings &= ~setting;
117 }
118
119 static void iohandler_init_engine() {
120     if(engine) return;
121     IOTHREAD_MUTEX_INIT(io_thread_sync);
122     IOTHREAD_MUTEX_INIT(io_poll_sync);
123     
124     //try other engines
125     if(!(iohandler_settings & IOHANDLER_SETTING_HIGH_PRECISION_TIMER)) {
126         if(!engine && engine_kevent.init && engine_kevent.init())
127             engine = &engine_kevent;
128         if(!engine && engine_epoll.init && engine_epoll.init())
129             engine = &engine_epoll;
130         if(!engine && engine_win32.init && engine_win32.init())
131             engine = &engine_win32;
132     }
133     
134     if (!engine) {
135         if(engine_select.init())
136             engine = &engine_select;
137         else {
138             iohandler_log(IOLOG_FATAL, "found no useable IO engine");
139             return;
140         }
141     }
142     iohandler_log(IOLOG_DEBUG, "using %s IO engine", engine->name);
143     iohandler_ssl_init();
144 }
145
146 static void iohandler_initialize_iobuf(struct IODescriptor *iofd) {
147     if(!iofd->readbuf.buffer) {
148         iofd->readbuf.buffer = malloc(IO_READ_BUFLEN + 2);
149         iofd->readbuf.bufpos = 0;
150         iofd->readbuf.buflen = IO_READ_BUFLEN;
151         iofd->writebuf.buffer = malloc(IO_READ_BUFLEN + 2);
152         iofd->writebuf.bufpos = 0;
153         iofd->writebuf.buflen = IO_READ_BUFLEN;
154     }
155 }
156
157 static void iohandler_trigger_event(struct IOEvent *event) {
158     if(!event->iofd->callback) return;
159     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);
160     event->iofd->callback(event);
161 }
162
163 static void iohandler_increase_iobuf(struct IOBuffer *iobuf, size_t required) {
164     if(iobuf->buflen >= required) return;
165     char *new_buf = realloc(iobuf->buffer, required + 2);
166     if(new_buf) {
167         iobuf->buffer = new_buf;
168         iobuf->buflen = required;
169     }
170 }
171
172 static void iohandler_append_iostartup(struct IODescriptorStartup *iostartup) {
173     iostartup->prev = NULL;
174     iostartup->next = first_iostartup;
175     first_iostartup = iostartup;
176 }
177
178 static void iohandler_remove_iostartup(struct IODescriptorStartup *iostartup) {
179     if(iostartup->prev)
180         iostartup->prev->next = iostartup->next;
181     else
182         first_iostartup = iostartup->next;
183     if(iostartup->next)
184         iostartup->next->prev = iostartup->prev;
185     if(iostartup->iodns)
186         iodns_abort(iostartup->iodns);
187     if(iostartup->srchost)
188         free(iostartup->srchost);
189     if(iostartup->dsthost)
190         free(iostartup->dsthost);
191     if(iostartup->srcaddr)
192         iodns_free_result(iostartup->srcaddr);
193     if(iostartup->dstaddr)
194         iodns_free_result(iostartup->dstaddr);
195     if(iostartup->iofd) {
196         if(iostartup->iofd->readbuf.buffer)
197             free(iostartup->iofd->readbuf.buffer);
198         if(iostartup->iofd->writebuf.buffer)
199             free(iostartup->iofd->writebuf.buffer);
200         free(iostartup->iofd);
201     }
202     free(iostartup);
203 }
204
205 static void iohandler_append(struct IOLowlevelDescriptor *iold) {
206     IOSYNCHRONIZE(io_thread_sync);
207     struct timeval *timeout = ((iold->timeout.tv_sec || iold->timeout.tv_usec) ? &iold->timeout : NULL);
208     if(timeout) {
209         struct IOLowlevelDescriptor *iold2;
210         int set_priority = 1;
211         if(timer_priority)
212             iold2 = timer_priority;
213         else
214             iold2 = first_descriptor;
215         if(iold2) {
216             for(;;iold2 = iold2->next) {
217                 if(timeval_is_smaler(timeout, (&iold2->timeout))) {
218                     iold->prev = iold2->prev;
219                     iold->next = iold2;
220                     if(iold2->prev)
221                         iold2->prev->next = iold;
222                     iold2->prev = iold;
223                     if(set_priority)
224                         timer_priority = iold;
225                     break;
226                 }
227                 if(iold2 == timer_priority)
228                     set_priority = 0;
229                 if(iold2->next == NULL) {
230                     iold->next = NULL;
231                     iold->prev = iold2;
232                     iold2->next = iold;
233                     if(set_priority)
234                         timer_priority = iold;
235                     break;
236                 }
237             }
238         } else {
239             iold->prev = NULL;
240             iold->next = NULL;
241             first_descriptor = iold;
242             timer_priority = iold;
243         }
244     } else {
245         iold->prev = NULL;
246         iold->next = first_descriptor;
247         if(first_descriptor)
248             first_descriptor->prev = iold;
249         first_descriptor = iold;
250     }
251     IODESYNCHRONIZE(io_thread_sync);
252 }
253
254 static void iohandler_remove(struct IOLowlevelDescriptor *iold) {
255     //remove IOLowlevelDescriptor from the list
256     IOSYNCHRONIZE(io_thread_sync);
257     if(iold->prev)
258         iold->prev->next = iold->next;
259     else
260         first_descriptor = iold->next;
261     if(iold->next)
262         iold->next->prev = iold->prev;
263     if(iold == timer_priority)
264         timer_priority = iold->next;
265     
266     struct IODescriptor *iofd = IOLOWLEVEL_GET_IOFD(iold);
267     if(iofd) {
268         if(iofd->readbuf.buffer)
269             free(iofd->readbuf.buffer);
270         if(iofd->writebuf.buffer)
271             free(iofd->writebuf.buffer);
272         iohandler_log(IOLOG_DEBUG, "removed IODescriptor (%d) of type `%s`", iold->fd, iohandler_iotype_name(iofd->type));
273         free(iofd);
274     }
275     
276     free(iold);
277     IODESYNCHRONIZE(io_thread_sync);
278 }
279
280
281 struct IODescriptor *iohandler_add_iofd(int sockfd, enum IOType type, struct timeval *timeout, iohandler_callback *callback) {
282     //just add a new IODescriptor
283     if(!engine) {
284         iohandler_init_engine();
285         if(!engine) {
286             return NULL;
287         }
288     }
289     
290     struct IODescriptor *iofd = calloc(1, sizeof(*iofd));
291     if(!iofd) {
292         iohandler_log(IOLOG_ERROR, "could not allocate memory for IODescriptor in %s:%d", __FILE__, __LINE__);
293         return NULL;
294     }
295     
296     struct IOLowlevelDescriptor *iold = calloc(1, sizeof(*iofd));
297     if(!iold) {
298         iohandler_log(IOLOG_ERROR, "could not allocate memory for IOLowlevelDescriptor in %s:%d", __FILE__, __LINE__);
299         free(iofd);
300         return NULL;
301     }
302     
303     iold->fd = (type == IOTYPE_STDIN ? fileno(stdin) : sockfd);
304     iold->flags = IOFLAGS_HAVE_IOFD | IOFLAGS_WANT_READ;
305     iold->data.iofd = iofd;
306     
307     iofd->fd.iold = iold;
308     iofd->flags = IOFDFLAGS_HAVE_IOLD;
309     iofd->type = type;
310     iofd->state = (type == IOTYPE_STDIN ? IO_CONNECTED : IO_CLOSED);
311     iofd->callback = callback;
312     
313     if(timeout) {
314         iold->timeout = *timeout;
315         if(iold->timeout.tv_usec > 1000000) {
316             iold->timeout.tv_usec -= 1000000;
317             iold->timeout.tv_sec++;
318         }
319     }
320     
321     if(type != IOTYPE_TIMER)
322         iohandler_initialize_iobuf(iofd);
323     
324     
325     engine->add(iold);
326     
327     //add IOLowlevelDescriptor to the list
328     iohandler_append(iold);
329     
330     iohandler_log(IOLOG_DEBUG, "added custom socket descriptor (%d) as type `%s`", sockfd, iohandler_iotype_name(type));
331     return iofd;
332 }
333
334 struct IOLowlevelDescriptor *iohandler_lowlevel_add(int fd, int want_read, int want_write, iolowlevel_callback *callback) {
335     
336 }
337
338 void iohandler_lowlevel_update(struct IOLowlevelDescriptor *iolow, int want_read, int want_write) {
339     
340 }
341
342 void iohandler_lowlevel_del(struct IOLowlevelDescriptor *iolow) {
343     
344 }
345
346 void iohandler_set_timeout(struct IODescriptor *iofd, struct timeval *timeout) {
347     struct IOLowlevelDescriptor *iold = IODESCRIPTOR_GET_IOLD(iofd);
348     if(iold) {
349         if(!timeout && !(iold->flags & IOFLAGS_HAVE_TIMEOUT))
350             return; //nothing to do
351         
352         if(iold->prev)
353             iold->prev->next = iold->next;
354         else
355             first_descriptor = iold->next;
356         if(iold->next)
357             iold->next->prev = iold->prev;
358         if(iold == timer_priority)
359             timer_priority = iold->next;
360         
361         if(timeout && timeout->tv_sec == 0 && iofd->constant_timeout) { // autoreload timer
362             iold->timeout.tv_usec += (iofd->constant_timeout % 1000) * 1000;
363             iold->timeout.tv_sec += (iofd->constant_timeout / 1000);
364             if(iold->timeout.tv_usec > 1000000) {
365                 iold->timeout.tv_sec += (iold->timeout.tv_usec / 1000000);
366                 iold->timeout.tv_usec %= 1000000;
367             }
368             iold->flags |= IOFLAGS_HAVE_TIMEOUT;
369         } else if(timeout) { // normal timer
370             iold->timeout = *timeout;
371             if(iold->timeout.tv_usec > 1000000) {
372                 iold->timeout.tv_usec -= 1000000;
373                 iold->timeout.tv_sec++;
374             }
375             iold->flags |= IOFLAGS_HAVE_TIMEOUT;
376         } else
377             iold->flags &= ~IOFLAGS_HAVE_TIMEOUT;
378         iohandler_append(iold);
379     } else {
380         struct IODescriptorStartup *iostartup = iofd->fd.iostartup;
381         
382         if(timeout) {
383             iostartup->timeout = *timeout;
384             if(iostartup->timeout.tv_usec > 1000000) {
385                 iostartup->timeout.tv_usec -= 1000000;
386                 iostartup->timeout.tv_sec++;
387             }
388         } else {
389             iostartup->timeout.tv_sec = 0;
390             iostartup->timeout.tv_usec = 0;
391         }
392     }
393 }
394
395 struct IODescriptor *iohandler_timer(struct timeval timeout, iohandler_callback *callback) {
396     struct IODescriptor *descriptor;
397     descriptor = iohandler_add_iofd(-1, IOTYPE_TIMER, &timeout, callback);
398     if(!descriptor) {
399         iohandler_log(IOLOG_ERROR, "could not create IODescriptor in %s:%d", __FILE__, __LINE__);
400         return NULL;
401     }
402     iohandler_log(IOLOG_DEBUG, "added timer descriptor (sec: %d; usec: %d)", timeout.tv_sec, timeout.tv_usec);
403     return descriptor;
404 }
405
406 struct IODescriptor *iohandler_constant_timer(int msec, iohandler_callback *callback) {
407     struct IODescriptor *descriptor;
408     struct timeval timeout;
409     gettimeofday(&timeout, NULL);
410     timeout.tv_usec += (msec % 1000) * 1000;
411     timeout.tv_sec += (msec / 1000);
412     if(timeout.tv_usec > 1000000) {
413         timeout.tv_sec += (timeout.tv_usec / 1000000);
414         timeout.tv_usec %= 1000000;
415     }
416     descriptor = iohandler_add_iofd(-1, IOTYPE_TIMER, &timeout, callback);
417     if(!descriptor) {
418         iohandler_log(IOLOG_ERROR, "could not allocate memory for IODescriptor in %s:%d", __FILE__, __LINE__);
419         return NULL;
420     }
421     descriptor->constant_timeout = msec;
422     iohandler_log(IOLOG_DEBUG, "added timer descriptor (sec: %d; usec: %d)", timeout.tv_sec, timeout.tv_usec);
423     return descriptor;
424 }
425
426 static void iohandler_process_iostartup(struct IODescriptorStartup *iostartup) {
427     struct IODescriptor *iofd = iostartup->iofd;
428     struct IOLowlevelDescriptor *iold;
429     struct IOEvent callback_event;
430     struct sockaddr_in ip4;
431     struct sockaddr_in6 ip6;
432     int addr_family, sockfd, opt;
433     
434     //open socket
435     if(iostartup->use_srcaddr)
436         iostartup->iptype = iostartup->use_srcaddr->type;
437     else if(iostartup->use_dstaddr)
438         iostartup->iptype = iostartup->use_dstaddr->type;
439     
440     addr_family = ((iostartup->iptype & IODNS_RECORD_AAAA) ? AF_INET6 : AF_INET);
441     
442     sockfd = socket(addr_family, SOCK_STREAM, 0);
443     if(!sockfd) {
444         callback_event.type = IOEVENT_SOCKET_ERROR;
445         goto iohandler_process_iostartup_fail;
446     }
447     
448     //prevent SIGPIPE
449     #ifndef WIN32
450     #if defined(SO_NOSIGPIPE)
451     opt = 1;
452     setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&opt, sizeof(opt));
453     #else
454     signal(SIGPIPE, SIG_IGN);
455     #endif
456     #endif
457     //make sockfd unblocking
458     #if defined(F_GETFL)
459     opt = fcntl(sockfd, F_GETFL);
460     fcntl(sockfd, F_SETFL, opt|O_NONBLOCK);
461     opt = fcntl(sockfd, F_GETFD);
462     fcntl(sockfd, F_SETFD, opt|FD_CLOEXEC);
463     #endif
464     
465     if(iostartup->listening) {
466         opt = 1;
467         setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt));
468     } else {
469         if((iostartup->iptype & IODNS_RECORD_AAAA)) {
470             struct sockaddr_in6 *ip6ptr = iostartup->use_srcaddr->address;
471             ip6ptr->sin6_family = addr_family;
472             ip6ptr->sin6_port = htons(iostartup->port);
473         } else {
474             struct sockaddr_in *ip4ptr = iostartup->use_srcaddr->address;
475             ip4ptr->sin_family = addr_family;
476             ip4ptr->sin_port = htons(iostartup->port);
477         }
478     }
479     
480     // Bind Socket to IP
481     if(iostartup->listening || iostartup->use_srcaddr) {
482         if((iostartup->iptype & IODNS_RECORD_AAAA)) {
483             memset(&ip6, 0, sizeof(ip6));
484             if(iostartup->use_srcaddr) {
485                 struct sockaddr_in6 *ip6ptr = iostartup->use_srcaddr->address;
486                 ip6.sin6_addr = ip6ptr->sin6_addr;
487             } else
488                 inet_pton(AF_INET6, "::1", &ip6.sin6_addr);
489             ip6.sin6_family = addr_family;
490             ip6.sin6_port = htons((iostartup->listening ? iostartup->port : 0));
491             opt = bind(sockfd, (struct sockaddr*)&ip6, sizeof(ip6));
492         } else {
493             memset(&ip6, 0, sizeof(ip6));
494             if(iostartup->use_srcaddr) {
495                 struct sockaddr_in4 *ip4ptr = iostartup->use_srcaddr->address;
496                 ip6.sin6_addr = ip4ptr->sin_addr;
497             } else
498                 ip4.sin_addr = INADDR_ANY;
499             ip4.sin_family = addr_family;
500             ip4.sin_port = htons((iostartup->listening ? iostartup->port : 0));
501             opt = bind(sockfd, (struct sockaddr*)&ip4, sizeof(ip4));
502         }
503         if(!opt) {
504             callback_event.type = IOEVENT_BIND_ERROR;
505             goto iohandler_process_iostartup_fail;
506         }
507     }
508     
509     iold = calloc(1, sizeof(*iold));
510     iold->fd = sockfd;
511     iold->flags = IOFLAGS_HAVE_IOFD | IOFLAGS_WANT_READ;
512     iold->data.iofd = iofd;
513     
514     if(iostartup->listening) {
515         listen(sockfd, 1);
516         iofd->state = IO_LISTENING;
517     } else {
518         connect(sockfd, iostartup->use_dstaddr->address, iostartup->use_dstaddr->addresslen);
519         iofd->state = IO_CONNECTING;
520     }
521     
522     iofd->fd.iold = iold;
523     iofd->flags = IOFDFLAGS_HAVE_IOLD;
524     
525     iostartup->iofd = NULL;
526     iohandler_remove_iostartup(iostartup);
527     return;
528     
529     iohandler_process_iostartup_fail:
530     if(sockfd)
531         close(sockfd);
532     callback_event.iofd = iostartup->iofd;
533     iostartup->iofd->flags |= IOFDFLAGS_FREE_LOCK;
534     iohandler_trigger_event(&callback_event);
535     iostartup->iofd->flags &= ~IOFDFLAGS_FREE_LOCK;
536     if(iostartup->iofd->flags & IOFDFLAGS_WANT_FREE)
537         iohandler_remove_iostartup(iostartup);
538     else
539         iohandler_close(iostartup->iofd);
540 }
541
542 static IODNS_CALLBACK(iohandler_dns_callback) {
543     struct IODNSQuery *query = event->query;
544     struct IODescriptorStartup *iostartup = query->data;
545     int trigger_dns_fail = 0;
546     iostartup->iodns = NULL;
547     
548     switch((iostartup->flags & IOSTARTUP_LOOKUPS)) {
549     case IOSTARTUP_SRCLOOKUP:
550         if(event->type == IODNSEVENT_FAILED) {
551             trigger_dns_fail = 1;
552             break;
553         }
554         if(iostartup->dsthost) {
555             iostartup->iodns = iodns_getaddrinfo(iostartup->dsthost, iostartup->iptype, iohandler_dns_callback);
556             iostartup->flags |= IOSTARTUP_DSTLOOKUP;
557             iostartup->iodns->data = iostartup;
558         }
559         break;
560     case IOSTARTUP_DSTLOOKUP:
561         if(event->type == IODNSEVENT_FAILED) {
562             trigger_dns_fail = 1;
563             break;
564         }
565         break;
566     }
567     
568     if(iostartup->iodns)
569         return;
570     
571     if(iostartup->iptype & IODNS_RECORD_AAAA) {
572         struct IODNSResult *result;
573         if(iostartup->srcaddr) {
574             for(result = iostartup->srcaddr; result; result = result->next) {
575                 if(result->type == IODNS_RECORD_AAAA) {
576                     iostartup->use_srcaddr = result;
577                     break;
578                 }
579             }
580         }
581         if(!iostartup->srcaddr || iostartup->use_srcaddr) {
582             for(result = iostartup->dstaddr; result; result = result->next) {
583                 if(result->type == IODNS_RECORD_AAAA) {
584                     iostartup->use_dstaddr = result;
585                     break;
586                 }
587             }
588         }
589         if((iostartup->srcaddr && !iostartup->use_srcaddr) || (iostartup->dstaddr && !iostartup->use_dstaddr)) {
590             iostartup->use_srcaddr = NULL;
591             iostartup->use_dstaddr = NULL;
592         }
593     }
594     if((iostartup->iptype & IODNS_RECORD_A) && !(iostartup->use_dstaddr || iostartup->use_srcaddr)) {
595         struct IODNSResult *result;
596         if(iostartup->srcaddr) {
597             for(result = iostartup->srcaddr; result; result = result->next) {
598                 if(result->type == IODNS_RECORD_A) {
599                     iostartup->use_srcaddr = result;
600                     break;
601                 }
602             }
603         }
604         if(!iostartup->srcaddr || iostartup->use_srcaddr) {
605             for(result = iostartup->srcaddr; result; result = result->next) {
606                 if(result->type == IODNS_RECORD_A) {
607                     iostartup->use_dstaddr = result;
608                     break;
609                 }
610             }
611         }
612         if((iostartup->srcaddr && !iostartup->use_srcaddr) || (iostartup->dstaddr && !iostartup->use_dstaddr)) {
613             iostartup->use_srcaddr = NULL;
614             iostartup->use_dstaddr = NULL;
615         }
616     }
617     if(!(iostartup->use_dstaddr || iostartup->use_srcaddr))
618         trigger_dns_fail = 1;
619     
620     if(trigger_dns_fail) {
621         struct IOEvent callback_event;
622         callback_event.type = IOEVENT_DNS_FAILED;
623         callback_event.iofd = iostartup->iofd;
624         iostartup->iofd->flags |= IOFDFLAGS_FREE_LOCK;
625         iohandler_trigger_event(&callback_event);
626         iostartup->iofd->flags &= ~IOFDFLAGS_FREE_LOCK;
627         if(iostartup->iofd->flags & IOFDFLAGS_WANT_FREE)
628             iohandler_remove_iostartup(iostartup);
629         else
630             iohandler_close(iostartup->iofd);
631         return;
632     }
633     
634     iohandler_process_iostartup(iostartup);
635 }
636
637 struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, int ssl, const char *bindhost, iohandler_callback *callback) {
638     return iohandler_connect_flags(hostname, port, ssl, bindhost, callback, IOHANDLER_CONNECT_IPV4 | IOHANDLER_CONNECT_IPV6);
639 }
640
641 struct IODescriptor *iohandler_connect_flags(const char *hostname, unsigned int port, int ssl, const char *bindhost, iohandler_callback *callback, int flags) {
642     if(!engine) {
643         iohandler_init_engine();
644         if(!engine) return NULL;
645     }
646     
647     //non-blocking connect
648     struct IODescriptorStartup *iostartup;
649     struct IODescriptor *iofd;
650     
651     iofd = calloc(1, sizeof(*iofd));
652     if(!iofd) {
653         
654         return NULL;
655     }
656     iostartup = calloc(1, sizeof(*iostartup));
657     if(!iostartup) {
658         
659         return NULL;
660     }
661     
662     iostartup->iofd = iofd;
663     iofd->fd.iostartup = iostartup;
664     
665     iofd->type = IOTYPE_CLIENT;
666     iofd->state = IO_PENDING;
667     iofd->callback = callback;
668     
669     iostartup->srchost = (bindhost ? strdup(bindhost) : NULL);
670     iostartup->dsthost = strdup(hostname);
671     iostartup->port = port;
672     iostartup->iptype = ((flags & IOHANDLER_CONNECT_IPV6) ? IODNS_RECORD_AAAA : 0) | ((flags & IOHANDLER_CONNECT_IPV4) ? IODNS_RECORD_A : 0);
673     iostartup->ssl = (ssl == 0 ? 0 : 1);
674     
675     //DNS Requests
676     if(bindhost) {
677         iostartup->iodns = iodns_getaddrinfo(bindhost, iostartup->iptype, iohandler_dns_callback);
678         iostartup->flags |= IOSTARTUP_SRCLOOKUP;
679     } else {
680         iostartup->iodns = iodns_getaddrinfo(hostname, iostartup->iptype, iohandler_dns_callback);
681         iostartup->flags |= IOSTARTUP_DSTLOOKUP;
682     }
683     iostartup->iodns->data = iostartup;
684     
685     iohandler_append_iostartup(iostartup);
686     
687     //Some Flags for later use
688     iofd->read_lines = 1;
689     
690     iohandler_log(IOLOG_DEBUG, "added client socket connecting to %s:%d (DNS LOOKUP)", hostname, port);
691     
692     return iofd;
693 }
694
695 struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, iohandler_callback *callback) {
696     return iohandler_listen_flags(hostname, port, callback, IOHANDLER_LISTEN_IPV4 | IOHANDLER_LISTEN_IPV6);
697 }
698
699 struct IODescriptor *iohandler_listen_flags(const char *hostname, unsigned int port, iohandler_callback *callback, int flags) {
700     if(!engine) {
701         iohandler_init_engine();
702         if(!engine) return NULL;
703     }
704     
705     //non-blocking listening socket
706     struct IODescriptorStartup *iostartup;
707     struct IODescriptor *iofd;
708     
709     iofd = calloc(1, sizeof(*iofd));
710     if(!iofd) {
711         
712         return NULL;
713     }
714     iostartup = calloc(1, sizeof(*iostartup));
715     if(!iostartup) {
716         
717         return NULL;
718     }
719     
720     iostartup->iofd = iofd;
721     iofd->fd.iostartup = iostartup;
722     
723     iofd->type = IOTYPE_SERVER;
724     iofd->state = IO_PENDING;
725     iofd->callback = callback;
726     
727     iostartup->srchost = (hostname ? strdup(hostname) : NULL);
728     iostartup->port = port;
729     iostartup->iptype = ((flags & IOHANDLER_LISTEN_IPV6) ? IODNS_RECORD_AAAA : 0) | ((flags & IOHANDLER_LISTEN_IPV4) ? IODNS_RECORD_A : 0);
730     iostartup->ssl = 0;
731     iostartup->listening = 1;
732     
733     //DNS Requests
734     if(hostname) {
735         iostartup->iodns = iodns_getaddrinfo(hostname, iostartup->iptype, iohandler_dns_callback);
736         iostartup->flags |= IOSTARTUP_SRCLOOKUP;
737         iostartup->iodns->data = iostartup;
738         iohandler_append_iostartup(iostartup);
739     } else
740         iohandler_process_iostartup(iostartup);
741     
742     iohandler_log(IOLOG_DEBUG, "added server socket listening on %s:%d", hostname, port);
743     return iofd;
744 }
745
746 struct IODescriptor *iohandler_listen_ssl(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iohandler_callback *callback) {
747     return iohandler_listen_ssl_flags(hostname, port, certfile, keyfile, callback, IOHANDLER_LISTEN_IPV4 | IOHANDLER_LISTEN_IPV6);
748 }
749
750 struct IODescriptor *iohandler_listen_ssl_flags(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iohandler_callback *callback, int flags) {
751     if(!engine) {
752         iohandler_init_engine();
753         if(!engine) return NULL;
754     }
755     
756     //non-blocking listening socket
757     struct IODescriptorStartup *iostartup;
758     struct IODescriptor *iofd;
759     
760     iofd = calloc(1, sizeof(*iofd));
761     if(!iofd) {
762         
763         return NULL;
764     }
765     iostartup = calloc(1, sizeof(*iostartup));
766     if(!iostartup) {
767         
768         return NULL;
769     }
770     
771     iostartup->iofd = iofd;
772     iofd->fd.iostartup = iostartup;
773     
774     iofd->type = IOTYPE_SERVER;
775     iofd->state = IO_PENDING;
776     iofd->callback = callback;
777     
778     iostartup->srchost = (hostname ? strdup(hostname) : NULL);
779     iostartup->port = port;
780     iostartup->iptype = ((flags & IOHANDLER_LISTEN_IPV6) ? IODNS_RECORD_AAAA : 0) | ((flags & IOHANDLER_LISTEN_IPV4) ? IODNS_RECORD_A : 0);
781     iostartup->ssl = 1;
782     iostartup->ssl_certfile = strdup(certfile);
783     iostartup->ssl_keyfile = strdup(keyfile);
784     iostartup->listening = 1;
785     
786     //DNS Requests
787     if(hostname) {
788         iostartup->iodns = iodns_getaddrinfo(hostname, iostartup->iptype, iohandler_dns_callback);
789         iohandler_append_iostartup(iostartup);
790     } else
791         iohandler_process_iostartup(iostartup);
792     
793     iohandler_log(IOLOG_DEBUG, "added SSL server socket listening on %s:%d", hostname, port);
794     return iofd;
795 }
796
797 void iohandler_write(struct IODescriptor *iofd, const char *line) {
798     size_t linelen = strlen(line);
799     iohandler_send(iofd, line, linelen);
800 }
801
802 void iohandler_send(struct IODescriptor *iofd, const char *data, size_t datalen) {
803     if(iofd->type == IOTYPE_TIMER || iofd->state == IO_CLOSED) {
804         iohandler_log(IOLOG_ERROR, "could not write to socket (%s)", (iofd->type == IOTYPE_TIMER ? "IOTYPE_TIMER" : "IO_CLOSED"));
805         return;
806     }
807     iohandler_log(IOLOG_DEBUG, "add %d to writebuf (fd: %d): %s", datalen, iofd->fd, data);
808     if(iofd->writebuf.buflen < iofd->writebuf.bufpos + datalen) {
809         iohandler_log(IOLOG_DEBUG, "increase writebuf (curr: %d) to %d (+%d bytes)", iofd->writebuf.buflen, iofd->writebuf.bufpos + datalen, (iofd->writebuf.bufpos + datalen - iofd->writebuf.buflen));
810         iohandler_increase_iobuf(&iofd->writebuf, iofd->writebuf.bufpos + datalen);
811         if(iofd->writebuf.buflen < iofd->writebuf.bufpos + datalen) {
812             iohandler_log(IOLOG_ERROR, "increase writebuf (curr: %d) to %d (+%d bytes) FAILED", iofd->writebuf.buflen, iofd->writebuf.bufpos + datalen, (iofd->writebuf.bufpos + datalen - iofd->writebuf.buflen));
813             return;
814         }
815     }
816     memcpy(iofd->writebuf.buffer + iofd->writebuf.bufpos, data, datalen);
817     iofd->writebuf.bufpos += datalen;
818     struct IOLowlevelDescriptor *iold = IODESCRIPTOR_GET_IOLD(iofd);
819     if(iold) {
820         iold->flags |= IOFLAGS_WANT_WRITE;
821         engine->update(iold);
822     }
823 }
824
825 void iohandler_printf(struct IODescriptor *iofd, const char *text, ...) {
826     va_list arg_list;
827     char sendBuf[IO_LINE_LEN];
828     int pos;
829     sendBuf[0] = '\0';
830     va_start(arg_list, text);
831     pos = vsnprintf(sendBuf, IO_LINE_LEN - 2, text, arg_list);
832     va_end(arg_list);
833     if (pos < 0 || pos > (IO_LINE_LEN - 2)) pos = IO_LINE_LEN - 2;
834     sendBuf[pos] = '\n';
835     sendBuf[pos+1] = '\0';
836     iohandler_send(iofd, sendBuf, pos+1);
837 }
838
839 static int iohandler_try_write(struct IODescriptor *iofd) {
840     struct IOLowlevelDescriptor *iold = IODESCRIPTOR_GET_IOLD(iofd);
841     if(!iofd->writebuf.bufpos || !iold) return 0;
842     iohandler_log(IOLOG_DEBUG, "write writebuf (%d bytes) to socket (fd: %d)", iofd->writebuf.bufpos, iofd->fd);
843     int res;
844     if(iofd->ssl_active)
845         res = iohandler_ssl_write(iofd, iofd->writebuf.buffer, iofd->writebuf.bufpos);
846     else
847         res = send(iofd->fd.iold->fd, iofd->writebuf.buffer, iofd->writebuf.bufpos, 0);
848     if(res < 0) {
849         if (errno != EAGAIN && errno != EWOULDBLOCK)
850             iohandler_log(IOLOG_ERROR, "could not write to socket (fd: %d): %d - %s", iofd->fd.iold->fd, errno, strerror(errno));
851         else
852             res = 0;
853     } else {
854         iofd->writebuf.bufpos -= res;
855         if(iofd->writebuf.bufpos)
856             iold->flags |= IOFLAGS_WANT_WRITE;
857         else
858             iold->flags &= ~IOFLAGS_WANT_WRITE;
859         engine->update(iold);
860     }
861     return res;
862 }
863
864 void iohandler_close(struct IODescriptor *iofd) {
865     struct IOLowlevelDescriptor *iold = IODESCRIPTOR_GET_IOLD(iofd);
866     int engine_remove = 1;
867     iofd->state = IO_CLOSED;
868     if(iold) {
869         if(iofd->writebuf.bufpos) {
870             //try to send everything before closing
871 #if defined(F_GETFL)
872             {
873                 int flags;
874                 flags = fcntl(iofd->fd, F_GETFL);
875                 fcntl(iofd->fd, F_SETFL, flags & ~O_NONBLOCK);
876                 flags = fcntl(iofd->fd, F_GETFD);
877                 fcntl(iofd->fd, F_SETFD, flags|FD_CLOEXEC);
878             }
879 #else
880             engine_remove = 0;
881             engine->remove(iofd);
882 #endif
883             iohandler_try_write(iofd);
884         }
885         if(iofd->ssl)
886             iohandler_ssl_disconnect(iofd);
887         if((iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT || iofd->type == IOTYPE_STDIN))
888             close(iold->fd);
889         if(engine_remove)
890             engine->remove(iold);
891         if(iofd->flags & IOFDFLAGS_FREE_LOCK)
892             iofd->flags |= IOFDFLAGS_WANT_FREE;
893         else
894             iohandler_remove(iold);
895     } else {
896         if(iofd->flags & IOFDFLAGS_FREE_LOCK)
897             iofd->flags |= IOFDFLAGS_WANT_FREE;
898         else
899             iohandler_remove_iostartup(iofd->fd.iostartup);
900     }
901 }
902
903 void iohandler_update(struct IODescriptor *iofd) {
904     iohandler_log(IOLOG_DEBUG, "external call to iohandler_update");
905     struct IOLowlevelDescriptor *iold = IODESCRIPTOR_GET_IOLD(iofd);
906     if(iold)
907         engine->update(iold);
908 }
909
910 void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) {
911     struct IOLowlevelDescriptor *iold = IODESCRIPTOR_GET_IOLD(iofd);
912     struct IOEvent callback_event;
913     callback_event.type = IOEVENT_IGNORE;
914     callback_event.iofd = iofd;
915     int remove_iofd = 0;
916     if(!iold)
917         return;
918     switch(iofd->state) {
919         case IO_SSLWAIT:
920             if(!readable && !writeable) {
921                 if(!(iofd->flags & IOFDFLAGS_SSL_SERVER_HS)) {
922                     callback_event.type = IOEVENT_SSLFAILED;
923                     iofd->state = IO_CLOSED;
924                     engine->update(iold);
925                 }
926                 remove_iofd = 1;
927             } else if((iofd->flags & IOFDFLAGS_SSL_SERVER_HS)) {
928                 iohandler_log(IOLOG_DEBUG, "triggering iohandler_ssl_server_handshake for %s (fd: %d)", iohandler_iotype_name(iofd->type), iofd->fd);
929                 iohandler_ssl_server_handshake(iofd);
930             } else {
931                 iohandler_log(IOLOG_DEBUG, "triggering iohandler_ssl_client_handshake for %s (fd: %d)", iohandler_iotype_name(iofd->type), iofd->fd);
932                 iohandler_ssl_client_handshake(iofd);
933             }
934             break;
935         case IO_CLOSED:
936             if(iofd->type == IOTYPE_TIMER) // Timers will be removed by IOEngine
937                 callback_event.type = IOEVENT_TIMEOUT;
938             else
939                 remove_iofd = 1;
940             break;
941         case IO_LISTENING:
942             if(readable) {
943                 int accept_fd = accept(iold->fd, NULL, 0);
944                 if(accept_fd < 0) {
945                     iohandler_log(IOLOG_ERROR, "could not accept client (server fd: %d): %d - %s", iofd->fd, errno, strerror(errno));
946                 } else {
947                     struct IODescriptor *client_iofd = iohandler_add(accept_fd, IOTYPE_CLIENT, NULL, NULL);
948                     if(iofd->ssl)
949                         iohandler_ssl_client_accepted(iofd, client_iofd);
950                     else {
951                         callback_event.type = IOEVENT_ACCEPT;
952                         callback_event.data.accept_iofd = client_iofd;
953                     }
954                 }
955             }
956             break;
957         case IO_CONNECTING:
958             if(readable) { //could not connect
959                 callback_event.type = IOEVENT_NOTCONNECTED;
960                 //socklen_t arglen;
961                 //arglen = sizeof(callback_event.data.errid);
962                 //if (getsockopt(iofd->fd, SOL_SOCKET, SO_ERROR, &callback_event.data.errid, &arglen) < 0)
963                 //    callback_event.data.errid = errno;
964                 iofd->state = IO_CLOSED;
965                 engine->update(iold);
966                 remove_iofd = 1;
967             } else if(writeable) {
968                 if(iofd->ssl && !(iofd->flags & IOFDFLAGS_SSL_ACTIVE)) {
969                     iohandler_log(IOLOG_DEBUG, "triggering iohandler_ssl_connect for %s (fd: %d)", iohandler_iotype_name(iofd->type), iofd->fd);
970                     iohandler_ssl_connect(iofd);
971                     return;
972                 }
973                 if(iofd->ssl && (iofd->flags & IOFDFLAGS_SSL_SERVER_HS)) {
974                     callback_event.type = IOEVENT_SSLACCEPT;
975                     callback_event.iofd = iofd->data;
976                     callback_event.data.accept_iofd = iofd;
977                     iofd->data = NULL;
978                 }
979                 else 
980                     callback_event.type = IOEVENT_CONNECTED;
981                 iofd->state = IO_CONNECTED;
982                 engine->update(iold);
983             }
984             break;
985         case IO_CONNECTED:
986             if(readable) {
987                 if(iofd->read_lines) {
988                     int bytes;
989                     
990                     if((iofd->flags & IOFDFLAGS_SSL_ACTIVE))
991                         bytes = iohandler_ssl_read(iofd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos);
992                     else {
993                         if(iofd->type == IOTYPE_STDIN)
994                             #ifdef WIN32
995                             bytes = readable;
996                             #else
997                             bytes = read(iold->fd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos);
998                             #endif
999                         else
1000                             bytes = recv(iold->fd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos, 0);
1001                     }
1002                     if(bytes <= 0) {
1003                         if (errno != EAGAIN || errno != EWOULDBLOCK) {
1004                             iofd->state = IO_CLOSED;
1005                             engine->update(iold);
1006                             callback_event.type = IOEVENT_CLOSED;
1007                             callback_event.data.errid = errno;
1008                             remove_iofd = 1;
1009                         }
1010                     } else {
1011                         int i, used_bytes = 0;
1012                         iohandler_log(IOLOG_DEBUG, "received %d bytes (fd: %d). readbuf position: %d", bytes, iold->fd, iofd->readbuf.bufpos);
1013                         iofd->readbuf.bufpos += bytes;
1014                         callback_event.type = IOEVENT_RECV;
1015                         for(i = 0; i < iofd->readbuf.bufpos; i++) {
1016                             if(iofd->readbuf.buffer[i] == '\r' && iofd->readbuf.buffer[i+1] == '\n')
1017                                 iofd->readbuf.buffer[i] = 0;
1018                             else if(iofd->readbuf.buffer[i] == '\n' || iofd->readbuf.buffer[i] == '\r') {
1019                                 iofd->readbuf.buffer[i] = 0;
1020                                 callback_event.data.recv_str = iofd->readbuf.buffer + used_bytes;
1021                                 iohandler_log(IOLOG_DEBUG, "parsed line (%d bytes): %s", i - used_bytes, iofd->readbuf.buffer + used_bytes);
1022                                 used_bytes = i+1;
1023                                 iofd->flags |= IOFDFLAGS_FREE_LOCK;
1024                                 iohandler_trigger_event(&callback_event);
1025                                 iofd->flags &= ~IOFDFLAGS_FREE_LOCK;
1026                                 if(iofd->flags & IOFDFLAGS_WANT_FREE)
1027                                     break;
1028                             } else if(i + 1 - used_bytes >= IO_LINE_LEN) { //512 max
1029                                 iofd->readbuf.buffer[i] = 0;
1030                                 callback_event.data.recv_str = iofd->readbuf.buffer + used_bytes;
1031                                 iohandler_log(IOLOG_DEBUG, "parsed and stripped line (%d bytes): %s", i - used_bytes, iofd->readbuf.buffer + used_bytes);
1032                                 for(; i < iofd->readbuf.bufpos; i++) { //skip the rest of the line
1033                                     if(iofd->readbuf.buffer[i] == '\n' || (iofd->readbuf.buffer[i] == '\r' && iofd->readbuf.buffer[i+1] != '\n')) {
1034                                         break;
1035                                     }
1036                                 }
1037                                 used_bytes = i+1;
1038                                 iofd->flags |= IOFDFLAGS_FREE_LOCK;
1039                                 iohandler_trigger_event(&callback_event);
1040                                 iofd->flags &= ~IOFDFLAGS_FREE_LOCK;
1041                                 if(iofd->flags & IOFDFLAGS_WANT_FREE)
1042                                     break;
1043                             }
1044                         }
1045                         if(used_bytes) {
1046                             if(used_bytes == iofd->readbuf.bufpos) {
1047                                 iofd->readbuf.bufpos = 0;
1048                                 iohandler_log(IOLOG_DEBUG, "readbuf fully processed (set buffer position to 0)");
1049                             } else {
1050                                 iohandler_log(IOLOG_DEBUG, "readbuf rest: %d bytes (used %d bytes)", iofd->readbuf.bufpos - used_bytes, used_bytes);
1051                                 memmove(iofd->readbuf.buffer, iofd->readbuf.buffer + used_bytes, iofd->readbuf.bufpos - used_bytes);
1052                                 iofd->readbuf.bufpos -= used_bytes;
1053                             }
1054                         }
1055                         callback_event.type = IOEVENT_IGNORE;
1056                     }
1057                 } else
1058                     callback_event.type = IOEVENT_READABLE;
1059             }
1060             if(writeable) {
1061                 int bytes;
1062                 bytes = iohandler_try_write(iofd);
1063                 if(bytes < 0) {
1064                     iofd->state = IO_CLOSED;
1065                     engine->update(iold);
1066                     callback_event.type = IOEVENT_CLOSED;
1067                     callback_event.data.errid = errno;
1068                     remove_iofd = 1;
1069                 }
1070             }
1071             break;
1072         case IO_PENDING:
1073             /* nothing to do for pending descriptors */
1074             break;
1075     }
1076     iofd->flags |= IOFDFLAGS_FREE_LOCK;
1077     if(callback_event.type == IOEVENT_IGNORE && !readable && !writeable) 
1078         callback_event.type = IOEVENT_TIMEOUT;
1079     if(callback_event.type != IOEVENT_IGNORE)
1080         iohandler_trigger_event(&callback_event);
1081     iofd->flags &= ~IOFDFLAGS_FREE_LOCK;
1082     if(iofd->flags & IOFDFLAGS_WANT_FREE)
1083         iohandler_remove(iold);
1084     else if(remove_iofd)
1085         iohandler_close(iofd);
1086 }
1087
1088 void iohandler_poll() {
1089     struct timeval timeout;
1090     timeout.tv_sec = IO_MAX_TIMEOUT;
1091     timeout.tv_usec = 0;
1092     iohandler_poll_timeout(timeout);
1093 }
1094
1095 void iohandler_poll_timeout(struct timeval timeout) {
1096     iodns_poll();
1097     if(engine) {
1098         IOSYNCHRONIZE(io_poll_sync); //quite senceless multithread support... better support will follow
1099         engine->loop(&timeout);
1100         IODESYNCHRONIZE(io_poll_sync);
1101     }
1102 }
1103
1104 //debugging functions
1105 char *iohandler_iotype_name(enum IOType type) {
1106     switch(type) {
1107         case IOTYPE_UNKNOWN:
1108             return "IOTYPE_UNKNOWN";
1109         case IOTYPE_SERVER:
1110             return "IOTYPE_SERVER";
1111         case IOTYPE_CLIENT:
1112             return "IOTYPE_CLIENT";
1113         case IOTYPE_STDIN:
1114             return "IOTYPE_STDIN";
1115         case IOTYPE_TIMER:
1116             return "IOTYPE_TIMER";
1117         default:
1118             return "(UNDEFINED)";
1119     }
1120 }
1121
1122 char *iohandler_iostatus_name(enum IOStatus status) {
1123     switch(status) {
1124         case IO_CLOSED:
1125             return "IO_CLOSED";
1126         case IO_LISTENING:
1127             return "IO_LISTENING";
1128         case IO_CONNECTING:
1129             return "IO_CONNECTING";
1130         case IO_CONNECTED:
1131             return "IO_CONNECTED";
1132         case IO_SSLWAIT:
1133             return "IO_SSLWAIT";
1134         default:
1135             return "(UNDEFINED)";
1136     }
1137 }
1138
1139 char *iohandler_ioeventtype_name(enum IOEventType type) {
1140     switch(type) {
1141         case IOEVENT_IGNORE:
1142             return "IOEVENT_IGNORE";
1143         case IOEVENT_READABLE:
1144             return "IOEVENT_READABLE";
1145         case IOEVENT_RECV:
1146             return "IOEVENT_RECV";
1147         case IOEVENT_CONNECTED:
1148             return "IOEVENT_CONNECTED";
1149         case IOEVENT_NOTCONNECTED:
1150             return "IOEVENT_NOTCONNECTED";
1151         case IOEVENT_CLOSED:
1152             return "IOEVENT_CLOSED";
1153         case IOEVENT_ACCEPT:
1154             return "IOEVENT_ACCEPT";
1155         case IOEVENT_SSLACCEPT:
1156             return "IOEVENT_SSLACCEPT";
1157         case IOEVENT_TIMEOUT:
1158             return "IOEVENT_TIMEOUT";
1159         default:
1160             return "(UNDEFINED)";
1161     }
1162 }