added SSL backend for IOMultiplexer
[TransparentIRC.git] / src / IOHandler.c
1 /* IOHandler.c - TransparentIRC 0.1
2  * Copyright (C) 2011-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 "IOEngine.h"
19 #include "IOHandler_SSL.h"
20
21 #define MAXLOG 1024
22
23 struct IODescriptor *first_descriptor = NULL;
24 struct IODescriptor *timer_priority = NULL;
25
26 void iohandler_log(enum IOLogType type, char *text, ...) {
27     va_list arg_list;
28     char logBuf[MAXLOG+1];
29     int pos;
30     logBuf[0] = '\0';
31     va_start(arg_list, text);
32     pos = vsnprintf(logBuf, MAXLOG - 1, text, arg_list);
33     va_end(arg_list);
34     if (pos < 0 || pos > (MAXLOG - 1)) pos = MAXLOG - 1;
35     logBuf[pos] = '\n';
36     logBuf[pos+1] = '\0';
37     
38     //do something with logBuf
39     //...
40     printf("%s", logBuf);
41 }
42
43 /* IO Engines */
44 extern struct IOEngine engine_select; /* select system call (should always be useable) */
45 extern struct IOEngine engine_kevent;
46 extern struct IOEngine engine_epoll;
47
48 struct IOEngine *engine = NULL;
49
50 static void iohandler_init_engine() {
51     if(engine) return;
52     //try other engines
53     if(!engine && engine_kevent.init && engine_kevent.init())
54         engine = &engine_kevent;
55     if(!engine && engine_epoll.init && engine_epoll.init())
56         engine = &engine_epoll;
57     
58     if (!engine) {
59         if(engine_select.init())
60             engine = &engine_select;
61         else {
62             iohandler_log(IOLOG_FATAL, "found no useable IO engine");
63             return;
64         }
65     }
66     iohandler_log(IOLOG_DEBUG, "using %s IO engine", engine->name);
67     iohandler_ssl_init();
68 }
69
70 static void iohandler_append(struct IODescriptor *descriptor) {
71     struct timeval *timeout = ((descriptor->timeout.tv_sec || descriptor->timeout.tv_usec) ? &descriptor->timeout : NULL);
72     if(timeout) {
73         struct IODescriptor *iofd;
74         int set_priority = 1;
75         descriptor->timeout = *timeout;
76         if(timer_priority)
77             iofd = timer_priority;
78         else
79             iofd = first_descriptor;
80         if(iofd) {
81             for(;;iofd = iofd->next) {
82                 if(timeval_is_smaler(timeout, (&iofd->timeout))) {
83                     descriptor->prev = iofd->prev;
84                     descriptor->next = iofd;
85                     iofd->prev = descriptor;
86                     if(iofd->prev)
87                         iofd->prev->next = descriptor;
88                     if(set_priority)
89                         timer_priority = descriptor;
90                     break;
91                 }
92                 if(iofd == timer_priority)
93                     set_priority = 0;
94                 if(iofd->next == NULL) {
95                     descriptor->next = NULL;
96                     descriptor->prev = iofd;
97                     iofd->next = descriptor;
98                     if(set_priority)
99                         timer_priority = descriptor;
100                     break;
101                 }
102             }
103         } else {
104             descriptor->prev = NULL;
105             descriptor->next = NULL;
106             first_descriptor = descriptor;
107             timer_priority = descriptor;
108         }
109         
110     } else {
111         descriptor->prev = NULL;
112         descriptor->next = first_descriptor;
113         if(first_descriptor)
114             first_descriptor->prev = descriptor;
115         first_descriptor = descriptor;
116     }
117 }
118
119 static void iohandler_remove(struct IODescriptor *descriptor, int engine_remove) {
120     //remove IODescriptor from the list
121     if(descriptor->prev)
122         descriptor->prev->next = descriptor->next;
123     else
124         first_descriptor = descriptor->next;
125     if(descriptor->next)
126         descriptor->next->prev = descriptor->prev;
127     if(descriptor == timer_priority)
128         timer_priority = descriptor->next;
129     
130     if(engine_remove)
131         engine->remove(descriptor);
132     if(descriptor->readbuf.buffer)
133         free(descriptor->readbuf.buffer);
134     if(descriptor->writebuf.buffer)
135         free(descriptor->writebuf.buffer);
136     iohandler_log(IOLOG_DEBUG, "removed IODescriptor (%d) of type `%s`", descriptor->fd, iohandler_iotype_name(descriptor->type));
137     free(descriptor);
138 }
139
140 struct IODescriptor *iohandler_add(int sockfd, enum IOType type, struct timeval *timeout, iohandler_callback *callback) {
141     //just add a new IODescriptor
142     struct IODescriptor *descriptor = calloc(1, sizeof(*descriptor));
143     if(!descriptor) {
144         iohandler_log(IOLOG_ERROR, "could not allocate memory for IODescriptor in %s:%d", __FILE__, __LINE__);
145         return NULL;
146     }
147     descriptor->fd = sockfd;
148     descriptor->type = type;
149     descriptor->state = IO_CLOSED;
150     descriptor->callback = callback;
151     if(timeout)
152         descriptor->timeout = *timeout;
153     if(type != IOTYPE_TIMER) {
154         descriptor->readbuf.buffer = malloc(IO_READ_BUFLEN + 2);
155         descriptor->readbuf.bufpos = 0;
156         descriptor->readbuf.buflen = IO_READ_BUFLEN;
157         descriptor->writebuf.buffer = malloc(IO_READ_BUFLEN + 2);
158         descriptor->writebuf.bufpos = 0;
159         descriptor->writebuf.buflen = IO_READ_BUFLEN;
160     }
161     
162     if(!engine) {
163         iohandler_init_engine();
164         if(!engine) {
165             return NULL;
166         }
167     }
168     engine->add(descriptor);
169     
170     //add IODescriptor to the list
171     iohandler_append(descriptor);
172     
173     iohandler_log(IOLOG_DEBUG, "added custom socket descriptor (%d) as type `%s`", sockfd, iohandler_iotype_name(type));
174     return descriptor;
175 }
176
177 void iohandler_set_timeout(struct IODescriptor *descriptor, struct timeval *timeout) {
178     if(descriptor->prev)
179         descriptor->prev->next = descriptor->next;
180     else
181         first_descriptor = descriptor->next;
182     if(descriptor->next)
183         descriptor->next->prev = descriptor->prev;
184     if(descriptor == timer_priority)
185         timer_priority = descriptor->next;
186     if(timeout) 
187         descriptor->timeout = *timeout;
188     else {
189         descriptor->timeout.tv_sec = 0;
190         descriptor->timeout.tv_usec = 0;
191     }
192     iohandler_append(descriptor);
193 }
194
195 static void iohandler_increase_iobuf(struct IOBuffer *iobuf, size_t required) {
196     if(iobuf->buflen >= required) return;
197     char *new_buf = realloc(iobuf->buffer, required + 2);
198     if(new_buf) {
199         iobuf->buffer = new_buf;
200         iobuf->buflen = required;
201     }
202 }
203
204 struct IODescriptor *iohandler_timer(struct timeval timeout, iohandler_callback *callback) {
205     struct IODescriptor *descriptor;
206     descriptor = iohandler_add(-1, IOTYPE_TIMER, &timeout, callback);
207     if(!descriptor) {
208         iohandler_log(IOLOG_ERROR, "could not allocate memory for IODescriptor in %s:%d", __FILE__, __LINE__);
209         return NULL;
210     }
211     iohandler_log(IOLOG_DEBUG, "added timer descriptor (sec: %d; usec: %d)", timeout.tv_sec, timeout.tv_usec);
212     return descriptor;
213 }
214
215 struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, int ssl, const char *bindhost, iohandler_callback *callback) {
216     //non-blocking connect
217     int sockfd;
218     struct addrinfo hints, *res, *freeres;
219     struct sockaddr_in *ip4 = NULL;
220     struct sockaddr_in6 *ip6 = NULL;
221     size_t dstaddrlen;
222     struct sockaddr *dstaddr = NULL;
223     struct IODescriptor *descriptor;
224     
225     memset (&hints, 0, sizeof (hints));
226     hints.ai_family = PF_UNSPEC;
227     hints.ai_socktype = SOCK_STREAM;
228     hints.ai_flags |= AI_CANONNAME;
229     if (getaddrinfo (hostname, NULL, &hints, &res)) {
230         iohandler_log(IOLOG_ERROR, "could not resolve %d to an IP address", hostname);
231         return NULL;
232     }
233     while (res) {
234         switch (res->ai_family) {
235         case AF_INET:
236             ip4 = (struct sockaddr_in *) res->ai_addr;
237             break;
238         case AF_INET6:
239             ip6 = (struct sockaddr_in6 *) res->ai_addr;
240             break;
241         }
242         freeres = res;
243         res = res->ai_next;
244         freeaddrinfo(res);
245     }
246     
247     if(ip6) {
248         sockfd = socket(AF_INET6, SOCK_STREAM, 0);
249         if(sockfd == -1) {
250             iohandler_log(IOLOG_ERROR, "could not create socket in %s:%d", __FILE__, __LINE__);
251             return NULL;
252         }
253         
254         ip6->sin6_family = AF_INET6;
255         ip6->sin6_port = htons(port);
256         
257         struct sockaddr_in6 *ip6vhost = NULL;
258         if (bindhost && !getaddrinfo(bindhost, NULL, &hints, &res)) {
259             while (res) {
260                 switch (res->ai_family) {
261                 case AF_INET6:
262                     ip6vhost = (struct sockaddr_in6 *) res->ai_addr;
263                     break;
264                 }
265                 freeres = res;
266                 res = res->ai_next;
267                 freeaddrinfo(res);
268             }
269         }
270         if(ip6vhost) {
271             ip6vhost->sin6_family = AF_INET6;
272             ip6vhost->sin6_port = htons(0);
273             bind(sockfd, (struct sockaddr*)ip6vhost, sizeof(*ip6vhost));
274         }
275         dstaddr = (struct sockaddr*)ip6;
276         dstaddrlen = sizeof(*ip6);
277     } else if(ip4) {
278         sockfd = socket(AF_INET, SOCK_STREAM, 0);
279         if(sockfd == -1) {
280             iohandler_log(IOLOG_ERROR, "could not create socket in %s:%d", __FILE__, __LINE__);
281             return NULL;
282         }
283         
284         ip4->sin_family = AF_INET;
285         ip4->sin_port = htons(port);
286         
287         struct sockaddr_in *ip4vhost = NULL;
288         if (bindhost && !getaddrinfo(bindhost, NULL, &hints, &res)) {
289             while (res) {
290                 switch (res->ai_family) {
291                 case AF_INET:
292                     ip4vhost = (struct sockaddr_in *) res->ai_addr;
293                     break;
294                 }
295                 freeres = res;
296                 res = res->ai_next;
297                 freeaddrinfo(res);
298             }
299         }
300         if(ip4vhost) {
301             ip4vhost->sin_family = AF_INET;
302             ip4vhost->sin_port = htons(0);
303             bind(sockfd, (struct sockaddr*)ip4vhost, sizeof(*ip4vhost));
304         }
305         dstaddr = (struct sockaddr*)ip4;
306         dstaddrlen = sizeof(*ip4);
307     } else
308         return NULL;
309     //make sockfd unblocking
310 #if defined(F_GETFL)
311     {
312         int flags;
313         flags = fcntl(sockfd, F_GETFL);
314         fcntl(sockfd, F_SETFL, flags|O_NONBLOCK);
315         flags = fcntl(sockfd, F_GETFD);
316         fcntl(sockfd, F_SETFD, flags|FD_CLOEXEC);
317     }
318 #else
319     /* I hope you're using the Win32 backend or something else that
320      * automatically marks the file descriptor non-blocking...
321      */
322 #endif
323     descriptor = iohandler_add(sockfd, IOTYPE_CLIENT, NULL, callback);
324     if(!descriptor) {
325         close(sockfd);
326         return NULL;
327     }
328     connect(sockfd, dstaddr, dstaddrlen); //returns EINPROGRESS here (nonblocking)
329     descriptor->state = IO_CONNECTING;
330     descriptor->ssl = (ssl ? 1 : 0);
331     descriptor->read_lines = 1;
332     engine->update(descriptor);
333     iohandler_log(IOLOG_DEBUG, "added client socket (%d) connecting to %s:%d", sockfd, hostname, port);
334     return descriptor;
335 }
336
337 struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, iohandler_callback *callback) {
338     int sockfd;
339     struct addrinfo hints, *res, *freeres;
340     struct sockaddr_in *ip4 = NULL;
341     struct sockaddr_in6 *ip6 = NULL;
342     struct IODescriptor *descriptor;
343     unsigned int opt;
344     
345     memset (&hints, 0, sizeof (hints));
346     hints.ai_family = PF_UNSPEC;
347     hints.ai_socktype = SOCK_STREAM;
348     hints.ai_flags |= AI_CANONNAME;
349     if (getaddrinfo (hostname, NULL, &hints, &res)) {
350         return NULL;
351     }
352     while (res) {
353         switch (res->ai_family) {
354         case AF_INET:
355             ip4 = (struct sockaddr_in *) res->ai_addr;
356             break;
357         case AF_INET6:
358             ip6 = (struct sockaddr_in6 *) res->ai_addr;
359             break;
360         }
361         freeres = res;
362         res = res->ai_next;
363         freeaddrinfo(res);
364     }
365     
366     if(ip6) {
367         sockfd = socket(AF_INET6, SOCK_STREAM, 0);
368         if(sockfd == -1) return NULL;
369         
370         opt = 1;
371         setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
372         
373         ip6->sin6_family = AF_INET6;
374         ip6->sin6_port = htons(port);
375         
376         bind(sockfd, (struct sockaddr*)ip6, sizeof(*ip6));
377     } else if(ip4) {
378         sockfd = socket(AF_INET, SOCK_STREAM, 0);
379         if(sockfd == -1) return NULL;
380         
381         opt = 1;
382         setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
383         
384         ip4->sin_family = AF_INET;
385         ip4->sin_port = htons(port);
386         
387         bind(sockfd, (struct sockaddr*)ip4, sizeof(*ip4));
388     } else
389         return NULL;
390     //make sockfd unblocking
391 #if defined(F_GETFL)
392     {
393         int flags;
394         flags = fcntl(sockfd, F_GETFL);
395         fcntl(sockfd, F_SETFL, flags|O_NONBLOCK);
396         flags = fcntl(sockfd, F_GETFD);
397         fcntl(sockfd, F_SETFD, flags|FD_CLOEXEC);
398     }
399 #else
400     /* I hope you're using the Win32 backend or something else that
401      * automatically marks the file descriptor non-blocking...
402      */
403 #endif
404     descriptor = iohandler_add(sockfd, IOTYPE_SERVER, NULL, callback);
405     if(!descriptor) {
406         close(sockfd);
407         return NULL;
408     }
409     listen(sockfd, 1);
410     descriptor->state = IO_LISTENING;
411     engine->update(descriptor);
412     iohandler_log(IOLOG_DEBUG, "added server socket (%d) listening on %s:%d", sockfd, hostname, port);
413     return descriptor;
414 }
415
416 void iohandler_write(struct IODescriptor *iofd, const char *line) {
417     size_t linelen = strlen(line);
418     iohandler_send(iofd, line, linelen);
419 }
420
421 void iohandler_send(struct IODescriptor *iofd, const char *data, size_t datalen) {
422     if(iofd->type == IOTYPE_TIMER || iofd->state == IO_CLOSED) {
423         iohandler_log(IOLOG_ERROR, "could not write to socket (%s)", (iofd->type == IOTYPE_TIMER ? "IOTYPE_TIMER" : "IO_CLOSED"));
424         return;
425     }
426     iohandler_log(IOLOG_DEBUG, "add %d to writebuf (fd: %d): %s", datalen, iofd->fd, data);
427     if(iofd->writebuf.buflen < iofd->writebuf.bufpos + datalen) {
428         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));
429         iohandler_increase_iobuf(&iofd->writebuf, iofd->writebuf.bufpos + datalen);
430         if(iofd->writebuf.buflen < iofd->writebuf.bufpos + datalen) {
431             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));
432             return;
433         }
434     }
435     memcpy(iofd->writebuf.buffer + iofd->writebuf.bufpos, data, datalen);
436     iofd->writebuf.bufpos += datalen;
437     engine->update(iofd);
438 }
439
440 void iohandler_printf(struct IODescriptor *iofd, const char *text, ...) {
441     va_list arg_list;
442     char sendBuf[LINELEN];
443     int pos;
444     sendBuf[0] = '\0';
445     va_start(arg_list, text);
446     pos = vsnprintf(sendBuf, LINELEN - 2, text, arg_list);
447     va_end(arg_list);
448     if (pos < 0 || pos > (LINELEN - 2)) pos = LINELEN - 2;
449     sendBuf[pos] = '\n';
450     sendBuf[pos+1] = '\0';
451     iohandler_send(iofd, sendBuf, pos+1);
452 }
453
454 void iohandler_try_write(struct IODescriptor *iofd) {
455     if(!iofd->writebuf.bufpos) return;
456     iohandler_log(IOLOG_DEBUG, "write writebuf (%d bytes) to socket (fd: %d)", iofd->writebuf.bufpos, iofd->fd);
457     int res;
458     if(iofd->ssl_active)
459         res = iohandler_ssl_write(iofd, iofd->writebuf.buffer, iofd->writebuf.bufpos);
460     else
461         res = send(iofd->fd, iofd->writebuf.buffer, iofd->writebuf.bufpos, 0);
462     if(res < 0) {
463         if (errno != EAGAIN) {
464             iohandler_log(IOLOG_ERROR, "could not write to socket (fd: %d): %d - %s", iofd->fd, errno, strerror(errno));
465         }
466     } else {
467         iofd->writebuf.bufpos -= res;
468         if(iofd->state != IO_CLOSED)
469             engine->update(iofd);
470     }
471 }
472
473 void iohandler_close(struct IODescriptor *iofd) {
474     int engine_remove = 1;
475     iofd->state = IO_CLOSED;
476     if(iofd->writebuf.bufpos) {
477         //try to send everything before closing
478 #if defined(F_GETFL)
479         {
480             int flags;
481             flags = fcntl(iofd->fd, F_GETFL);
482             fcntl(iofd->fd, F_SETFL, flags & ~O_NONBLOCK);
483             flags = fcntl(iofd->fd, F_GETFD);
484             fcntl(iofd->fd, F_SETFD, flags|FD_CLOEXEC);
485         }
486 #else
487         engine_remove = 0;
488         engine->remove(iofd);
489 #endif
490         iohandler_try_write(iofd);
491     }
492     //close IODescriptor
493     if(iofd->ssl)
494         iohandler_ssl_disconnect(iofd);
495     if(iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT || iofd->type == IOTYPE_STDIN)
496         close(iofd->fd);
497     iohandler_remove(iofd, engine_remove);
498 }
499
500 void iohandler_update(struct IODescriptor *iofd) {
501     iohandler_log(IOLOG_DEBUG, "external call to iohandler_update (fd: %d)", iofd->fd);
502     engine->update(iofd);
503 }
504
505 static void iohandler_trigger_event(struct IOEvent *event) {
506     if(!event->iofd->callback) return;
507     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);
508     event->iofd->callback(event);
509 }
510
511 void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) {
512     struct IOEvent callback_event;
513     callback_event.type = IOEVENT_IGNORE;
514     callback_event.iofd = iofd;
515     switch(iofd->state) {
516         case IO_SSLWAIT:
517             if(!readable && !writeable) {
518                 callback_event.type = IOEVENT_SSLFAILED;
519                 iofd->state = IO_CLOSED;
520             } else {
521                 iohandler_log(IOLOG_DEBUG, "triggering iohandler_ssl_client_handshake for %s (fd: %d)", iohandler_iotype_name(iofd->type), iofd->fd);
522                 iohandler_ssl_client_handshake(iofd);
523             }
524             break;
525         case IO_CLOSED:
526             if(iofd->type == IOTYPE_TIMER)
527                 callback_event.type = IOEVENT_TIMEOUT;
528             break;
529         case IO_LISTENING:
530             if(readable) {
531                 callback_event.data.accept_fd = accept(iofd->fd, NULL, 0);
532                 if(callback_event.data.accept_fd < 0) {
533                     iohandler_log(IOLOG_ERROR, "could not accept client (server fd: %d): %d - %s", iofd->fd, errno, strerror(errno));
534                 } else
535                     callback_event.type = IOEVENT_ACCEPT;
536             }
537             break;
538         case IO_CONNECTING:
539             if(readable) { //could not connect
540                 callback_event.type = IOEVENT_NOTCONNECTED;
541                 socklen_t arglen;
542                 arglen = sizeof(callback_event.data.errid);
543                 if (getsockopt(iofd->fd, SOL_SOCKET, SO_ERROR, &callback_event.data.errid, &arglen) < 0)
544                     callback_event.data.errid = errno;
545                 iofd->state = IO_CLOSED;
546                                 engine->update(iofd);
547             } else if(writeable) {
548                 if(iofd->ssl && !iofd->ssl_active) {
549                     iohandler_log(IOLOG_DEBUG, "triggering iohandler_ssl_connect for %s (fd: %d)", iohandler_iotype_name(iofd->type), iofd->fd);
550                     iohandler_ssl_connect(iofd);
551                     return;
552                 }
553                 callback_event.type = IOEVENT_CONNECTED;
554                 iofd->state = IO_CONNECTED;
555                 engine->update(iofd);
556             }
557             break;
558         case IO_CONNECTED:
559             if(readable) {
560                 if(iofd->read_lines) {
561                     int bytes;
562                     if(iofd->ssl_active)
563                         bytes = iohandler_ssl_read(iofd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos);
564                     else
565                         bytes = recv(iofd->fd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos, 0);
566                     if(bytes <= 0) {
567                         if (errno != EAGAIN) {
568                             iofd->state = IO_CLOSED;
569                                                         engine->update(iofd);
570                             callback_event.type = IOEVENT_CLOSED;
571                             callback_event.data.errid = errno;
572                         }
573                     } else {
574                         int i, used_bytes = 0;
575                         iohandler_log(IOLOG_DEBUG, "received %d bytes (fd: %d). readbuf position: %d", bytes, iofd->fd, iofd->readbuf.bufpos);
576                         iofd->readbuf.bufpos += bytes;
577                         callback_event.type = IOEVENT_RECV;
578                         for(i = 0; i < iofd->readbuf.bufpos; i++) {
579                             if(iofd->readbuf.buffer[i] == '\r' && iofd->readbuf.buffer[i+1] == '\n')
580                                 iofd->readbuf.buffer[i] = 0;
581                             else if(iofd->readbuf.buffer[i] == '\n' || iofd->readbuf.buffer[i] == '\r') {
582                                 iofd->readbuf.buffer[i] = 0;
583                                 callback_event.data.recv_str = iofd->readbuf.buffer + used_bytes;
584                                 iohandler_log(IOLOG_DEBUG, "parsed line (%d bytes): %s", i - used_bytes, iofd->readbuf.buffer + used_bytes);
585                                 used_bytes = i+1;
586                                 iohandler_trigger_event(&callback_event);
587                             } else if(i + 1 - used_bytes >= LINELEN) { //512 max
588                                 iofd->readbuf.buffer[i] = 0;
589                                 callback_event.data.recv_str = iofd->readbuf.buffer + used_bytes;
590                                 iohandler_log(IOLOG_DEBUG, "parsed and stripped line (%d bytes): %s", i - used_bytes, iofd->readbuf.buffer + used_bytes);
591                                 for(; i < iofd->readbuf.bufpos; i++) { //skip the rest of the line
592                                     if(iofd->readbuf.buffer[i] == '\n' || (iofd->readbuf.buffer[i] == '\r' && iofd->readbuf.buffer[i+1] != '\n')) {
593                                         break;
594                                     }
595                                 }
596                                 used_bytes = i+1;
597                                 iohandler_trigger_event(&callback_event);
598                             }
599                         }
600                         if(used_bytes) {
601                             if(used_bytes == iofd->readbuf.bufpos) {
602                                 iofd->readbuf.bufpos = 0;
603                                 iohandler_log(IOLOG_DEBUG, "readbuf fully processed (set buffer position to 0)");
604                             } else {
605                                 iohandler_log(IOLOG_DEBUG, "readbuf rest: %d bytes (used %d bytes)", iofd->readbuf.bufpos - used_bytes, used_bytes);
606                                 memmove(iofd->readbuf.buffer, iofd->readbuf.buffer + used_bytes, iofd->readbuf.bufpos - used_bytes);
607                                 iofd->readbuf.bufpos -= used_bytes;
608                             }
609                         }
610                         callback_event.type = IOEVENT_IGNORE;
611                     }
612                 } else
613                     callback_event.type = IOEVENT_READABLE;
614             }
615             if(writeable) {
616                 iohandler_try_write(iofd);
617             }
618             break;
619     }
620     if(callback_event.type == IOEVENT_IGNORE && !readable && !writeable) 
621         callback_event.type = IOEVENT_TIMEOUT;
622     if(callback_event.type != IOEVENT_IGNORE)
623         iohandler_trigger_event(&callback_event);
624 }
625
626 void iohandler_poll() {
627     if(engine) {
628         struct timeval timeout;
629         timeout.tv_sec = IO_MAX_TIMEOUT;
630         timeout.tv_usec = 0;
631         engine->loop(&timeout);
632     }
633 }
634
635 //debugging functions
636 char *iohandler_iotype_name(enum IOType type) {
637     switch(type) {
638         case IOTYPE_UNKNOWN:
639             return "IOTYPE_UNKNOWN";
640         case IOTYPE_SERVER:
641             return "IOTYPE_SERVER";
642         case IOTYPE_CLIENT:
643             return "IOTYPE_CLIENT";
644         case IOTYPE_STDIN:
645             return "IOTYPE_STDIN";
646         case IOTYPE_TIMER:
647             return "IOTYPE_TIMER";
648         default:
649             return "(UNDEFINED)";
650     }
651 }
652
653 char *iohandler_iostatus_name(enum IOStatus status) {
654     switch(status) {
655         case IO_CLOSED:
656             return "IO_CLOSED";
657         case IO_LISTENING:
658             return "IO_LISTENING";
659         case IO_CONNECTING:
660             return "IO_CONNECTING";
661         case IO_CONNECTED:
662             return "IO_CONNECTED";
663         case IO_SSLWAIT:
664             return "IO_SSLWAIT";
665         default:
666             return "(UNDEFINED)";
667     }
668 }
669
670 char *iohandler_ioeventtype_name(enum IOEventType type) {
671     switch(type) {
672         case IOEVENT_IGNORE:
673             return "IOEVENT_IGNORE";
674         case IOEVENT_READABLE:
675             return "IOEVENT_READABLE";
676         case IOEVENT_RECV:
677             return "IOEVENT_RECV";
678         case IOEVENT_CONNECTED:
679             return "IOEVENT_CONNECTED";
680         case IOEVENT_NOTCONNECTED:
681             return "IOEVENT_NOTCONNECTED";
682         case IOEVENT_CLOSED:
683             return "IOEVENT_CLOSED";
684         case IOEVENT_ACCEPT:
685             return "IOEVENT_ACCEPT";
686         case IOEVENT_TIMEOUT:
687             return "IOEVENT_TIMEOUT";
688         default:
689             return "(UNDEFINED)";
690     }
691 }