[IOMultiplexerV2] dev snapshot
[NextIRCd.git] / src / IOSockets.c
1 /* IOSockets.c - IOMultiplexer v2
2  * Copyright (C) 2014  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 #define _IOHandler_internals
18 #include "IOInternal.h"
19 #include "IOHandler.h"
20 #include "IOSockets.h"
21 #include "IOLog.h"
22 #include "IODNSLookup.h"
23
24 #ifdef HAVE_PTHREAD_H
25 static pthread_mutex_t iosocket_sync, iosocket_dns_sync;
26 #endif
27
28 static struct _IOSocket *iosocket_first = NULL;
29 static struct _IOSocket *iosocket_last = NULL;
30
31 /* IO Engines */
32 extern struct IOEngine engine_select; /* select system call (should always be useable) */
33 extern struct IOEngine engine_kevent;
34 extern struct IOEngine engine_epoll;
35 extern struct IOEngine engine_win32;
36
37 struct IOEngine *engine = NULL;
38
39
40 static void iosocket_increase_buffer(struct IOSocketBuffer *iobuf, size_t required);
41 static int iosocket_parse_address(const char *hostname, struct IODNSAddress *addr, int records);
42 static int iosocket_lookup_hostname(struct _IOSocket *iosock, const char *hostname, int records, int bindaddr);
43 static void iosocket_lookup_apply(struct _IOSocket *iosock, struct IODNSAddress *addr, int bindlookup);
44 static void iosocket_connect_finish(struct _IOSocket *iosock);
45 static void iosocket_listen_finish(struct _IOSocket *iosock);
46
47
48
49
50 static void iosockets_init_engine() {
51     //try other engines
52         if(!engine && engine_kevent.init && engine_kevent.init())
53                 engine = &engine_kevent;
54         if(!engine && engine_epoll.init && engine_epoll.init())
55                 engine = &engine_epoll;
56         if(!engine && engine_win32.init && engine_win32.init())
57                 engine = &engine_win32;
58     
59     if (!engine) {
60         if(engine_select.init())
61             engine = &engine_select;
62         else {
63             iolog_trigger(IOLOG_FATAL, "found no useable IO engine");
64             return;
65         }
66     }
67     iolog_trigger(IOLOG_DEBUG, "using %s IOSockets engine", engine->name);
68 }
69
70 void _init_sockets() {
71         IOTHREAD_MUTEX_INIT(iosocket_sync);
72         IOTHREAD_MUTEX_INIT(iosocket_dns_sync);
73         iosockets_init_engine();
74 }
75
76
77 struct _IOSocket _create_socket() {
78         struct _IOSocket *iosock = calloc(1, sizeof(*iosock));
79         if(!iosock) {
80                 iolog_trigger(IOLOG_ERROR, "could not allocate memory for _IOSocket in %s:%d", __FILE__, __LINE__);
81                 return NULL;
82         }
83         IOSYNCHRONIZE(iosocket_sync);
84         if(iosocket_last)
85                 iosocket_last->next = iosock;
86         else
87                 iosocket_first = iosock;
88         iosock->prev = iosocket_last;
89         iosocket_last = iosock;
90         IODESYNCHRONIZE(iosocket_sync);
91         return iosock;
92 }
93
94 void _free_socket(struct _IOSocket *iosock) {
95         iosocket_deactivate(iosock);
96         IOSYNCHRONIZE(iosocket_sync);
97         if(iosock->prev)
98                 iosock->prev->next = iosock->next;
99         else
100                 iosocket_first = iosock->next;
101         if(iosock->next)
102                 iosock->next->prev = iosock->prev;
103         else
104                 iosocket_last = iosock->prev;
105         IODESYNCHRONIZE(iosocket_sync);
106         
107         if(iosock->bind.addr.addresslen)
108                 free(iosock->bind.addr.address);
109         if(iosock->dest.addr.addresslen)
110                 free(iosock->dest.addr.address);
111         if(iosock->readbuf.buffer)
112                 free(iosock->readbuf.buffer);
113         if(iosock->writebuf.buffer)
114                 free(iosock->writebuf.buffer);
115         
116         free(iosock);
117 }
118
119 static void iosocket_activate(struct _IOSocket *iosock) {
120         if((iosock->flags & IOSOCKETFLAG_ACTIVE))
121                 return;
122         iosock->flags |= IOSOCKETFLAG_ACTIVE;
123         engine->add(iosock);
124 }
125
126 static void iosocket_deactivate(struct _IOSocket *iosock) {
127         if(!(iosock->flags & IOSOCKETFLAG_ACTIVE))
128                 return;
129         iosock->flags &= ~IOSOCKETFLAG_ACTIVE;
130         engine->remove(iosock);
131 }
132
133 static void iosocket_increase_buffer(struct IOSocketBuffer *iobuf, size_t required) {
134     if(iobuf->buflen >= required) return;
135     char *new_buf = realloc(iobuf->buffer, required + 2);
136     if(new_buf) {
137         iobuf->buffer = new_buf;
138         iobuf->buflen = required;
139     }
140 }
141
142 static int iosocket_parse_address(const char *hostname, struct IODNSAddress *addr, int records) {
143         int ret;
144         if((flags & IOSOCKET_ADDR_IPV4)) {
145                 struct sockaddr_in ip4addr;
146                 ret = inet_pton(AF_INET, hostname, &(ip4addr.sin_addr));
147                 if(ret == 1) {
148                         addr->addresslen = sizeof(*ip4addr);
149                         addr->address = malloc(addr->addresslen);
150                         if(!addr->address) {
151                                 iolog_trigger(IOLOG_ERROR, "could not allocate memory for sockaddr in %s:%d", __FILE__, __LINE__);
152                                 return -1;
153                         }
154                         memcpy(addr->address, ip4addr, addr->addresslen);
155                         return 1;
156                 }
157         }
158         if((flags & IOSOCKET_ADDR_IPV6)) {
159                 struct sockaddr_in6 ip6addr;
160                 ret = inet_pton(AF_INET6, hostname, &(ip6addr.sin6_addr));
161                 if(ret == 1) {
162                         addr->addresslen = sizeof(*ip6addr);
163                         addr->address = malloc(addr->addresslen);
164                         if(!addr->address) {
165                                 iolog_trigger(IOLOG_ERROR, "could not allocate memory for sockaddr in %s:%d", __FILE__, __LINE__);
166                                 return -1;
167                         }
168                         memcpy(addr->address, ip6addr, addr->addresslen);
169                         return 1;
170                 }
171         }
172         return 0;
173 }
174
175 static int iosocket_lookup_hostname(struct _IOSocket *iosock, const char *hostname, int records, int bindaddr) {
176         struct IOSocketDNSLookup *lookup = calloc(1, sizeof(*lookup));
177         if(!lookup) {
178                 iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOSocketDNSLookup in %s:%d", __FILE__, __LINE__);
179                 return 0;
180         }
181         
182         struct _IODNSQuery *query = _create_dnsquery();
183         if(!query) {
184                 free(lookup);
185                 return 0;
186         }
187         
188         query->parent = lookup;
189         query->flags |= IODNSFLAG_PARENT_SOCKET;
190         lookup->iosocket = iosocket;
191         lookup->query = query;
192         strncpy(lookup->hostname, hostname, sizeof(lookup->hostname));
193         lookup->hostname[sizeof(lookup->hostname)-1] = 0;
194         if(bindaddr) {
195                 lookup->bindlookup = 1;
196                 iosock->bind.addrlookup = lookup;
197         } else {
198                 lookup->bindlookup = 0;
199                 iosock->dest.addrlookup = lookup;
200         }
201         
202         int dnsrecords = 0;
203         if((records & IOSOCKET_ADDR_IPV4))
204                 dnsrecords |= IODNS_RECORD_A;
205         if((records & IOSOCKET_ADDR_IPV6))
206                 dnsrecords |= IODNS_RECORD_AAAA;
207         
208         query->request.host = strdup(hostname);
209         query->type = (dnsrecords & IODNS_FORWARD);
210         
211         _start_dnsquery(query);
212         return 1;
213 }
214
215 void iosocket_lookup_callback(struct IOSocketDNSLookup *lookup, struct IODNSEvent *event) {
216         lookup->query = NULL;
217         IOSYNCHRONIZE(iosocket_dns_sync);
218         struct _IOSocket *iosock = lookup->iosock;
219         if(iosock == NULL) {
220                 IODESYNCHRONIZE(iosocket_dns_sync);
221                 return;
222         }
223         if(event->type == IODNSEVENT_SUCCESS)
224                 lookup->result = event->result;
225         else
226                 lookup->result = NULL;
227         
228         if(lookup->bindlookup) {
229                 iosock->flags &= ~IOSOCKETFLAG_PENDING_BINDDNS;
230                 iosock->flags |= IOSOCKETFLAG_DNSDONE_BINDDNS;
231         } else {
232                 iosock->flags &= ~IOSOCKETFLAG_PENDING_DESTDNS;
233                 iosock->flags |= IOSOCKETFLAG_DNSDONE_DESTDNS;
234         }
235         
236         int dns_finished = 0;
237         if((iosock->flags & (IOSOCKETFLAG_PENDING_BINDDNS | IOSOCKETFLAG_PENDING_DESTDNS)) == 0)
238                 dns_finished = 1;
239         IODESYNCHRONIZE(iosocket_dns_sync);
240         if(dns_finished) {
241                 int ret;
242                 ret = iosocket_lookup_apply(iosock);
243                 if(ret) { //if ret=0 an error occured in iosocket_lookup_apply and we should stop here.
244                         if((iosock->flags & IOSOCKETFLAG_LISTENING))
245                                 iosocket_listen_finish(iosock);
246                         else
247                                 iosocket_connect_finish(iosock);
248                 }
249         }
250 }
251
252 static void iosocket_lookup_apply(struct _IOSocket *iosock) {
253         char errbuf[512];
254         struct IOSocketDNSLookup *bind_lookup = ((iosock->flags & IOSOCKETFLAG_DNSDONE_BINDDNS) ? iosock->bind.addrlookup : NULL);
255         struct IOSocketDNSLookup *dest_lookup = ((iosock->flags & IOSOCKETFLAG_DNSDONE_DESTDNS) ? iosock->dest.addrlookup : NULL);
256         if(!bind_lookup && !dest_lookup) {
257                 iosock->flags |= IOSOCKETFLAG_DNSERROR;
258                 sprintf(errbuf, "Internal Error");
259                 iolog_trigger(IOLOG_ERROR, "trying to apply lookup results without any lookups processed in %s:%d", __FILE__, __LINE__);
260                 goto iosocket_lookup_clear;
261         }
262         
263         struct IODNSResult *result;
264         int bind_numip4 = 0, bind_numip6 = 0;
265         int dest_numip4 = 0, dest_numip6 = 0;
266         
267         if(bind_lookup) {
268                 for(result = bind_lookup->result; result; result = result->next) {
269                         if((result->type & IODNS_RECORD_A))
270                                 bind_numip4++;
271                         if((result->type & IODNS_RECORD_AAAA))
272                                 bind_numip6++;
273                 }
274         }
275         if(dest_lookup) {
276                 for(result = dest_lookup->result; result; result = result->next) {
277                         if((result->type & IODNS_RECORD_A))
278                                 dest_numip4++;
279                         if((result->type & IODNS_RECORD_AAAA))
280                                 dest_numip6++;
281                 }
282         }
283         int useip6 = 0;
284         int useip4 = 0;
285         if(bind_lookup && (bind_numip6 == 0 && bind_numip4 == 0)) {
286                 iosock->flags |= IOSOCKETFLAG_DNSERROR;
287                 sprintf(errbuf, "could not lookup bind address (%s)", bind_lookup->hostname);
288                 goto iosocket_lookup_clear;
289         } else if(dest_lookup && (dest_numip6 == 0 && dest_numip4 == 0)) {
290                 iosock->flags |= IOSOCKETFLAG_DNSERROR;
291                 sprintf(errbuf, "could not lookup destination address (%s)", dest_lookup->hostname);
292                 goto iosocket_lookup_clear;
293         } else if(bind_lookup && dest_lookup) {
294                 if(bind_numip6 > 0 && dest_numip6 > 0)
295                         useip6 = 1;
296                 else if(bind_numip4 > 0 && dest_numip4 > 0)
297                         useip4 = 1;
298                 else {
299                         iosock->flags |= IOSOCKETFLAG_DNSERROR;
300                         sprintf(errbuf, "could not lookup adresses of the same IP family for bind and destination host. (bind: %d ip4, %d ip6 | dest: %d ip4, %d ip6)", bind_numip4, bind_numip6, dest_numip4, dest_numip6);
301                         goto iosocket_lookup_clear;
302                 }
303         } else if(bind_lookup) {
304                 if(bind_numip6)
305                         useip6 = 1;
306                 else if(bind_numip4)
307                         useip4 = 1;
308         } else if(dest_lookup) {
309                 if(dest_numip6)
310                         useip6 = 1;
311                 else if(dest_numip4)
312                         useip4 = 1;
313         }
314         
315         int usetype = 0;
316         if(useip6) {
317                 usetype = IODNS_RECORD_AAAA;
318                 iosock->flags |= IOSOCKETFLAG_IPV6SOCKET;
319         } else {
320                 usetype = IODNS_RECORD_A;
321         }
322         
323         #define IOSOCKET_APPLY_COPYADDR(type) \
324         iosock->type.addr.addresslen = result->result.addr.addresslen; \
325         iosock->type.addr.address = malloc(result->result.addr.addresslen); \
326         if(!iosock->type.addr.address) { \
327                 iolog_trigger(IOLOG_ERROR, "could not allocate memory for sockaddr in %s:%d", __FILE__, __LINE__); \
328                 iosock->type.addr.addresslen = 0; \
329                 iosock->flags |= IOSOCKETFLAG_DNSERROR; \
330                 sprintf(errbuf, "could not allocate memory for dns information"); \
331                 goto iosocket_lookup_clear; \
332         } \
333         memcpy(iosock->type.addr.address, result->result.addr.address, result->result.addr.addresslen);
334         
335         
336         if(bind_lookup) {
337                 int usenum = (useip6 ? bind_numip6 : bind_numip4);
338                 usenum = rand() % usenum;
339                 for(result = bind_lookup->result; result; result = result->next) {
340                         if((result->type & usetype)) {
341                                 if(usenum == 0) {
342                                         IOSOCKET_APPLY_COPYADDR(bind)
343                                         break;
344                                 }
345                                 usenum--;
346                         }
347                 }
348         } else
349                 iosock->bind.addr.addresslen = 0;
350         
351         if(dest_lookup) {
352                 int usenum = (useip6 ? dest_numip6 : dest_numip4);
353                 usenum = rand() % usenum;
354                 for(result = dest_lookup->result; result; result = result->next) {
355                         if((result->type & usetype)) {
356                                 if(usenum == 0) {
357                                         IOSOCKET_APPLY_COPYADDR(dest)
358                                         break;
359                                 }
360                                 usenum--;
361                         }
362                 }
363         } else
364                 iosock->dest.addr.addresslen = 0;
365         
366         iosocket_lookup_clear:
367         if(bind_lookup) {
368                 if(bind_lookup->result)
369                         iodns_free_result(bind_lookup->result);
370                 free(bind_lookup);
371         }
372         if(dest_lookup) {
373                 if(bind_lookup->result)
374                         iodns_free_result(dest_lookup->result);
375                 free(dest_lookup);
376         }
377         
378         if((iosock->flags & IOSOCKETFLAG_DNSERROR)) {
379                 // TODO: trigger error
380                 
381                 return 0;
382         } else
383                 return 1;
384 }
385
386 static void iosocket_connect_finish(struct _IOSocket *iosock) {
387         int sockfd;
388         if((iosock->flags & IOSOCKETFLAG_IPV6SOCKET))
389                 sockfd = socket(AF_INET6, SOCK_STREAM, 0);
390         else
391                 sockfd = socket(AF_INET, SOCK_STREAM, 0);
392         if(sockfd == -1) {
393                 iolog_trigger(IOLOG_ERROR, "could not create socket in %s:%d", __FILE__, __LINE__);
394                 // TODO: trigger error
395                 
396                 return;
397         }
398         
399         // set port and bind address
400         if((iosock->flags & IOSOCKETFLAG_IPV6SOCKET)) {
401                 struct sockaddr_in6 *ip6 = iosock->dest.addr.address;
402                 ip6->sin6_family = AF_INET6;
403         ip6->sin6_port = htons(iosock->port);
404                 
405                 if(iosock->bind.addr.addresslen) {
406                         struct sockaddr_in6 *ip6bind = iosock->bind.addr.address;
407                         ip6bind->sin6_family = AF_INET6;
408                         ip6bind->sin6_port = htons(0);
409                         
410                         bind(sockfd, (struct sockaddr*)ip6bind, sizeof(*ip6bind));
411                 }
412         } else {
413                 struct sockaddr_in *ip4 = iosock->dest.addr.address;
414                 ip->sin_family = AF_INET;
415         ip->sin_port = htons(iosock->port);
416                 
417                 if(iosock->bind.addr.addresslen) {
418                         struct sockaddr_in *ip4bind = iosock->bind.addr.address;
419                         ip4bind->sin_family = AF_INET;
420                         ip4bind->sin6_port = htons(0);
421                         
422                         bind(sockfd, (struct sockaddr*)ip4bind, sizeof(*ip4bind));
423                 }
424         }
425         
426         // prevent SIGPIPE
427     #ifndef WIN32
428     #if defined(SO_NOSIGPIPE)
429     {
430         int set = 1;
431         setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
432     }
433     #else
434     signal(SIGPIPE, SIG_IGN);
435     #endif
436     #endif
437         
438     // make sockfd unblocking
439     #if defined(F_GETFL)
440     {
441         int fcntl_flags;
442         fcntl_flags = fcntl(sockfd, F_GETFL);
443         fcntl(sockfd, F_SETFL, fcntl_flags|O_NONBLOCK);
444         fcntl_flags = fcntl(sockfd, F_GETFD);
445         fcntl(sockfd, F_SETFD, fcntl_flags|FD_CLOEXEC);
446     }
447     #else
448     /* I hope you're using the Win32 backend or something else that
449      * automatically marks the file descriptor non-blocking...
450      */
451     #endif
452         
453         connect(sockfd, iosock->dest.addr.address, iosock->dest.addr.addresslen); //returns EINPROGRESS here (nonblocking)
454         iosock->fd = sockfd;
455         
456         iosocket_activate(iosock);
457 }
458
459 static void iosocket_listen_finish(struct _IOSocket *iosock) {
460         int sockfd;
461         if((iosock->flags & IOSOCKETFLAG_IPV6SOCKET))
462                 sockfd = socket(AF_INET6, SOCK_STREAM, 0);
463         else
464                 sockfd = socket(AF_INET, SOCK_STREAM, 0);
465         if(sockfd == -1) {
466                 iolog_trigger(IOLOG_ERROR, "could not create socket in %s:%d", __FILE__, __LINE__);
467                 // TODO: trigger error
468                 
469                 return;
470         }
471         
472         // set port and bind address
473         if((iosock->flags & IOSOCKETFLAG_IPV6SOCKET)) {
474                 struct sockaddr_in6 *ip6bind = iosock->bind.addr.address;
475                 ip6bind->sin6_family = AF_INET6;
476                 ip6bind->sin6_port = htons(0);
477                 
478                 int opt = 1;
479         setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
480                 
481                 bind(sockfd, (struct sockaddr*)ip6bind, sizeof(*ip6bind));
482         } else {
483                 struct sockaddr_in *ip4bind = iosock->bind.addr.address;
484                 ip4bind->sin_family = AF_INET;
485                 ip4bind->sin6_port = htons(0);
486                 
487                 int opt = 1;
488         setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
489                 
490                 bind(sockfd, (struct sockaddr*)ip4bind, sizeof(*ip4bind));
491         }
492         
493         // prevent SIGPIPE
494     #ifndef WIN32
495     #if defined(SO_NOSIGPIPE)
496     {
497         int set = 1;
498         setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
499     }
500     #else
501     signal(SIGPIPE, SIG_IGN);
502     #endif
503     #endif
504         
505     // make sockfd unblocking
506     #if defined(F_GETFL)
507     {
508         int fcntl_flags;
509         fcntl_flags = fcntl(sockfd, F_GETFL);
510         fcntl(sockfd, F_SETFL, fcntl_flags|O_NONBLOCK);
511         fcntl_flags = fcntl(sockfd, F_GETFD);
512         fcntl(sockfd, F_SETFD, fcntl_flags|FD_CLOEXEC);
513     }
514     #else
515     /* I hope you're using the Win32 backend or something else that
516      * automatically marks the file descriptor non-blocking...
517      */
518     #endif
519         
520         listen(sockfd, 1);
521         iosock->fd = sockfd;
522         
523         iosocket_activate(iosock);
524 }
525
526 /* public functions */
527
528 struct IOSocket *iosocket_connect(const char *hostname, unsigned int port, int ssl, const char *bindhost, iosocket_callback *callback) {
529         return iosocket_connect_flags(hostname, port, ssl, bindhost, callback, (IOSOCKET_ADDR_IPV4 | IOSOCKET_ADDR_IPV6));
530 }
531
532 struct IOSocket *iosocket_connect_flags(const char *hostname, unsigned int port, int ssl, const char *bindhost, iosocket_callback *callback, int flags) {
533         struct IOSocket *iodescriptor = calloc(1, sizeof(*iodescriptor));
534         if(!iodescriptor) {
535                 iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOSocket in %s:%d", __FILE__, __LINE__);
536                 return NULL;
537         }
538         
539         struct _IOSocket *iosock = _create_socket();
540         if(!iosock) {
541                 free(iodescriptor);
542                 return NULL;
543         }
544         
545         iodescriptor->iosocket = iosock;
546         iodescriptor->status = IOSOCKET_CONNECTING;
547         iodescriptor->callback = callback;
548         iosock->parent = iodescriptor;
549         iosock->flags |= IOSOCKETFLAG_PARENT_PUBLIC;
550         if(ssl)
551                 iosock->flags |= IOSOCKETFLAG_SSLSOCKET;
552         
553         IOSYNCHRONIZE(iosocket_dns_sync);
554         if(bindhost) {
555                 switch(iosocket_parse_address(bindhost, &iosock->bind.addr, flags)) {
556                 case -1:
557                         free(iosock);
558                         return NULL;
559                 case 0:
560                         /* start dns lookup */
561                         iosock->flags |= IOSOCKETFLAG_PENDING_BINDDNS;
562                         iosocket_lookup_hostname(iosock, bindhost, flags, 1);
563                         break;
564                 case 1:
565                         /* valid address */
566                         break;
567                 }
568         }
569         switch(iosocket_parse_address(hostname, &iosock->dest.addr, flags)) {
570         case -1:
571                 free(iosock);
572                 return NULL;
573         case 0:
574                 /* start dns lookup */
575                 iosock->flags |= IOSOCKETFLAG_PENDING_DESTDNS;
576                 iosocket_lookup_hostname(iosock, hostname, flags, 0);
577                 break;
578         case 1:
579                 /* valid address */
580                 break;
581         }
582         IODESYNCHRONIZE(iosocket_dns_sync);
583         if((iosock->flags & (IOSOCKETFLAG_PENDING_BINDDNS | IOSOCKETFLAG_PENDING_DESTDNS)) == 0) {
584                 iosocket_connect_finish(iosock);
585         }
586         return iodescriptor;
587 }
588
589 struct IOSocket *iosocket_listen(const char *hostname, unsigned int port, iosocket_callback *callback) {
590         return iosocket_listen_flags(hostname, port, callback, (IOSOCKET_ADDR_IPV4 | IOSOCKET_ADDR_IPV6));
591 }
592
593 struct IOSocket *iosocket_listen_flags(const char *hostname, unsigned int port, iosocket_callback *callback, int flags) {
594         struct IOSocket *iodescriptor = calloc(1, sizeof(*iodescriptor));
595         if(!iodescriptor) {
596                 iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOSocket in %s:%d", __FILE__, __LINE__);
597                 return NULL;
598         }
599         
600         struct _IOSocket *iosock = _create_socket();
601         if(!iosock) {
602                 free(iodescriptor);
603                 return NULL;
604         }
605         
606         iodescriptor->iosocket = iosock;
607         iodescriptor->status = IOSOCKET_CONNECTING;
608         iodescriptor->callback = callback;
609         iosock->parent = iodescriptor;
610         iosock->flags |= IOSOCKETFLAG_PARENT_PUBLIC | IOSOCKETFLAG_LISTENING;
611         if(ssl)
612                 iosock->flags |= IOSOCKETFLAG_SSLSOCKET;
613         
614         IOSYNCHRONIZE(iosocket_dns_sync);
615         switch(iosocket_parse_address(hostname, &iosock->bind.addr, flags)) {
616         case -1:
617                 free(iosock);
618                 return NULL;
619         case 0:
620                 /* start dns lookup */
621                 iosock->flags |= IOSOCKETFLAG_PENDING_BINDDNS;
622                 iosocket_lookup_hostname(iosock, hostname, flags, 1);
623                 break;
624         case 1:
625                 /* valid address */
626                 break;
627         }
628         IODESYNCHRONIZE(iosocket_dns_sync);
629         if((iosock->flags & IOSOCKETFLAG_PENDING_BINDDNS) == 0) {
630                 iosocket_listen_finish(iosock);
631         }
632         return iodescriptor;
633 }
634
635 struct IOSocket *iosocket_listen_ssl(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iosocket_callback *callback) {
636         return iosocket_listen_ssl_flags(hostname, port, certfile, keyfile, callback, (IOSOCKET_ADDR_IPV4 | IOSOCKET_ADDR_IPV6));
637 }
638
639 struct IOSocket *iosocket_listen_ssl_flags(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iosocket_callback *callback, int flags) {
640         //TODO: SSL
641         return NULL;
642 }
643
644 void iosocket_close(struct IOSocket *iosocket) {
645         struct _IOSocket *iosock = iosocket->iosocket;
646         if(iosock == NULL) {
647                 iolog_trigger(IOLOG_WARNING, "called iosocket_close for destroyed IOSocket in %s:%d", __FILE__, __LINE__);
648                 return NULL;
649         }
650         
651         iosock->flags |= IOSOCKETFLAG_SHUTDOWN;
652         
653         if(iosock->writebuf.bufpos) {
654         //try to send everything before closing
655 #if defined(F_GETFL)
656         {
657             int flags;
658             flags = fcntl(iosock->fd, F_GETFL);
659             fcntl(iosock->fd, F_SETFL, flags & ~O_NONBLOCK);
660             flags = fcntl(iosock->fd, F_GETFD);
661             fcntl(iosock->fd, F_SETFD, flags|FD_CLOEXEC);
662         }
663 #else
664         iosocket_deactivate(iosock);
665 #endif
666         iosocket_try_write(iosock);
667     }
668         //close IOSocket
669     if(iosock->sslnode) {
670                 //TODO: SSL
671         }
672         close(iosock->fd);
673     _free_socket(iosock);
674         iosocket->iosocket = NULL;
675         iosocket->status = IOSOCKET_CLOSED;
676         iogc_add(iosocket);
677 }
678
679 static int iohandler_try_write(struct _IOSocket *iosock) {
680     if(!iosock->writebuf.bufpos) return 0;
681     iolog_trigger(IOLOG_DEBUG, "write writebuf (%d bytes) to socket (fd: %d)", iosock->writebuf.bufpos, iosock->fd);
682     int res;
683     if(iosock->sslnode) {
684         /* res = iohandler_ssl_write(iofd, iofd->writebuf.buffer, iofd->writebuf.bufpos); */
685                 // TODO
686     } else
687         res = send(iosock->fd, iosock->writebuf.buffer, iosock->writebuf.bufpos, 0);
688     if(res < 0) {
689         if (errno != EAGAIN && errno != EWOULDBLOCK)
690             iolog_trigger(IOLOG_ERROR, "could not write to socket (fd: %d): %d - %s", iosock->fd, errno, strerror(errno));
691         else
692             res = 0;
693     } else {
694         iosock->writebuf.bufpos -= res;
695                 if((iosock->flags & (IOSOCKETFLAG_ACTIVE & IOSOCKETFLAG_SHUTDOWN)) == IOSOCKETFLAG_ACTIVE)
696                         engine->update(iosock);
697     }
698     return res;
699 }
700
701 void iosocket_send(struct IOSocket *iosocket, const char *data, size_t datalen) {
702         struct _IOSocket *iosock = iosocket->iosocket;
703         if(iosock == NULL) {
704                 iolog_trigger(IOLOG_WARNING, "called iosocket_close for destroyed IOSocket in %s:%d", __FILE__, __LINE__);
705                 return;
706         }
707         if(iosock->flags & IOSOCKETFLAG_SHUTDOWN) {
708         iolog_trigger(IOLOG_ERROR, "could not write to socket (socket is closing)");
709         return;
710     }
711     iolog_trigger(IOLOG_DEBUG, "add %d to writebuf (fd: %d): %s", datalen, iosock->fd, data);
712     if(iosock->writebuf.buflen < iosock->writebuf.bufpos + datalen) {
713         iolog_trigger(IOLOG_DEBUG, "increase writebuf (curr: %d) to %d (+%d bytes)", iosock->writebuf.buflen, iosock->writebuf.bufpos + datalen, (iosock->writebuf.bufpos + datalen - iosock->writebuf.buflen));
714         iosocket_increase_buffer(&iosock->writebuf, iosock->writebuf.bufpos + datalen);
715         if(iosock->writebuf.buflen < iosock->writebuf.bufpos + datalen) {
716             iolog_trigger(IOLOG_ERROR, "increase writebuf (curr: %d) to %d (+%d bytes) FAILED", iosock->writebuf.buflen, iosock->writebuf.bufpos + datalen, (iosock->writebuf.bufpos + datalen - iosock->writebuf.buflen));
717             return;
718         }
719     }
720     memcpy(iosock->writebuf.buffer + iosock->writebuf.bufpos, data, datalen);
721     iosock->writebuf.bufpos += datalen;
722         if((iosock->flags & IOSOCKETFLAG_ACTIVE))
723                 engine->update(iosock);
724 }
725
726 void iosocket_write(struct IOSocket *iosocket, const char *line) {
727         size_t linelen = strlen(line);
728     iohandler_send(iosocket, line, linelen);
729 }
730
731 void iosocket_printf(struct IOSocket *iosocket, const char *text, ...) {
732         va_list arg_list;
733     char sendBuf[IOSOCKET_LINE_LEN];
734     int pos;
735     sendBuf[0] = '\0';
736     va_start(arg_list, text);
737     pos = vsnprintf(sendBuf, IOSOCKET_LINE_LEN - 2, text, arg_list);
738     va_end(arg_list);
739     if (pos < 0 || pos > (IOSOCKET_LINE_LEN - 2)) pos = IOSOCKET_LINE_LEN - 2;
740     sendBuf[pos] = '\n';
741     sendBuf[pos+1] = '\0';
742     iohandler_send(iosocket, sendBuf, pos+1);
743 }
744
745
746
747
748