added IOHandler
[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
18 struct IODescriptor *first_descriptor = NULL;
19
20 /* IO Engines */
21 extern struct IOEngine engine_select; /* select system call (should always be useable) */
22
23 struct IOEngine *engine = NULL;
24
25 static void iohandler_init_engine() {
26     //try other engines
27     
28     if(engine)
29         //found an useable IO engine
30     else if (engine_select.init())
31         engine = &engine_select;
32     else {
33         //STDERR: found no useable IO engine
34     }
35 }
36
37 struct IODescriptor *iohandler_add(int sockfd, IOType type, iohandler_callback *callback) {
38     //just add a new IODescriptor
39     struct IODescriptor *descriptor = calloc(1, sizeof(*descriptor));
40     if(!descriptor) return NULL;
41     descriptor->fd = sockfd;
42     descriptor->type = type;
43     descriptor->state = IO_CLOSED;
44     descriptor->callback = callback;
45     if(type != IOTYPE_TIMER) {
46         descriptor->readbuf.buffer = malloc(IOREADBUFLEN + 2);
47         descriptor->readbuf.bufpos = 0;
48         descriptor->readbuf.buflen = IOREADBUFLEN;
49         descriptor->writebuf.buffer = malloc(IOREADBUFLEN + 2);
50         descriptor->writebuf.bufpos = 0;
51         descriptor->writebuf.buflen = IOREADBUFLEN;
52     }
53     
54     if(!engine) {
55         iohandler_init_engine();
56         if(!engine) {
57             return NULL;
58         }
59     }
60     engine->add(descriptor);
61     
62     //add IODescriptor to the list
63     descriptor->prev = NULL;
64     descriptor->next = first_descriptor;
65     first_descriptor->prev = descriptor;
66     first_descriptor = descriptor;
67     
68     return descriptor;
69 }
70
71 static void iohandler_remove(struct IODescriptor *descriptor) {
72     //remove IODescriptor from the list
73     if(descriptor->prev)
74         descriptor->prev->next = descriptor->next;
75     else
76         first_descriptor = descriptor->next;
77     if(descriptor->next)
78         descriptor->next->prev = descriptor->prev;
79     engine->remove(descriptor);
80     if(descriptor->readbuf.buffer)
81         free(descriptor->readbuf.buffer);
82     if(descriptor->writebuf.buffer)
83         free(descriptor->writebuf.buffer);
84     free(descriptor);
85 }
86
87 static void iohandler_increase_iobuf(struct IOBuffer *iobuf, size_t required) {
88     if(iobuf->buflen >= required) return;
89     char *new_buf = realloc(iobuf->buffer, required + 2);
90     if(new_buf) {
91         iobuf->buffer = new_buf;
92         iobuf->buflen = required;
93     }
94 }
95
96 struct IODescriptor *iohandler_timer(timeval timeout, iohandler_callback *callback) {
97     struct IODescriptor *descriptor;
98     descriptor = iohandler_add(-1, IOTYPE_TIMER, callback);
99     if(!descriptor) return NULL;
100     descriptor->timeout = timeout;
101     engine->update(descriptor);
102     return descriptor;
103 }
104
105 struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, const char *bind, iohandler_callback *callback) {
106     //non-blocking connect
107     int sockfd;
108     struct addrinfo hints, *res, *freeres;
109     struct sockaddr_in *ip4 = NULL;
110     struct sockaddr_in6 *ip6 = NULL;
111     size_t dstaddrlen;
112     struct sockaddr *dstaddr = NULL;
113     struct IODescriptor *descriptor;
114     
115     memset (&hints, 0, sizeof (hints));
116     hints.ai_family = PF_UNSPEC;
117     hints.ai_socktype = SOCK_STREAM;
118     hints.ai_flags |= AI_CANONNAME;
119     if (getaddrinfo (hostname, NULL, &hints, &res)) {
120         return NULL;
121     }
122     while (res) {
123         switch (res->ai_family) {
124         case AF_INET:
125             ip4 = (struct sockaddr_in *) res->ai_addr;
126             break;
127         case AF_INET6:
128             ip6 = (struct sockaddr_in6 *) res->ai_addr;
129             break;
130         }
131         freeres = res;
132         res = res->ai_next;
133         freeaddrinfo(res);
134     }
135     
136     if(ip6) {
137         sockfd = socket(AF_INET6, SOCK_STREAM, 0);
138         if(sockfd == -1) return NULL;
139         
140         ip6->sin6_family = AF_INET6;
141         ip6->sin6_port = htons(port);
142         
143         struct sockaddr_in6 *ip6vhost = NULL;
144         if (bind && !getaddrinfo(bind, NULL, &hints, &res)) {
145             while (res) {
146                 switch (res->ai_family) {
147                 case AF_INET6:
148                     ip6vhost = (struct sockaddr_in6 *) res->ai_addr;
149                     break;
150                 }
151                 freeres = res;
152                 res = res->ai_next;
153                 freeaddrinfo(res);
154             }
155         }
156         if(ip6vhost) {
157             ip6vhost->sin6_family = AF_INET6;
158             ip6vhost->sin6_port = htons(0);
159             bind(sockfd, (struct sockaddr*)ip6vhost, sizeof(*ip6vhost));
160         }
161         dstaddr = (struct sockaddr*)ip6;
162         dstaddrlen = sizeof(*ip6);
163     } else if(ip4) {
164         sockfd = socket(AF_INET, SOCK_STREAM, 0);
165         if(sockfd == -1) return NULL;
166         
167         ip4->sin_family = AF_INET;
168         ip4->sin_port = htons(port);
169         
170         struct sockaddr_in *ip4vhost = NULL;
171         if (bind && !getaddrinfo(bind, NULL, &hints, &res)) {
172             while (res) {
173                 switch (res->ai_family) {
174                 case AF_INET:
175                     ip4vhost = (struct sockaddr_in *) res->ai_addr;
176                     break;
177                 }
178                 freeres = res;
179                 res = res->ai_next;
180                 freeaddrinfo(res);
181             }
182         }
183         if(ip4vhost) {
184             ip4vhost->sin_family = AF_INET;
185             ip4vhost->sin_port = htons(0);
186             bind(sockfd, (struct sockaddr*)ip4vhost, sizeof(*ip4vhost));
187         }
188         dstaddr = (struct sockaddr*)ip4;
189         dstaddrlen = sizeof(*ip4);
190     } else
191         return NULL;
192     //make sockfd unblocking
193 #if defined(F_GETFL)
194     flags = fcntl(sockfd, F_GETFL);
195     fcntl(sockfd, F_SETFL, flags|O_NONBLOCK);
196     flags = fcntl(sockfd, F_GETFD);
197     fcntl(sockfd, F_SETFD, flags|FD_CLOEXEC);
198 #else
199     /* I hope you're using the Win32 backend or something else that
200      * automatically marks the file descriptor non-blocking...
201      */
202     (void)flags;
203 #endif
204     descriptor = iohandler_add(sockfd, IOTYPE_CLIENT, callback);
205     if(!descriptor) {
206         close(sockfd);
207         return NULL;
208     }
209     connect(sockfd, dstaddr, dstaddrlen); //returns EINPROGRESS here (nonblocking)
210     descriptor->state = IO_CONNECTING;
211     descriptor->read_lines = 1;
212     engine->update(descriptor);
213 }
214
215 struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, iohandler_callback *callback) {
216     int sockfd;
217     struct addrinfo hints, *res, *freeres;
218     struct sockaddr_in *ip4 = NULL;
219     struct sockaddr_in6 *ip6 = NULL;
220     struct IODescriptor *descriptor;
221     unsigned int opt;
222     
223     memset (&hints, 0, sizeof (hints));
224     hints.ai_family = PF_UNSPEC;
225     hints.ai_socktype = SOCK_STREAM;
226     hints.ai_flags |= AI_CANONNAME;
227     if (getaddrinfo (hostname, NULL, &hints, &res)) {
228         return NULL;
229     }
230     while (res) {
231         switch (res->ai_family) {
232         case AF_INET:
233             ip4 = (struct sockaddr_in *) res->ai_addr;
234             break;
235         case AF_INET6:
236             ip6 = (struct sockaddr_in6 *) res->ai_addr;
237             break;
238         }
239         freeres = res;
240         res = res->ai_next;
241         freeaddrinfo(res);
242     }
243     
244     if(ip6) {
245         sockfd = socket(AF_INET6, SOCK_STREAM, 0);
246         if(sockfd == -1) return NULL;
247         
248         opt = 1;
249         setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
250         
251         ip6->sin6_family = AF_INET6;
252         ip6->sin6_port = htons(port);
253         
254         bind(sockfd, (struct sockaddr*)ip6, sizeof(*ip6));
255     } else if(ip4) {
256         sockfd = socket(AF_INET, SOCK_STREAM, 0);
257         if(sockfd == -1) return NULL;
258         
259         opt = 1;
260         setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
261         
262         ip4->sin_family = AF_INET;
263         ip4->sin_port = htons(port);
264         
265         bind(sockfd, (struct sockaddr*)ip4, sizeof(*ip4));
266     } else
267         return NULL;
268     //make sockfd unblocking
269 #if defined(F_GETFL)
270     flags = fcntl(sockfd, F_GETFL);
271     fcntl(sockfd, F_SETFL, flags|O_NONBLOCK);
272     flags = fcntl(sockfd, F_GETFD);
273     fcntl(sockfd, F_SETFD, flags|FD_CLOEXEC);
274 #else
275     /* I hope you're using the Win32 backend or something else that
276      * automatically marks the file descriptor non-blocking...
277      */
278     (void)flags;
279 #endif
280     descriptor = iohandler_add(sockfd, IOTYPE_SERVER, callback);
281     if(!descriptor) {
282         close(sockfd);
283         return NULL;
284     }
285     listen(sockfd, 1);
286     descriptor->state = IO_LISTENING;
287     engine->update(descriptor);
288 }
289
290 void iohandler_write(struct IODescriptor *iofd, const char *line) {
291     size_t linelen = strlen(line);
292     iohandler_send(iofd, line, linelen);
293 }
294
295 void iohandler_send(struct IODescriptor *iofd, const char *data, size_t datalen) {
296     if(iofd->type == IOTYPE_TIMER) return; //can not write to timer? :D
297     if(iofd->writebuf.buflen < iofd->writebuf.bufpos + datalen) {
298         iohandler_increase_iobuf(&iofd->writebuf, iofd->writebuf.bufpos + datalen);
299         if(iofd->writebuf.buflen < iofd->writebuf.bufpos + datalen)
300             return;
301     }
302     memcpy(iofd->writebuf.buffer + iofd->writebuf.bufpos, data, datalen);
303     engine->update(iofd);
304 }
305
306 void iohandler_printf(struct IODescriptor *iofd, const char *text, ...) {
307     va_list arg_list;
308     char sendBuf[LINELEN];
309     int pos;
310     sendBuf[0] = '\0';
311     va_start(arg_list, text);
312     pos = vsnprintf(sendBuf, LINELEN - 2, text, arg_list);
313     va_end(arg_list);
314     if (pos < 0 || pos > (LINELEN - 2)) pos = LINELEN - 2;
315     sendBuf[pos] = '\n';
316     sendBuf[pos+1] = '\0';
317     iohandler_send(iofd, sendBuf, pos+1);
318 }
319
320 void iohandler_try_write(struct IODescriptor *iofd) {
321     if(!iofd->writebuf.bufpos) return;
322     int res = send(iofd->fd, iofd->writebuf.buffer, iofd->writebuf.bufpos, 0);
323     if(res < 0) {
324         if (errno != EAGAIN) {
325             //error: could not write
326         }
327     } else {
328         iofd->writebuf.bufpos -= res;
329         engine->update(iofd);
330     }
331 }
332
333 void iohandler_close(struct IODescriptor *iofd) {
334     //close IODescriptor
335     if(iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT || iofd->type == IOTYPE_STDIN)
336         close(iofd->fd);
337     iohandler_remove(iofd);
338 }
339
340 void iohandler_update(struct IODescriptor *iofd) {
341     engine->update(iofd);
342 }
343
344 static void iohandler_trigger_event(struct IOEvent *event) {
345     if(!event->iofd->callback) return;
346     event->iofd->callback(event);
347 }
348
349 void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) {
350     struct IOEvent callback_event;
351     callback_event.type = IOEVENT_IGNORE;
352     callback_event.iofd = iofd;
353     switch(iofd->state) {
354         case IO_CLOSED:
355             if(iofd->type == IOTYPE_TIMER)
356                 callback_event.type = IOEVENT_TIMEOUT;
357             break;
358         case IO_LISTENING:
359             callback_event.data.accept_fd = accept(iofd->fd, NULL, 0);
360             if(callback_event.data.accept_fd < 0) {
361                 //error: could not accept
362             } else
363                 callback_event.type = IOEVENT_ACCEPT;
364             break;
365         case IO_CONNECTING:
366             if(readable) { //could not connect
367                 callback_event.type = IOEVENT_NOTCONNECTED;
368                 socklen_t arglen;
369                 arglen = sizeof(callback_event.errno);
370                 if (getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &callback_event.errno, &arglen) < 0)
371                     callback_event.data.errno = errno;
372                 iofd->state = IO_CLOSED;
373             } else if(writeable) {
374                 callback_event.type = IOEVENT_CONNECTED;
375                 iofd->state = IO_CONNECTED;
376                 engine->update(iofd);
377             }
378             break;
379         case IO_CONNECTED:
380             if(readable) {
381                 if(iofd->read_lines) {
382                     int bytes = recv(iofd->fd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos, 0);
383                     if(bytes <= 0) {
384                         if (errno != EAGAIN) {
385                             callback_event.type = IOEVENT_CLOSED;
386                             callback_event.data.errno = errno;
387                         }
388                     } else {
389                         int i, used_bytes = 0, buffer_offset = 0;
390                         iofd->readbuf.bufpos += bytes;
391                         callback_event.type = IOEVENT_RECV;
392                         for(i = 0; i < iofd->readbuf.bufpos; i++) {
393                             if(iofd->readbuf.buffer[i] == '\r' && iofd->readbuf.buffer[i+1] == '\n')
394                                 iofd->readbuf.buffer[i] = 0;
395                             else if(iofd->readbuf.buffer[i] == '\n' || iofd->readbuf.buffer[i] == '\r') {
396                                 iofd->readbuf.buffer[i] = 0;
397                                 callback_event.data.recv_str = iofd->readbuf.buffer + used_bytes;
398                                 used_bytes = i+1;
399                                 iohandler_trigger_event(&callback_event);
400                             } else if(i + 1 - used_bytes >= LINELEN) { //512 max
401                                 iofd->readbuf.buffer[i] = 0;
402                                 callback_event.data.recv_str = iofd->readbuf.buffer + used_bytes;
403                                 for(; i < iofd->readbuf.bufpos; i++) { //skip the rest of the line
404                                     if(iofd->readbuf.buffer[i] == '\n' || (iofd->readbuf.buffer[i] == '\r' && iofd->readbuf.buffer[i+1] != '\n')) {
405                                         break;
406                                     }
407                                 }
408                                 used_bytes = i+1;
409                                 iohandler_trigger_event(&callback_event);
410                             }
411                         }
412                         if(used_bytes) {
413                             if(used_bytes == iofd->readbuf.bufpos)
414                                 iofd->readbuf.bufpos = 0;
415                             else
416                                 memmove(iofd->readbuf.buffer, iofd->readbuf.buffer + used_bytes, iofd->readbuf.bufpos - used_bytes);
417                         }
418                         callback_event.type = IOEVENT_IGNORE;
419                     }
420                 } else
421                     callback_event.type = IOEVENT_READABLE;
422             }
423             if(writeable) {
424                 iohandler_try_write(iofd);
425             }
426             break;
427     }
428     if(callback_event.type != IOEVENT_IGNORE)
429         iohandler_trigger_event(&callback_event);
430 }
431
432 void iohandler_poll() {
433     if(engine) {
434         struct timeval timeout;
435         timeout.tv_sec = IO_MAX_TIMEOUT;
436         timeout.tv_usec = 0;
437         engine->loop(&timeout);
438     }
439 }
440