Remove FD task after connection is created.
[silc.git] / lib / silcutil / unix / silcunixnet.c
1 /*
2
3   silcunixnet.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2006 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22 #include "silcnet.h"
23
24 #ifdef HAVE_IPV6
25 #define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ?    \
26   sizeof(so.sin6) : sizeof(so.sin))
27 #else
28 #define SIZEOF_SOCKADDR(so) (sizeof(so.sin))
29 #endif
30
31 typedef union {
32   struct sockaddr sa;
33   struct sockaddr_in sin;
34 #ifdef HAVE_IPV6
35   struct sockaddr_in6 sin6;
36 #endif
37 } SilcSockaddr;
38
39 static SilcBool silc_net_set_sockaddr(SilcSockaddr *addr, const char *ip_addr,
40                                       int port)
41 {
42   int len;
43
44   memset(addr, 0, sizeof(*addr));
45
46   /* Check for IPv4 and IPv6 addresses */
47   if (ip_addr) {
48     if (!silc_net_is_ip(ip_addr)) {
49       SILC_LOG_ERROR(("%s is not IP address", ip_addr));
50       return FALSE;
51     }
52
53     if (silc_net_is_ip4(ip_addr)) {
54       /* IPv4 address */
55       len = sizeof(addr->sin.sin_addr);
56       silc_net_addr2bin(ip_addr,
57                         (unsigned char *)&addr->sin.sin_addr.s_addr, len);
58       addr->sin.sin_family = AF_INET;
59       addr->sin.sin_port = port ? htons(port) : 0;
60     } else {
61 #ifdef HAVE_IPV6
62       /* IPv6 address */
63       len = sizeof(addr->sin6.sin6_addr);
64       silc_net_addr2bin(ip_addr,
65                         (unsigned char *)&addr->sin6.sin6_addr, len);
66       addr->sin6.sin6_family = AF_INET6;
67       addr->sin6.sin6_port = port ? htons(port) : 0;
68 #else
69       SILC_LOG_ERROR(("IPv6 support is not compiled in"));
70       return FALSE;
71 #endif
72     }
73   } else {
74     /* Any address */
75     addr->sin.sin_family = AF_INET;
76     addr->sin.sin_addr.s_addr = INADDR_ANY;
77     if (port)
78       addr->sin.sin_port = htons(port);
79   }
80
81   return TRUE;
82 }
83
84 /* Deliver new stream to upper layer */
85
86 static void silc_net_accept_stream(SilcSocketStreamStatus status,
87                                    SilcStream stream, void *context)
88 {
89   SilcNetListener listener = context;
90
91   if (status != SILC_SOCKET_OK)
92     return;
93
94   listener->callback(SILC_NET_OK, stream, listener->context);
95 }
96
97 /* Accept incoming connection and notify upper layer */
98
99 SILC_TASK_CALLBACK(silc_net_accept)
100 {
101   SilcNetListener listener = context;
102   int sock;
103
104   SILC_LOG_DEBUG(("Accepting new connection"));
105
106   sock = silc_net_accept_connection(fd);
107   if (sock < 0)
108     return;
109
110   /* Set socket options */
111   silc_net_set_socket_nonblock(sock);
112   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
113
114   /* Create socket stream */
115   silc_socket_tcp_stream_create(sock, listener->lookup,
116                                 listener->require_fqdn, schedule,
117                                 silc_net_accept_stream, listener);
118 }
119
120 /* Create TCP network listener */
121
122 SilcNetListener
123 silc_net_tcp_create_listener(const char **local_ip_addr,
124                              SilcUInt32 local_ip_count, int port,
125                              SilcBool lookup, SilcBool require_fqdn,
126                              SilcSchedule schedule,
127                              SilcNetCallback callback, void *context)
128 {
129   SilcNetListener listener = NULL;
130   SilcSockaddr server;
131   int i, sock, rval;
132   const char *ipany = "0.0.0.0";
133
134   SILC_LOG_DEBUG(("Creating TCP listener"));
135
136   if (port < 1 || !schedule || !callback)
137     goto err;
138
139   listener = silc_calloc(1, sizeof(*listener));
140   if (!listener) {
141     callback(SILC_NET_NO_MEMORY, NULL, context);
142     return NULL;
143   }
144   listener->schedule = schedule;
145   listener->callback = callback;
146   listener->context = context;
147   listener->require_fqdn = require_fqdn;
148   listener->lookup = lookup;
149
150   if (local_ip_count > 0) {
151     listener->socks = silc_calloc(local_ip_count, sizeof(*listener->socks));
152     if (!listener->socks) {
153       callback(SILC_NET_NO_MEMORY, NULL, context);
154       return NULL;
155     }
156   } else {
157     listener->socks = silc_calloc(1, sizeof(*listener->socks));
158     if (!listener->socks) {
159       callback(SILC_NET_NO_MEMORY, NULL, context);
160       return NULL;
161     }
162
163     local_ip_count = 1;
164   }
165
166   /* Bind to local addresses */
167   for (i = 0; i < local_ip_count; i++) {
168     SILC_LOG_DEBUG(("Binding to local address %s",
169                     local_ip_addr ? local_ip_addr[i] : ipany));
170
171     /* Set sockaddr for server */
172     if (!silc_net_set_sockaddr(&server,
173                                local_ip_addr ? local_ip_addr[i] : ipany,
174                                port))
175       goto err;
176
177     /* Create the socket */
178     sock = socket(server.sin.sin_family, SOCK_STREAM, 0);
179     if (sock < 0) {
180       SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
181       goto err;
182     }
183
184     /* Set the socket options */
185     rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
186     if (rval < 0) {
187       SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
188       goto err;
189     }
190
191     /* Bind the listener socket */
192     rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server));
193     if (rval < 0) {
194       SILC_LOG_DEBUG(("Cannot bind socket: %s", strerror(errno)));
195       goto err;
196     }
197
198     /* Specify that we are listenning */
199     rval = listen(sock, 5);
200     if (rval < 0) {
201       SILC_LOG_ERROR(("Cannot set socket listenning: %s", strerror(errno)));
202       goto err;
203     }
204
205     /* Set the server socket to non-blocking mode */
206     silc_net_set_socket_nonblock(sock);
207
208     /* Schedule for incoming connections */
209     silc_schedule_task_add_fd(schedule, sock, silc_net_accept, listener);
210
211     SILC_LOG_DEBUG(("TCP listener created, fd=%d", sock));
212     listener->socks[i] = sock;
213     listener->socks_count++;
214   }
215
216   return listener;
217
218  err:
219   if (callback)
220     callback(SILC_NET_ERROR, NULL, context);
221   if (listener)
222     silc_net_close_listener(listener);
223   return NULL;
224 }
225
226 /* Close network listener */
227
228 void silc_net_close_listener(SilcNetListener listener)
229 {
230   int i;
231
232   SILC_LOG_DEBUG(("Closing network listener"));
233
234   for (i = 0; i < listener->socks_count; i++) {
235     silc_schedule_task_del_by_fd(listener->schedule, listener->socks[i]);
236     shutdown(listener->socks[i], 2);
237     close(listener->socks[i]);
238   }
239
240   silc_free(listener->socks);
241   silc_free(listener);
242 }
243
244 /* Create UDP stream */
245
246 SilcStream
247 silc_net_udp_connect(const char *local_ip_addr, int local_port,
248                      const char *remote_ip_addr, int remote_port,
249                      SilcSchedule schedule)
250 {
251   SilcStream stream;
252   SilcSockaddr server;
253   int sock = -1, rval;
254   const char *ipany = "0.0.0.0";
255
256   SILC_LOG_DEBUG(("Creating UDP stream"));
257
258   if (!schedule)
259     goto err;
260
261   /* Bind to local addresses */
262   SILC_LOG_DEBUG(("Binding to local address %s",
263                   local_ip_addr ? local_ip_addr : ipany));
264
265   /* Set sockaddr for server */
266   if (!silc_net_set_sockaddr(&server, local_ip_addr ? local_ip_addr : ipany,
267                              local_port))
268     goto err;
269
270   /* Create the socket */
271   sock = socket(server.sin.sin_family, SOCK_DGRAM, 0);
272   if (sock < 0) {
273     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
274     goto err;
275   }
276
277   /* Set the socket options */
278   rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
279   if (rval < 0) {
280     SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
281     goto err;
282   }
283 #ifdef SO_REUSEPORT
284   rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEPORT, 1);
285   if (rval < 0) {
286     SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
287     goto err;
288   }
289 #endif /* SO_REUSEPORT */
290
291   /* Bind the listener socket */
292   rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server));
293   if (rval < 0) {
294     SILC_LOG_DEBUG(("Cannot bind socket: %s", strerror(errno)));
295     goto err;
296   }
297
298   /* Set socket to non-blocking mode */
299   silc_net_set_socket_nonblock(sock);
300
301   /* Set to connected state if remote address is provided. */
302   if (remote_ip_addr && remote_port) {
303     if (!silc_net_set_sockaddr(&server, remote_ip_addr, remote_port))
304       goto err;
305
306     rval = connect(sock, &server.sa, SIZEOF_SOCKADDR(server));
307     if (rval < 0) {
308       SILC_LOG_DEBUG(("Cannot connect UDP stream: %s", strerror(errno)));
309       goto err;
310     }
311   }
312
313   /* Set send and receive buffer size */
314 #ifdef SO_SNDBUF
315   rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_SNDBUF, 65535);
316   if (rval < 0) {
317     SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
318     goto err;
319   }
320 #endif /* SO_SNDBUF */
321 #ifdef SO_RCVBUF
322   rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_RCVBUF, 65535);
323   if (rval < 0) {
324     SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
325     goto err;
326   }
327 #endif /* SO_RCVBUF */
328
329   /* Encapsulate into socket stream */
330   stream =
331     silc_socket_udp_stream_create(sock, local_ip_addr ?
332                                   silc_net_is_ip6(local_ip_addr) : FALSE,
333                                   schedule);
334   if (!stream)
335     goto err;
336
337   SILC_LOG_DEBUG(("UDP stream created, fd=%d", sock));
338   return stream;
339
340  err:
341   if (sock != -1)
342     close(sock);
343   return NULL;
344 }
345
346 /* Receive UDP packet */
347
348 int silc_net_udp_receive(SilcStream stream, char *remote_ip_addr,
349                          SilcUInt32 remote_ip_addr_size, int *remote_port,
350                          unsigned char *ret_data, SilcUInt32 data_size)
351 {
352   SilcSocketStream sock = stream;
353   SilcSockaddr s;
354   struct sockaddr *from;
355   int len, flen;
356
357   SILC_LOG_DEBUG(("Reading data from UDP socket %d", sock->sock));
358
359   if (remote_ip_addr && remote_port) {
360     if (sock->ipv6) {
361       from = (struct sockaddr *)&s.sin6;
362       flen = sizeof(s.sin6);
363     } else {
364       from = (struct sockaddr *)&s.sin;
365       flen = sizeof(s.sin);
366     }
367     len = recvfrom(sock->sock, ret_data, data_size, 0, from, &flen);
368   } else
369     len = recv(sock->sock, ret_data, data_size, 0);
370
371   if (len < 0) {
372     if (errno == EAGAIN || errno == EINTR) {
373       SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
374       silc_schedule_set_listen_fd(sock->schedule, sock->sock,
375                                   SILC_TASK_READ, FALSE);
376       return -1;
377     }
378     SILC_LOG_DEBUG(("Cannot read from UDP socket: %d:%s",
379                     sock->sock, strerror(errno)));
380     silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
381     sock->sock_error = errno;
382     return -2;
383   }
384
385   SILC_LOG_DEBUG(("Read %d bytes", len));
386
387   if (!len)
388     silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
389
390   /* Return remote address */
391   if (remote_ip_addr && remote_port) {
392     if (sock->ipv6) {
393       *remote_port = ntohs(s.sin6.sin6_port);
394       inet_ntop(AF_INET6, &s.sin6.sin6_addr, remote_ip_addr,
395                 remote_ip_addr_size);
396     } else {
397       *remote_port = ntohs(s.sin.sin_port);
398       inet_ntop(AF_INET, &s.sin.sin_addr, remote_ip_addr,
399                 remote_ip_addr_size);
400     }
401   }
402
403   return len;
404 }
405
406 /* Send UDP packet */
407
408 void silc_net_udp_send(SilcStream stream,
409                        const char *remote_ip_addr, int remote_port,
410                        const unsigned char *data, SilcUInt32 data_len)
411 {
412   SilcSocketStream sock = stream;
413   SilcSockaddr remote;
414
415   SILC_LOG_DEBUG(("Writing data to UDP socket %d", sock->sock));
416
417   /* Set sockaddr for server */
418   if (!silc_net_set_sockaddr(&remote, remote_ip_addr, remote_port))
419     return;
420
421   /* Send */
422   sendto(sock->sock, data, data_len, 0, &remote.sa, SIZEOF_SOCKADDR(remote));
423 }
424
425 /* Asynchronous TCP/IP connecting */
426
427 typedef struct {
428   SilcNetStatus status;
429   SilcSocketStreamStatus stream_status;
430   SilcStream stream;
431   SilcFSMStruct fsm;
432   SilcFSMSemaStruct sema;
433   SilcAsyncOperation op;
434   SilcAsyncOperation sop;
435   char *local_ip;
436   char *remote;
437   char ip_addr[64];
438   int sock;
439   SilcNetCallback callback;
440   void *context;
441   unsigned int port     : 24;
442   unsigned int retry    : 7;
443   unsigned int aborted  : 1;
444 } *SilcNetConnect;
445
446 SILC_FSM_STATE(silc_net_connect_st_start);
447 SILC_FSM_STATE(silc_net_connect_st_connected);
448 SILC_FSM_STATE(silc_net_connect_st_stream);
449 SILC_FSM_STATE(silc_net_connect_st_finish);
450
451 SILC_TASK_CALLBACK(silc_net_connect_wait)
452 {
453   SilcNetConnect conn = context;
454   SILC_FSM_SEMA_POST(&conn->sema);
455   silc_schedule_task_del_by_fd(schedule, conn->sock);
456 }
457
458 SILC_FSM_STATE(silc_net_connect_st_start)
459 {
460   SilcNetConnect conn = fsm_context;
461   int sock, rval;
462   SilcSockaddr desthost;
463   SilcBool prefer_ipv6 = TRUE;
464
465   if (conn->aborted) {
466     /** Aborted */
467     silc_fsm_next(fsm, silc_net_connect_st_finish);
468     return SILC_FSM_CONTINUE;
469   }
470
471   /* Do host lookup */
472  retry:
473   if (!silc_net_gethostbyname(conn->remote, prefer_ipv6,
474                               conn->ip_addr, sizeof(conn->ip_addr))) {
475     SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
476                     "host", conn->remote));
477
478     /** Network unreachable */
479     conn->status = SILC_NET_HOST_UNREACHABLE;
480     silc_fsm_next(fsm, silc_net_connect_st_finish);
481     return SILC_FSM_CONTINUE;
482   }
483
484   /* Set sockaddr for this connection */
485   if (!silc_net_set_sockaddr(&desthost, conn->ip_addr, conn->port)) {
486     /** Sockaddr failed */
487     silc_fsm_next(fsm, silc_net_connect_st_finish);
488     return SILC_FSM_CONTINUE;
489   }
490
491   /* Create the connection socket */
492   sock = socket(desthost.sin.sin_family, SOCK_STREAM, 0);
493   if (sock < 0) {
494     /* If address is IPv6, then fallback to IPv4 and see whether we can do
495        better with that on socket creation. */
496     if (prefer_ipv6 && silc_net_is_ip6(conn->ip_addr)) {
497       prefer_ipv6 = FALSE;
498       goto retry;
499     }
500
501     /** Cannot create socket */
502     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
503     silc_fsm_next(fsm, silc_net_connect_st_finish);
504     return SILC_FSM_CONTINUE;
505   }
506
507   /* Bind to the local address if provided */
508   if (conn->local_ip) {
509     SilcSockaddr local;
510
511     /* Set sockaddr for local listener, and try to bind it. */
512     if (silc_net_set_sockaddr(&local, conn->local_ip, 0))
513       bind(sock, &local.sa, SIZEOF_SOCKADDR(local));
514   }
515
516   /* Set the socket to non-blocking mode */
517   silc_net_set_socket_nonblock(sock);
518
519   /* Connect to the host */
520   rval = connect(sock, &desthost.sa, SIZEOF_SOCKADDR(desthost));
521   if (rval < 0) {
522     if (errno != EINPROGRESS) {
523       /* retry using an IPv4 adress, if IPv6 didn't work */
524       if (prefer_ipv6 && silc_net_is_ip6(conn->ip_addr)) {
525         shutdown(sock, 2);
526         close(sock);
527
528         prefer_ipv6 = FALSE;
529         goto retry;
530       }
531
532       shutdown(sock, 2);
533       close(sock);
534
535       /** Cannot connect to remote host */
536       SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
537       silc_fsm_next(fsm, silc_net_connect_st_finish);
538       return SILC_FSM_CONTINUE;
539     }
540   }
541
542   /* Set appropriate options */
543 #if defined(TCP_NODELAY)
544   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
545 #endif
546   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
547
548   SILC_LOG_DEBUG(("Connection operation in progress"));
549
550   conn->sock = sock;
551
552   /** Wait for connection */
553   silc_fsm_next(fsm, silc_net_connect_st_connected);
554   silc_fsm_sema_init(&conn->sema, fsm, 0);
555   silc_schedule_task_add_fd(silc_fsm_get_schedule(fsm), sock,
556                             silc_net_connect_wait, conn);
557   silc_schedule_set_listen_fd(silc_fsm_get_schedule(fsm), sock,
558                               SILC_TASK_WRITE, FALSE);
559   SILC_FSM_SEMA_WAIT(&conn->sema);
560   return SILC_FSM_CONTINUE;
561 }
562
563 static void silc_net_connect_wait_stream(SilcSocketStreamStatus status,
564                                          SilcStream stream, void *context)
565 {
566   SilcNetConnect conn = context;
567   conn->stream_status = status;
568   conn->stream = stream;
569   SILC_FSM_CALL_CONTINUE(&conn->fsm);
570 }
571
572 SILC_FSM_STATE(silc_net_connect_st_connected)
573 {
574   SilcNetConnect conn = fsm_context;
575   SilcSchedule schedule = silc_fsm_get_schedule(fsm);
576   int opt = EINVAL, optlen = sizeof(opt), ret;
577
578   if (conn->aborted) {
579     /** Aborted */
580     silc_fsm_next(fsm, silc_net_connect_st_finish);
581     return SILC_FSM_CONTINUE;
582   }
583
584   ret = silc_net_get_socket_opt(conn->sock, SOL_SOCKET, SO_ERROR,
585                                 &opt, &optlen);
586
587   silc_schedule_task_del_by_fd(schedule, conn->sock);
588   silc_schedule_unset_listen_fd(schedule, conn->sock);
589
590   if (ret != 0 || opt != 0) {
591     if (conn->retry) {
592       /** Retry connecting */
593       SILC_LOG_DEBUG(("Retry connecting"));
594       conn->retry--;
595       silc_net_close_connection(conn->sock);
596       silc_fsm_next(fsm, silc_net_connect_st_start);
597       return SILC_FSM_CONTINUE;
598     }
599
600 #if defined(ECONNREFUSED)
601     if (errno == ECONNREFUSED)
602       conn->status = SILC_NET_CONNECTION_REFUSED;
603 #endif /* ECONNREFUSED */
604 #if defined(ETIMEDOUT)
605     if (errno == ETIMEDOUT)
606       conn->status = SILC_NET_CONNECTION_TIMEOUT;
607 #endif /* ETIMEDOUT */
608 #if defined(ENETUNREACH)
609     if (errno == ENETUNREACH)
610       conn->status = SILC_NET_HOST_UNREACHABLE;
611 #endif /* ENETUNREACH */
612
613     /** Connecting failed */
614     SILC_LOG_DEBUG(("Connecting failed"));
615     silc_fsm_next(fsm, silc_net_connect_st_finish);
616     return SILC_FSM_CONTINUE;
617   }
618
619   /** Connection created */
620   silc_fsm_next(fsm, silc_net_connect_st_stream);
621   SILC_FSM_CALL((conn->sop = silc_socket_tcp_stream_create(
622                                      conn->sock, FALSE, FALSE,
623                                      schedule,
624                                      silc_net_connect_wait_stream, conn)));
625 }
626
627 SILC_FSM_STATE(silc_net_connect_st_stream)
628 {
629   SilcNetConnect conn = fsm_context;
630
631   if (conn->aborted) {
632     /** Aborted */
633     silc_fsm_next(fsm, silc_net_connect_st_finish);
634     return SILC_FSM_CONTINUE;
635   }
636
637   if (conn->stream_status != SILC_SOCKET_OK) {
638     /** Stream creation failed */
639     if (conn->stream_status == SILC_SOCKET_UNKNOWN_IP)
640       conn->status = SILC_NET_UNKNOWN_IP;
641     else if (conn->stream_status == SILC_SOCKET_UNKNOWN_HOST)
642       conn->status = SILC_NET_UNKNOWN_HOST;
643     else
644       conn->status = SILC_NET_ERROR;
645     silc_fsm_next(fsm, silc_net_connect_st_finish);
646     return SILC_FSM_CONTINUE;
647   }
648
649   /* Set stream information */
650   silc_socket_stream_set_info(conn->stream,
651                               !silc_net_is_ip(conn->remote) ? conn->remote :
652                               conn->ip_addr, conn->ip_addr, conn->port);
653
654   /** Stream created successfully */
655   SILC_LOG_DEBUG(("Connected successfully"));
656   conn->status = SILC_NET_OK;
657   silc_fsm_next(fsm, silc_net_connect_st_finish);
658   return SILC_FSM_CONTINUE;
659 }
660
661 SILC_FSM_STATE(silc_net_connect_st_finish)
662 {
663   SilcNetConnect conn = fsm_context;
664
665   /* Deliver error or new stream */
666   if (!conn->aborted) {
667     conn->callback(conn->status, conn->stream, conn->context);
668     if (conn->op)
669       silc_async_free(conn->op);
670     if (conn->sop)
671       silc_async_free(conn->sop);
672   }
673
674   return SILC_FSM_FINISH;
675 }
676
677 static void silc_net_connect_abort(SilcAsyncOperation op, void *context)
678 {
679   SilcNetConnect conn = context;
680   conn->aborted = TRUE;
681
682   /* Abort underlaying stream creation too */
683   if (conn->sop)
684     silc_async_abort(conn->op, NULL, NULL);
685 }
686
687 static void silc_net_connect_destructor(SilcFSM fsm, void *fsm_context,
688                                         void *destructor_context)
689 {
690   SilcNetConnect conn = fsm_context;
691   silc_free(conn->local_ip);
692   silc_free(conn->remote);
693   silc_free(conn);
694 }
695
696 /* Create asynchronous TCP/IP connection. */
697
698 SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
699                                         const char *remote_ip_addr,
700                                         int remote_port,
701                                         SilcSchedule schedule,
702                                         SilcNetCallback callback,
703                                         void *context)
704 {
705   SilcNetConnect conn;
706
707   if (!remote_ip_addr || remote_port < 1 || !schedule || !callback)
708     return NULL;
709
710   SILC_LOG_DEBUG(("Creating connection to host %s port %d",
711                   remote_ip_addr, remote_port));
712
713   conn = silc_calloc(1, sizeof(*conn));
714   if (!conn) {
715     callback(SILC_NET_NO_MEMORY, NULL, context);
716     return NULL;
717   }
718
719   /* Start async operation */
720   conn->op = silc_async_alloc(silc_net_connect_abort, NULL, conn);
721   if (!conn->op) {
722     callback(SILC_NET_NO_MEMORY, NULL, context);
723     return NULL;
724   }
725
726   if (local_ip_addr)
727     conn->local_ip = strdup(local_ip_addr);
728   conn->remote = strdup(remote_ip_addr);
729   if (!conn->remote) {
730     callback(SILC_NET_NO_MEMORY, NULL, context);
731     return NULL;
732   }
733   conn->port = remote_port;
734   conn->callback = callback;
735   conn->context = context;
736   conn->retry = 1;
737   conn->status = SILC_NET_ERROR;
738
739   silc_fsm_init(&conn->fsm, conn, silc_net_connect_destructor, NULL, schedule);
740   silc_fsm_start(&conn->fsm, silc_net_connect_st_start);
741
742   return conn->op;
743 }
744
745 /* Closes the connection by closing the socket connection. */
746
747 void silc_net_close_connection(int sock)
748 {
749   close(sock);
750 }
751
752 /* Set's the socket to non-blocking mode. */
753
754 int silc_net_set_socket_nonblock(int sock)
755 {
756   return fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
757 }
758
759 /* Converts the IP number string from numbers-and-dots notation to
760    binary form. */
761
762 SilcBool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len)
763 {
764   int ret = 0;
765
766   if (silc_net_is_ip4(addr)) {
767     /* IPv4 address */
768     struct in_addr tmp;
769     ret = inet_aton(addr, &tmp);
770     if (bin_len < 4)
771       return FALSE;
772
773     memcpy(bin, (unsigned char *)&tmp.s_addr, 4);
774 #ifdef HAVE_IPV6
775   } else {
776     struct addrinfo hints, *ai;
777     SilcSockaddr *s;
778
779     /* IPv6 address */
780     if (bin_len < 16)
781       return FALSE;
782
783     memset(&hints, 0, sizeof(hints));
784     hints.ai_family = AF_INET6;
785     if (getaddrinfo(addr, NULL, &hints, &ai))
786       return FALSE;
787
788     if (ai) {
789       s = (SilcSockaddr *)ai->ai_addr;
790       memcpy(bin, &s->sin6.sin6_addr, sizeof(s->sin6.sin6_addr));
791       freeaddrinfo(ai);
792     }
793
794     ret = TRUE;
795 #endif /* HAVE_IPV6 */
796   }
797
798   return ret != 0;
799 }