Added preliminary Symbian support.
[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 < 0 || !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, 64);
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                                   remote_ip_addr ? TRUE : FALSE, 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     SILC_LOG_DEBUG(("UDP packet from %s:%d", remote_ip_addr, *remote_port));
403   }
404
405   return len;
406 }
407
408 /* Send UDP packet */
409
410 int silc_net_udp_send(SilcStream stream,
411                       const char *remote_ip_addr, int remote_port,
412                       const unsigned char *data, SilcUInt32 data_len)
413 {
414   SilcSocketStream sock = stream;
415   SilcSockaddr remote;
416   int ret;
417
418   SILC_LOG_DEBUG(("Sending data to UDP socket %d", sock->sock));
419
420   /* Set sockaddr */
421   if (!silc_net_set_sockaddr(&remote, remote_ip_addr, remote_port))
422     return -2;
423
424   /* Send */
425   ret = sendto(sock->sock, data, data_len, 0, &remote.sa,
426                SIZEOF_SOCKADDR(remote));
427   if (ret < 0) {
428     if (errno == EAGAIN || errno == EINTR) {
429       SILC_LOG_DEBUG(("Could not send immediately, will do it later"));
430       silc_schedule_set_listen_fd(sock->schedule, sock->sock,
431                                   SILC_TASK_READ | SILC_TASK_WRITE, FALSE);
432       return -1;
433     }
434     SILC_LOG_DEBUG(("Cannot send to UDP socket: %s", strerror(errno)));
435     silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
436     sock->sock_error = errno;
437     return -2;
438   }
439
440   SILC_LOG_DEBUG(("Sent data %d bytes", ret));
441   if (silc_schedule_get_fd_events(sock->schedule, sock->sock) &
442       SILC_TASK_WRITE)
443     silc_schedule_set_listen_fd(sock->schedule, sock->sock,
444                                 SILC_TASK_READ, FALSE);
445
446   return ret;
447 }
448
449 /* Asynchronous TCP/IP connecting */
450
451 typedef struct {
452   SilcNetStatus status;
453   SilcSocketStreamStatus stream_status;
454   SilcStream stream;
455   SilcFSMStruct fsm;
456   SilcFSMEventStruct event;
457   SilcAsyncOperation op;
458   SilcAsyncOperation sop;
459   char *local_ip;
460   char *remote;
461   char ip_addr[64];
462   int sock;
463   SilcNetCallback callback;
464   void *context;
465   unsigned int port     : 24;
466   unsigned int retry    : 7;
467   unsigned int aborted  : 1;
468 } *SilcNetConnect;
469
470 SILC_FSM_STATE(silc_net_connect_st_start);
471 SILC_FSM_STATE(silc_net_connect_st_connected);
472 SILC_FSM_STATE(silc_net_connect_st_stream);
473 SILC_FSM_STATE(silc_net_connect_st_finish);
474
475 SILC_TASK_CALLBACK(silc_net_connect_wait)
476 {
477   SilcNetConnect conn = context;
478   SILC_FSM_EVENT_SIGNAL(&conn->event);
479   silc_schedule_task_del_by_fd(schedule, conn->sock);
480 }
481
482 SILC_FSM_STATE(silc_net_connect_st_start)
483 {
484   SilcNetConnect conn = fsm_context;
485   int sock, rval;
486   SilcSockaddr desthost;
487   SilcBool prefer_ipv6 = TRUE;
488
489   if (conn->aborted) {
490     /** Aborted */
491     silc_fsm_next(fsm, silc_net_connect_st_finish);
492     SILC_FSM_CONTINUE;
493   }
494
495   /* Do host lookup */
496  retry:
497   if (!silc_net_gethostbyname(conn->remote, prefer_ipv6,
498                               conn->ip_addr, sizeof(conn->ip_addr))) {
499     SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
500                     "host", conn->remote));
501
502     /** Network unreachable */
503     conn->status = SILC_NET_HOST_UNREACHABLE;
504     silc_fsm_next(fsm, silc_net_connect_st_finish);
505     SILC_FSM_CONTINUE;
506   }
507
508   /* Set sockaddr for this connection */
509   if (!silc_net_set_sockaddr(&desthost, conn->ip_addr, conn->port)) {
510     /** Sockaddr failed */
511     silc_fsm_next(fsm, silc_net_connect_st_finish);
512     SILC_FSM_CONTINUE;
513   }
514
515   /* Create the connection socket */
516   sock = socket(desthost.sin.sin_family, SOCK_STREAM, 0);
517   if (sock < 0) {
518     /* If address is IPv6, then fallback to IPv4 and see whether we can do
519        better with that on socket creation. */
520     if (prefer_ipv6 && silc_net_is_ip6(conn->ip_addr)) {
521       prefer_ipv6 = FALSE;
522       goto retry;
523     }
524
525     /** Cannot create socket */
526     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
527     silc_fsm_next(fsm, silc_net_connect_st_finish);
528     SILC_FSM_CONTINUE;
529   }
530
531   /* Bind to the local address if provided */
532   if (conn->local_ip) {
533     SilcSockaddr local;
534
535     /* Set sockaddr for local listener, and try to bind it. */
536     if (silc_net_set_sockaddr(&local, conn->local_ip, 0))
537       bind(sock, &local.sa, SIZEOF_SOCKADDR(local));
538   }
539
540   /* Set the socket to non-blocking mode */
541   silc_net_set_socket_nonblock(sock);
542
543   /* Connect to the host */
544   rval = connect(sock, &desthost.sa, SIZEOF_SOCKADDR(desthost));
545   if (rval < 0) {
546     if (errno != EINPROGRESS) {
547       /* retry using an IPv4 adress, if IPv6 didn't work */
548       if (prefer_ipv6 && silc_net_is_ip6(conn->ip_addr)) {
549         shutdown(sock, 2);
550         close(sock);
551
552         prefer_ipv6 = FALSE;
553         goto retry;
554       }
555
556       shutdown(sock, 2);
557       close(sock);
558
559       /** Cannot connect to remote host */
560       SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
561       silc_fsm_next(fsm, silc_net_connect_st_finish);
562       SILC_FSM_CONTINUE;
563     }
564   }
565
566   /* Set appropriate options */
567 #if defined(TCP_NODELAY)
568   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
569 #endif
570   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
571
572   SILC_LOG_DEBUG(("Connection operation in progress"));
573
574   conn->sock = sock;
575
576   /** Wait for connection */
577   silc_fsm_next(fsm, silc_net_connect_st_connected);
578   silc_fsm_event_init(&conn->event, fsm);
579   silc_schedule_task_add_fd(silc_fsm_get_schedule(fsm), sock,
580                             silc_net_connect_wait, conn);
581   silc_schedule_set_listen_fd(silc_fsm_get_schedule(fsm), sock,
582                               SILC_TASK_WRITE, FALSE);
583   SILC_FSM_EVENT_WAIT(&conn->event);
584   SILC_FSM_CONTINUE;
585 }
586
587 static void silc_net_connect_wait_stream(SilcSocketStreamStatus status,
588                                          SilcStream stream, void *context)
589 {
590   SilcNetConnect conn = context;
591   conn->stream_status = status;
592   conn->stream = stream;
593   SILC_FSM_CALL_CONTINUE(&conn->fsm);
594 }
595
596 SILC_FSM_STATE(silc_net_connect_st_connected)
597 {
598   SilcNetConnect conn = fsm_context;
599   SilcSchedule schedule = silc_fsm_get_schedule(fsm);
600   int opt = EINVAL, optlen = sizeof(opt), ret;
601
602   if (conn->aborted) {
603     /** Aborted */
604     silc_fsm_next(fsm, silc_net_connect_st_finish);
605     SILC_FSM_CONTINUE;
606   }
607
608   ret = silc_net_get_socket_opt(conn->sock, SOL_SOCKET, SO_ERROR,
609                                 &opt, &optlen);
610
611   silc_schedule_task_del_by_fd(schedule, conn->sock);
612   silc_schedule_unset_listen_fd(schedule, conn->sock);
613
614   if (ret != 0 || opt != 0) {
615     if (conn->retry) {
616       /** Retry connecting */
617       SILC_LOG_DEBUG(("Retry connecting"));
618       conn->retry--;
619       silc_net_close_connection(conn->sock);
620       silc_fsm_next(fsm, silc_net_connect_st_start);
621       SILC_FSM_CONTINUE;
622     }
623
624 #if defined(ECONNREFUSED)
625     if (errno == ECONNREFUSED)
626       conn->status = SILC_NET_CONNECTION_REFUSED;
627 #endif /* ECONNREFUSED */
628 #if defined(ETIMEDOUT)
629     if (errno == ETIMEDOUT)
630       conn->status = SILC_NET_CONNECTION_TIMEOUT;
631 #endif /* ETIMEDOUT */
632 #if defined(ENETUNREACH)
633     if (errno == ENETUNREACH)
634       conn->status = SILC_NET_HOST_UNREACHABLE;
635 #endif /* ENETUNREACH */
636
637     /** Connecting failed */
638     SILC_LOG_DEBUG(("Connecting failed"));
639     silc_fsm_next(fsm, silc_net_connect_st_finish);
640     SILC_FSM_CONTINUE;
641   }
642
643   /** Connection created */
644   silc_fsm_next(fsm, silc_net_connect_st_stream);
645   SILC_FSM_CALL((conn->sop = silc_socket_tcp_stream_create(
646                                      conn->sock, FALSE, FALSE,
647                                      schedule,
648                                      silc_net_connect_wait_stream, conn)));
649 }
650
651 SILC_FSM_STATE(silc_net_connect_st_stream)
652 {
653   SilcNetConnect conn = fsm_context;
654
655   if (conn->aborted) {
656     /** Aborted */
657     silc_fsm_next(fsm, silc_net_connect_st_finish);
658     SILC_FSM_CONTINUE;
659   }
660
661   if (conn->stream_status != SILC_SOCKET_OK) {
662     /** Stream creation failed */
663     if (conn->stream_status == SILC_SOCKET_UNKNOWN_IP)
664       conn->status = SILC_NET_UNKNOWN_IP;
665     else if (conn->stream_status == SILC_SOCKET_UNKNOWN_HOST)
666       conn->status = SILC_NET_UNKNOWN_HOST;
667     else
668       conn->status = SILC_NET_ERROR;
669     silc_fsm_next(fsm, silc_net_connect_st_finish);
670     SILC_FSM_CONTINUE;
671   }
672
673   /* Set stream information */
674   silc_socket_stream_set_info(conn->stream,
675                               !silc_net_is_ip(conn->remote) ? conn->remote :
676                               conn->ip_addr, conn->ip_addr, conn->port);
677
678   /** Stream created successfully */
679   SILC_LOG_DEBUG(("Connected successfully"));
680   conn->status = SILC_NET_OK;
681   silc_fsm_next(fsm, silc_net_connect_st_finish);
682   SILC_FSM_CONTINUE;
683 }
684
685 SILC_FSM_STATE(silc_net_connect_st_finish)
686 {
687   SilcNetConnect conn = fsm_context;
688
689   /* Deliver error or new stream */
690   if (!conn->aborted) {
691     conn->callback(conn->status, conn->stream, conn->context);
692     if (conn->op)
693       silc_async_free(conn->op);
694     if (conn->sop)
695       silc_async_free(conn->sop);
696   }
697
698   SILC_FSM_FINISH;
699 }
700
701 static void silc_net_connect_abort(SilcAsyncOperation op, void *context)
702 {
703   SilcNetConnect conn = context;
704   conn->aborted = TRUE;
705
706   /* Abort underlaying stream creation too */
707   if (conn->sop)
708     silc_async_abort(conn->op, NULL, NULL);
709 }
710
711 static void silc_net_connect_destructor(SilcFSM fsm, void *fsm_context,
712                                         void *destructor_context)
713 {
714   SilcNetConnect conn = fsm_context;
715   silc_free(conn->local_ip);
716   silc_free(conn->remote);
717   silc_free(conn);
718 }
719
720 /* Create asynchronous TCP/IP connection. */
721
722 SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
723                                         const char *remote_ip_addr,
724                                         int remote_port,
725                                         SilcSchedule schedule,
726                                         SilcNetCallback callback,
727                                         void *context)
728 {
729   SilcNetConnect conn;
730
731   if (!remote_ip_addr || remote_port < 1 || !schedule || !callback)
732     return NULL;
733
734   SILC_LOG_DEBUG(("Creating connection to host %s port %d",
735                   remote_ip_addr, remote_port));
736
737   conn = silc_calloc(1, sizeof(*conn));
738   if (!conn) {
739     callback(SILC_NET_NO_MEMORY, NULL, context);
740     return NULL;
741   }
742
743   /* Start async operation */
744   conn->op = silc_async_alloc(silc_net_connect_abort, NULL, conn);
745   if (!conn->op) {
746     silc_free(conn);
747     callback(SILC_NET_NO_MEMORY, NULL, context);
748     return NULL;
749   }
750
751   if (local_ip_addr)
752     conn->local_ip = strdup(local_ip_addr);
753   conn->remote = strdup(remote_ip_addr);
754   if (!conn->remote) {
755     silc_async_free(conn->op);
756     silc_free(conn->local_ip);
757     silc_free(conn);
758     callback(SILC_NET_NO_MEMORY, NULL, context);
759     return NULL;
760   }
761   conn->port = remote_port;
762   conn->callback = callback;
763   conn->context = context;
764   conn->retry = 1;
765   conn->status = SILC_NET_ERROR;
766
767   silc_fsm_init(&conn->fsm, conn, silc_net_connect_destructor, NULL, schedule);
768   silc_fsm_start(&conn->fsm, silc_net_connect_st_start);
769
770   return conn->op;
771 }
772
773 /* Closes the connection by closing the socket connection. */
774
775 void silc_net_close_connection(int sock)
776 {
777   close(sock);
778 }
779
780 /* Set's the socket to non-blocking mode. */
781
782 int silc_net_set_socket_nonblock(SilcSocket sock)
783 {
784   return fcntl((int)sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
785 }
786
787 /* Converts the IP number string from numbers-and-dots notation to
788    binary form. */
789
790 SilcBool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len)
791 {
792   int ret = 0;
793
794   if (silc_net_is_ip4(addr)) {
795     /* IPv4 address */
796     struct in_addr tmp;
797     ret = inet_aton(addr, &tmp);
798     if (bin_len < 4)
799       return FALSE;
800
801     memcpy(bin, (unsigned char *)&tmp.s_addr, 4);
802 #ifdef HAVE_IPV6
803   } else {
804     struct addrinfo hints, *ai;
805     SilcSockaddr *s;
806
807     /* IPv6 address */
808     if (bin_len < 16)
809       return FALSE;
810
811     memset(&hints, 0, sizeof(hints));
812     hints.ai_family = AF_INET6;
813     if (getaddrinfo(addr, NULL, &hints, &ai))
814       return FALSE;
815
816     if (ai) {
817       s = (SilcSockaddr *)ai->ai_addr;
818       memcpy(bin, &s->sin6.sin6_addr, sizeof(s->sin6.sin6_addr));
819       freeaddrinfo(ai);
820     }
821
822     ret = TRUE;
823 #endif /* HAVE_IPV6 */
824   }
825
826   return ret != 0;
827 }