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