Merge branch 'silc.1.1.branch' of silc repo
[runtime.git] / lib / silcutil / unix / silcunixnet.c
1 /*
2
3   silcunixnet.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2008 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
20 #include "silcruntime.h"
21
22 /************************** Types and definitions ***************************/
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 utility functions **************************/
40
41 static SilcBool silc_net_set_sockaddr(SilcSockaddr *addr, const char *ip_addr,
42                                       int port)
43 {
44   int len;
45
46   memset(addr, 0, sizeof(*addr));
47
48   /* Check for IPv4 and IPv6 addresses */
49   if (ip_addr) {
50     if (!silc_net_is_ip(ip_addr)) {
51       SILC_LOG_ERROR(("%s is not IP address", ip_addr));
52       silc_set_errno_reason(SILC_ERR_BAD_IP, "%s is not an IP address",
53                             ip_addr);
54       return FALSE;
55     }
56
57     if (silc_net_is_ip4(ip_addr)) {
58       /* IPv4 address */
59       len = sizeof(addr->sin.sin_addr);
60       if (!silc_net_addr2bin(ip_addr,
61                              (unsigned char *)&addr->sin.sin_addr.s_addr, len))
62         return FALSE;
63       addr->sin.sin_family = AF_INET;
64       addr->sin.sin_port = port ? htons(port) : 0;
65     } else {
66 #ifdef HAVE_IPV6
67       /* IPv6 address */
68       len = sizeof(addr->sin6.sin6_addr);
69       if (!silc_net_addr2bin(ip_addr,
70                              (unsigned char *)&addr->sin6.sin6_addr, len))
71         return FALSE;
72       addr->sin6.sin6_family = AF_INET6;
73       addr->sin6.sin6_port = port ? htons(port) : 0;
74 #else
75       SILC_LOG_ERROR(("IPv6 support is not compiled in"));
76       silc_set_errno(SILC_ERR_NOT_SUPPORTED);
77       return FALSE;
78 #endif
79     }
80   } else {
81     /* Any address */
82     addr->sin.sin_family = AF_INET;
83     addr->sin.sin_addr.s_addr = INADDR_ANY;
84     if (port)
85       addr->sin.sin_port = htons(port);
86   }
87
88   return TRUE;
89 }
90
91 /****************************** TCP Listener ********************************/
92
93 /* Deliver new stream to upper layer */
94
95 static void silc_net_accept_stream(SilcResult status,
96                                    SilcStream stream, void *context)
97 {
98   SilcNetListener listener = context;
99
100   if (status != SILC_OK)
101     return;
102
103   listener->callback(SILC_OK, stream, listener->context);
104 }
105
106 /* Accept incoming connection and notify upper layer */
107
108 SILC_TASK_CALLBACK(silc_net_accept)
109 {
110   SilcNetListener listener = context;
111   int sock;
112
113   SILC_LOG_DEBUG(("Accepting new connection"));
114
115   sock = silc_net_accept_connection(fd);
116   if (sock < 0)
117     return;
118
119   /* Set socket options */
120   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
121
122   /* Create socket stream */
123   silc_socket_tcp_stream_create(sock, listener->lookup,
124                                 listener->require_fqdn, schedule,
125                                 silc_net_accept_stream, listener);
126 }
127
128 /* Create TCP network listener */
129
130 SilcNetListener
131 silc_net_tcp_create_listener(const char **local_ip_addr,
132                              SilcUInt32 local_ip_count, int port,
133                              SilcBool lookup, SilcBool require_fqdn,
134                              SilcSchedule schedule,
135                              SilcNetCallback callback, void *context)
136 {
137   SilcNetListener listener = NULL;
138   SilcSockaddr server;
139   int i, sock, rval;
140   const char *ipany = "0.0.0.0";
141
142   SILC_LOG_DEBUG(("Creating TCP listener"));
143
144   if (!schedule) {
145     schedule = silc_schedule_get_global();
146     if (!schedule) {
147       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
148       goto err;
149     }
150   }
151
152   if (port < 0 || !callback) {
153     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
154     goto err;
155   }
156
157   listener = silc_calloc(1, sizeof(*listener));
158   if (!listener)
159     return NULL;
160   listener->schedule = schedule;
161   listener->callback = callback;
162   listener->context = context;
163   listener->require_fqdn = require_fqdn;
164   listener->lookup = lookup;
165
166   if (local_ip_count > 0) {
167     listener->socks = silc_calloc(local_ip_count, sizeof(*listener->socks));
168     if (!listener->socks)
169       return NULL;
170   } else {
171     listener->socks = silc_calloc(1, sizeof(*listener->socks));
172     if (!listener->socks)
173       return NULL;
174
175     local_ip_count = 1;
176   }
177
178   /* Bind to local addresses */
179   for (i = 0; i < local_ip_count; i++) {
180     SILC_LOG_DEBUG(("Binding to local address %s:%d",
181                     local_ip_addr ? local_ip_addr[i] : ipany, port));
182
183     /* Set sockaddr for server */
184     if (!silc_net_set_sockaddr(&server,
185                                local_ip_addr ? local_ip_addr[i] : ipany,
186                                port))
187       goto err;
188
189     /* Create the socket */
190     sock = socket(server.sin.sin_family, SOCK_STREAM, 0);
191     if (sock < 0) {
192       silc_set_errno_posix(errno);
193       SILC_LOG_ERROR(("Cannot create socket: %s",
194                       silc_errno_string(silc_errno)));
195       goto err;
196     }
197
198     /* Set the socket options */
199     rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
200     if (rval < 0) {
201       SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
202       close(sock);
203       goto err;
204     }
205
206     /* Bind the listener socket */
207     rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server));
208     if (rval < 0) {
209       silc_set_errno_posix(errno);
210       SILC_LOG_ERROR(("Cannot bind socket: %s",
211                       silc_errno_string(silc_errno)));
212       close(sock);
213       goto err;
214     }
215
216     /* Specify that we are listenning */
217     rval = listen(sock, 64);
218     if (rval < 0) {
219       silc_set_errno_posix(errno);
220       SILC_LOG_ERROR(("Cannot set socket listenning: %s",
221                       silc_errno_string(silc_errno)));
222       close(sock);
223       goto err;
224     }
225
226     /* Set the server socket to non-blocking mode */
227     silc_net_set_socket_nonblock(sock);
228
229     /* Schedule for incoming connections */
230     silc_schedule_task_add_fd(schedule, sock, silc_net_accept, listener);
231
232     SILC_LOG_DEBUG(("TCP listener created, fd=%d", sock));
233     listener->socks[i] = sock;
234     listener->socks_count++;
235   }
236
237   return listener;
238
239  err:
240   if (listener)
241     silc_net_close_listener(listener);
242   return NULL;
243 }
244
245 /* Create TCP listener, multiple ports */
246
247 SilcNetListener
248 silc_net_tcp_create_listener2(const char *local_ip_addr, int *ports,
249                               SilcUInt32 port_count,
250                               SilcBool ignore_port_error,
251                               SilcBool lookup, SilcBool require_fqdn,
252                               SilcSchedule schedule,
253                               SilcNetCallback callback, void *context)
254 {
255   SilcNetListener listener = NULL;
256   SilcSockaddr server;
257   int i, sock, rval;
258   const char *ipany = "0.0.0.0";
259
260   SILC_LOG_DEBUG(("Creating TCP listener"));
261
262   if (!schedule) {
263     schedule = silc_schedule_get_global();
264     if (!schedule) {
265       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
266       goto err;
267     }
268   }
269
270   if (!callback) {
271     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
272     goto err;
273   }
274
275   listener = silc_calloc(1, sizeof(*listener));
276   if (!listener)
277     return NULL;
278   listener->schedule = schedule;
279   listener->callback = callback;
280   listener->context = context;
281   listener->require_fqdn = require_fqdn;
282   listener->lookup = lookup;
283
284   if (port_count > 0) {
285     listener->socks = silc_calloc(port_count, sizeof(*listener->socks));
286     if (!listener->socks)
287       return NULL;
288   } else {
289     listener->socks = silc_calloc(1, sizeof(*listener->socks));
290     if (!listener->socks)
291       return NULL;
292
293     port_count = 1;
294   }
295
296   /* Bind to ports */
297   for (i = 0; i < port_count; i++) {
298     SILC_LOG_DEBUG(("Binding to local address %s:%d",
299                     local_ip_addr ? local_ip_addr : ipany,
300                     ports ? ports[i] : 0));
301
302     /* Set sockaddr for server */
303     if (!silc_net_set_sockaddr(&server,
304                                local_ip_addr ? local_ip_addr : ipany,
305                                ports ? ports[i] : 0)) {
306       if (ignore_port_error)
307         continue;
308       goto err;
309     }
310
311     /* Create the socket */
312     sock = socket(server.sin.sin_family, SOCK_STREAM, 0);
313     if (sock < 0) {
314       if (ignore_port_error)
315         continue;
316       silc_set_errno_posix(errno);
317       SILC_LOG_ERROR(("Cannot create socket: %s",
318                       silc_errno_string(silc_errno)));
319       goto err;
320     }
321
322     /* Set the socket options */
323     rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
324     if (rval < 0) {
325       close(sock);
326       if (ignore_port_error)
327         continue;
328       SILC_LOG_ERROR(("Cannot set socket options: %s",
329                       silc_errno_string(silc_errno)));
330       goto err;
331     }
332
333     /* Bind the listener socket */
334     rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server));
335     if (rval < 0) {
336       close(sock);
337       if (ignore_port_error)
338         continue;
339       silc_set_errno_posix(errno);
340       SILC_LOG_ERROR(("Cannot bind socket: %s",
341                       silc_errno_string(silc_errno)));
342       goto err;
343     }
344
345     /* Specify that we are listenning */
346     rval = listen(sock, 64);
347     if (rval < 0) {
348       close(sock);
349       if (ignore_port_error)
350         continue;
351       silc_set_errno_posix(errno);
352       SILC_LOG_ERROR(("Cannot set socket listenning: %s",
353                       silc_errno_string(silc_errno)));
354       goto err;
355     }
356
357     /* Set the server socket to non-blocking mode */
358     silc_net_set_socket_nonblock(sock);
359
360     /* Schedule for incoming connections */
361     silc_schedule_task_add_fd(schedule, sock, silc_net_accept, listener);
362
363     SILC_LOG_DEBUG(("TCP listener created, fd=%d", sock));
364     listener->socks[i] = sock;
365     listener->socks_count++;
366   }
367
368   if (ignore_port_error && !listener->socks_count)
369     goto err;
370
371   return listener;
372
373  err:
374   if (listener)
375     silc_net_close_listener(listener);
376   return NULL;
377 }
378
379 /* Close network listener */
380
381 void silc_net_close_listener(SilcNetListener listener)
382 {
383   int i;
384
385   SILC_LOG_DEBUG(("Closing network listener"));
386
387   for (i = 0; i < listener->socks_count; i++) {
388     silc_schedule_task_del_by_fd(listener->schedule, listener->socks[i]);
389     shutdown(listener->socks[i], 2);
390     close(listener->socks[i]);
391   }
392
393   silc_free(listener->socks);
394   silc_free(listener);
395 }
396
397 /******************************* UDP Stream *********************************/
398
399 /* Create UDP stream */
400
401 SilcStream
402 silc_net_udp_connect(const char *local_ip_addr, int local_port,
403                      const char *remote_ip_addr, int remote_port,
404                      SilcSchedule schedule)
405 {
406   SilcStream stream;
407   SilcSockaddr server;
408   int sock = -1, rval;
409   const char *ipany = "0.0.0.0";
410
411   SILC_LOG_DEBUG(("Creating UDP stream"));
412
413   if (!schedule) {
414     schedule = silc_schedule_get_global();
415     if (!schedule) {
416       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
417       goto err;
418     }
419   }
420
421   /* Bind to local addresses */
422   SILC_LOG_DEBUG(("Binding to local address %s",
423                   local_ip_addr ? local_ip_addr : ipany));
424
425   /* Set sockaddr for server */
426   if (!silc_net_set_sockaddr(&server, local_ip_addr ? local_ip_addr : ipany,
427                              local_port))
428     goto err;
429
430   /* Create the socket */
431   sock = socket(server.sin.sin_family, SOCK_DGRAM, 0);
432   if (sock < 0) {
433     silc_set_errno_posix(errno);
434     SILC_LOG_ERROR(("Cannot create socket: %s",
435                     silc_errno_string(silc_errno)));
436     goto err;
437   }
438
439   /* Set the socket options */
440   rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
441   if (rval < 0) {
442     SILC_LOG_ERROR(("Cannot set socket options: %s",
443                     silc_errno_string(silc_errno)));
444     goto err;
445   }
446 #ifdef SO_REUSEPORT
447   rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEPORT, 1);
448   if (rval < 0) {
449     SILC_LOG_ERROR(("Cannot set socket options: %s",
450                     silc_errno_string(silc_errno)));
451     goto err;
452   }
453 #endif /* SO_REUSEPORT */
454
455   /* Bind the listener socket */
456   rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server));
457   if (rval < 0) {
458     silc_set_errno_posix(errno);
459     SILC_LOG_DEBUG(("Cannot bind socket: %s", silc_errno_string(silc_errno)));
460     goto err;
461   }
462
463   /* Set to connected state if remote address is provided. */
464   if (remote_ip_addr && remote_port) {
465     if (!silc_net_set_sockaddr(&server, remote_ip_addr, remote_port))
466       goto err;
467
468     rval = connect(sock, &server.sa, SIZEOF_SOCKADDR(server));
469     if (rval < 0) {
470       silc_set_errno_posix(errno);
471       SILC_LOG_DEBUG(("Cannot connect UDP stream: %s",
472                       silc_errno_string(silc_errno)));
473       goto err;
474     }
475   }
476
477   /* Set send and receive buffer size */
478 #ifdef SO_SNDBUF
479   rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_SNDBUF, 765535);
480   if (rval < 0) {
481     rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_SNDBUF, 65535);
482     if (rval < 0) {
483       SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
484       goto err;
485     }
486   }
487 #endif /* SO_SNDBUF */
488 #ifdef SO_RCVBUF
489   rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_RCVBUF, 765535);
490   if (rval < 0) {
491     rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_RCVBUF, 65535);
492     if (rval < 0) {
493       SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
494       goto err;
495     }
496   }
497 #endif /* SO_RCVBUF */
498
499   /* Encapsulate into socket stream */
500   stream =
501     silc_socket_udp_stream_create(sock, local_ip_addr ?
502                                   silc_net_is_ip6(local_ip_addr) : FALSE,
503                                   remote_ip_addr ? TRUE : FALSE, schedule);
504   if (!stream)
505     goto err;
506
507   SILC_LOG_DEBUG(("UDP stream created, fd=%d", sock));
508   return stream;
509
510  err:
511   if (sock != -1)
512     close(sock);
513   return NULL;
514 }
515
516 /* Receive UDP packet */
517
518 int silc_net_udp_receive(SilcStream stream, char *remote_ip_addr,
519                          SilcUInt32 remote_ip_addr_size, int *remote_port,
520                          unsigned char *ret_data, SilcUInt32 data_size)
521 {
522   SilcSocketStream sock = stream;
523   SilcSockaddr s;
524   struct sockaddr *from;
525   int len;
526   unsigned int flen;
527
528   SILC_LOG_DEBUG(("Reading data from UDP socket %d", sock->sock));
529
530   if (remote_ip_addr && remote_port) {
531     if (sock->ipv6) {
532 #ifdef HAVE_IPV6
533       from = (struct sockaddr *)&s.sin6;
534       flen = sizeof(s.sin6);
535 #endif /* HAVE_IPV6 */
536     } else {
537       from = (struct sockaddr *)&s.sin;
538       flen = sizeof(s.sin);
539     }
540     len = recvfrom(sock->sock, ret_data, data_size, 0, from, &flen);
541   } else
542     len = recv(sock->sock, ret_data, data_size, 0);
543
544   if (len < 0) {
545     silc_set_errno_posix(errno);
546     if (errno == EAGAIN || errno == EINTR) {
547       SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
548       silc_schedule_set_listen_fd(sock->schedule, sock->sock,
549                                   SILC_TASK_READ, FALSE);
550       return -1;
551     }
552     SILC_LOG_DEBUG(("Cannot read from UDP socket: %d:%s",
553                     sock->sock, strerror(errno)));
554     silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
555     return -2;
556   }
557
558   SILC_LOG_DEBUG(("Read %d bytes", len));
559
560   if (!len)
561     silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
562
563   /* Return remote address */
564   if (remote_ip_addr && remote_port) {
565     if (sock->ipv6) {
566 #ifdef HAVE_IPV6
567       *remote_port = ntohs(s.sin6.sin6_port);
568       inet_ntop(AF_INET6, &s.sin6.sin6_addr, remote_ip_addr,
569                 remote_ip_addr_size);
570 #else
571       *remote_port = 0;
572 #endif /* HAVE_IPV6 */
573     } else {
574       *remote_port = ntohs(s.sin.sin_port);
575       inet_ntop(AF_INET, &s.sin.sin_addr, remote_ip_addr,
576                 remote_ip_addr_size);
577     }
578
579     SILC_LOG_DEBUG(("UDP packet from %s:%d", remote_ip_addr, *remote_port));
580   }
581
582   return len;
583 }
584
585 /* Send UDP packet */
586
587 int silc_net_udp_send(SilcStream stream,
588                       const char *remote_ip_addr, int remote_port,
589                       const unsigned char *data, SilcUInt32 data_len)
590 {
591   SilcSocketStream sock = stream;
592   SilcSockaddr remote;
593   int ret;
594
595   SILC_LOG_DEBUG(("Sending data to UDP socket %d", sock->sock));
596
597   /* Set sockaddr */
598   if (!silc_net_set_sockaddr(&remote, remote_ip_addr, remote_port))
599     return -2;
600
601   /* Send */
602   ret = sendto(sock->sock, data, data_len, 0, &remote.sa,
603                SIZEOF_SOCKADDR(remote));
604   if (ret < 0) {
605     silc_set_errno_posix(errno);
606     if (errno == EAGAIN || errno == EINTR) {
607       SILC_LOG_DEBUG(("Could not send immediately, will do it later"));
608       silc_schedule_set_listen_fd(sock->schedule, sock->sock,
609                                   SILC_TASK_READ | SILC_TASK_WRITE, FALSE);
610       return -1;
611     }
612     SILC_LOG_DEBUG(("Cannot send to UDP socket: %s", strerror(errno)));
613     silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
614     return -2;
615   }
616
617   SILC_LOG_DEBUG(("Sent data %d bytes", ret));
618   if (silc_schedule_get_fd_events(sock->schedule, sock->sock) &
619       SILC_TASK_WRITE)
620     silc_schedule_set_listen_fd(sock->schedule, sock->sock,
621                                 SILC_TASK_READ, FALSE);
622
623   return ret;
624 }
625
626 /******************************* TCP Stream *********************************/
627
628 /* Asynchronous TCP/IP connecting */
629
630 typedef struct {
631   SilcResult status;
632   SilcStream stream;
633   SilcFSMStruct fsm;
634   SilcFSMEventStruct event;
635   SilcAsyncOperation op;
636   SilcAsyncOperation sop;
637   char *local_ip;
638   char *remote;
639   char ip_addr[64];
640   int sock;
641   SilcNetCallback callback;
642   void *context;
643   unsigned int port     : 24;
644   unsigned int retry    : 7;
645   unsigned int aborted  : 1;
646 } *SilcNetConnect;
647
648 SILC_FSM_STATE(silc_net_connect_st_start);
649 SILC_FSM_STATE(silc_net_connect_st_connected);
650 SILC_FSM_STATE(silc_net_connect_st_stream);
651 SILC_FSM_STATE(silc_net_connect_st_finish);
652
653 SILC_TASK_CALLBACK(silc_net_connect_wait)
654 {
655   SilcNetConnect conn = context;
656   SILC_FSM_EVENT_SIGNAL(&conn->event);
657 }
658
659 SILC_FSM_STATE(silc_net_connect_st_start)
660 {
661   SilcNetConnect conn = fsm_context;
662   int sock, rval;
663   SilcSockaddr desthost;
664   SilcBool prefer_ipv6 = TRUE;
665
666   if (conn->aborted) {
667     /** Aborted */
668     silc_fsm_next(fsm, silc_net_connect_st_finish);
669     return SILC_FSM_CONTINUE;
670   }
671
672   /* Do host lookup */
673  retry:
674   if (!silc_net_gethostbyname(conn->remote, prefer_ipv6,
675                               conn->ip_addr, sizeof(conn->ip_addr))) {
676     SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
677                     "host", conn->remote));
678
679     /** Network unreachable */
680     conn->status = SILC_ERR_UNREACHABLE;
681     silc_fsm_next(fsm, silc_net_connect_st_finish);
682     return SILC_FSM_CONTINUE;
683   }
684
685   /* Set sockaddr for this connection */
686   if (!silc_net_set_sockaddr(&desthost, conn->ip_addr, conn->port)) {
687     /** Sockaddr failed */
688     silc_fsm_next(fsm, silc_net_connect_st_finish);
689     return SILC_FSM_CONTINUE;
690   }
691
692   /* Create the connection socket */
693   sock = socket(desthost.sin.sin_family, SOCK_STREAM, 0);
694   if (sock < 0) {
695     /* If address is IPv6, then fallback to IPv4 and see whether we can do
696        better with that on socket creation. */
697     if (prefer_ipv6 && silc_net_is_ip6(conn->ip_addr)) {
698       prefer_ipv6 = FALSE;
699       goto retry;
700     }
701
702     /** Cannot create socket */
703     silc_set_errno_posix(errno);
704     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
705     silc_fsm_next(fsm, silc_net_connect_st_finish);
706     return SILC_FSM_CONTINUE;
707   }
708
709   /* Bind to the local address if provided */
710   if (conn->local_ip) {
711     SilcSockaddr local;
712
713     /* Set sockaddr for local listener, and try to bind it. */
714     if (silc_net_set_sockaddr(&local, conn->local_ip, 0))
715       bind(sock, &local.sa, SIZEOF_SOCKADDR(local));
716   }
717
718   /* Set the socket to non-blocking mode */
719   silc_net_set_socket_nonblock(sock);
720
721   /* Connect to the host */
722   rval = connect(sock, &desthost.sa, SIZEOF_SOCKADDR(desthost));
723   if (rval < 0) {
724     if (errno != EINPROGRESS) {
725       silc_set_errno_posix(errno);
726       shutdown(sock, 2);
727       close(sock);
728
729       /* Retry using an IPv4 adress, if IPv6 didn't work */
730       if (prefer_ipv6 && silc_net_is_ip6(conn->ip_addr)) {
731         prefer_ipv6 = FALSE;
732         goto retry;
733       }
734
735       /** Cannot connect to remote host */
736       SILC_LOG_ERROR(("Cannot connect to remote host: %s",
737                       silc_errno_string(silc_errno)));
738       silc_fsm_next(fsm, silc_net_connect_st_finish);
739       return SILC_FSM_CONTINUE;
740     }
741   }
742
743   /* Set appropriate options */
744 #if defined(TCP_NODELAY)
745   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
746 #endif
747   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
748
749   SILC_LOG_DEBUG(("Connection operation in progress"));
750
751   conn->sock = sock;
752
753   /** Wait for connection */
754   silc_fsm_next(fsm, silc_net_connect_st_connected);
755   silc_fsm_event_init(&conn->event, fsm);
756   silc_schedule_task_add_fd(silc_fsm_get_schedule(fsm), sock,
757                             silc_net_connect_wait, conn);
758   silc_schedule_set_listen_fd(silc_fsm_get_schedule(fsm), sock,
759                               SILC_TASK_WRITE, FALSE);
760   SILC_FSM_EVENT_WAIT(&conn->event);
761   return SILC_FSM_CONTINUE;
762 }
763
764 static void silc_net_connect_wait_stream(SilcResult status,
765                                          SilcStream stream, void *context)
766 {
767   SilcNetConnect conn = context;
768   conn->sop = NULL;
769   conn->status = status;
770   conn->stream = stream;
771   SILC_FSM_CALL_CONTINUE(&conn->fsm);
772 }
773
774 SILC_FSM_STATE(silc_net_connect_st_connected)
775 {
776   SilcNetConnect conn = fsm_context;
777   SilcSchedule schedule = silc_fsm_get_schedule(fsm);
778   int opt = EINVAL, optlen = sizeof(opt), ret;
779
780   if (conn->aborted) {
781     /** Aborted */
782     silc_schedule_unset_listen_fd(schedule, conn->sock);
783     silc_schedule_task_del_by_fd(schedule, conn->sock);
784     silc_fsm_next(fsm, silc_net_connect_st_finish);
785     return SILC_FSM_CONTINUE;
786   }
787
788   ret = silc_net_get_socket_opt(conn->sock, SOL_SOCKET, SO_ERROR,
789                                 &opt, &optlen);
790
791   silc_schedule_unset_listen_fd(schedule, conn->sock);
792   silc_schedule_task_del_by_fd(schedule, conn->sock);
793
794   if (ret != 0 || opt != 0) {
795     if (conn->retry) {
796       /** Retry connecting */
797       SILC_LOG_DEBUG(("Retry connecting"));
798       conn->retry--;
799       silc_net_close_connection(conn->sock);
800       silc_fsm_next(fsm, silc_net_connect_st_start);
801       return SILC_FSM_CONTINUE;
802     }
803
804     /* Set error */
805     silc_set_errno_posix(opt);
806     conn->status = silc_errno;
807
808     /** Connecting failed */
809     SILC_LOG_DEBUG(("Connecting failed, error %s",
810                     silc_errno_string(silc_errno)));
811     silc_fsm_next(fsm, silc_net_connect_st_finish);
812     return SILC_FSM_CONTINUE;
813   }
814
815   SILC_LOG_DEBUG(("TCP connection established"));
816
817   /** Connection created */
818   silc_fsm_next(fsm, silc_net_connect_st_stream);
819   SILC_FSM_CALL((conn->sop = silc_socket_tcp_stream_create(
820                                      conn->sock, TRUE, FALSE, schedule,
821                                      silc_net_connect_wait_stream, conn)));
822 }
823
824 SILC_FSM_STATE(silc_net_connect_st_stream)
825 {
826   SilcNetConnect conn = fsm_context;
827
828   if (conn->aborted) {
829     /** Aborted */
830     silc_fsm_next(fsm, silc_net_connect_st_finish);
831     return SILC_FSM_CONTINUE;
832   }
833
834   if (conn->status != SILC_OK) {
835     /** Stream creation failed */
836     silc_fsm_next(fsm, silc_net_connect_st_finish);
837     return SILC_FSM_CONTINUE;
838   }
839
840   /** Stream created successfully */
841   SILC_LOG_DEBUG(("Connected successfully, sock %d", conn->sock));
842   conn->status = SILC_OK;
843   silc_fsm_next(fsm, silc_net_connect_st_finish);
844   return SILC_FSM_CONTINUE;
845 }
846
847 SILC_FSM_STATE(silc_net_connect_st_finish)
848 {
849   SilcNetConnect conn = fsm_context;
850
851   /* Deliver error or new stream */
852   if (!conn->aborted) {
853     conn->callback(conn->status, conn->stream, conn->context);
854     if (conn->op)
855       silc_async_free(conn->op);
856   }
857
858   if (conn->sock && conn->status != SILC_NET_OK)
859     silc_net_close_connection(conn->sock);
860
861   return SILC_FSM_FINISH;
862 }
863
864 static void silc_net_connect_abort(SilcAsyncOperation op, void *context)
865 {
866   SilcNetConnect conn = context;
867   conn->aborted = TRUE;
868
869   /* Abort underlaying stream creation too */
870   if (conn->sop) {
871     silc_async_abort(conn->sop, NULL, NULL);
872     conn->sop = NULL;
873   }
874 }
875
876 static void silc_net_connect_destructor(SilcFSM fsm, void *fsm_context,
877                                         void *destructor_context)
878 {
879   SilcNetConnect conn = fsm_context;
880   silc_free(conn->local_ip);
881   silc_free(conn->remote);
882   silc_free(conn);
883 }
884
885 /* Create asynchronous TCP/IP connection. */
886
887 SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
888                                         const char *remote_ip_addr,
889                                         int remote_port,
890                                         SilcSchedule schedule,
891                                         SilcNetCallback callback,
892                                         void *context)
893 {
894   SilcNetConnect conn;
895
896   if (!schedule) {
897     schedule = silc_schedule_get_global();
898     if (!schedule) {
899       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
900       return NULL;
901     }
902   }
903
904   if (!remote_ip_addr || remote_port < 1 || !callback) {
905     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
906     return NULL;
907   }
908
909   SILC_LOG_DEBUG(("Creating connection to host %s port %d",
910                   remote_ip_addr, remote_port));
911
912   conn = silc_calloc(1, sizeof(*conn));
913   if (!conn) {
914     callback(silc_errno, NULL, context);
915     return NULL;
916   }
917
918   /* Start async operation */
919   conn->op = silc_async_alloc(silc_net_connect_abort, NULL, conn);
920   if (!conn->op) {
921     silc_free(conn);
922     callback(silc_errno, NULL, context);
923     return NULL;
924   }
925
926   if (local_ip_addr)
927     conn->local_ip = silc_strdup(local_ip_addr);
928   conn->remote = silc_strdup(remote_ip_addr);
929   if (!conn->remote) {
930     silc_async_free(conn->op);
931     silc_free(conn->local_ip);
932     silc_free(conn);
933     callback(silc_errno, NULL, context);
934     return NULL;
935   }
936   conn->port = remote_port;
937   conn->callback = callback;
938   conn->context = context;
939   conn->retry = 1;
940   conn->status = SILC_ERR;
941
942   silc_fsm_init(&conn->fsm, conn, silc_net_connect_destructor, NULL, schedule);
943   silc_fsm_start(&conn->fsm, silc_net_connect_st_start);
944
945   return conn->op;
946 }
947
948 /* Closes the connection by closing the socket connection. */
949
950 void silc_net_close_connection(int sock)
951 {
952   SILC_LOG_DEBUG(("Closing sock %d", sock));
953   close(sock);
954 }
955
956 /* Set's the socket to non-blocking mode. */
957
958 int silc_net_set_socket_nonblock(SilcSocket sock)
959 {
960   int ret = fcntl((int)sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
961   if (ret)
962     silc_set_errno_posix(errno);
963   return ret;
964 }
965
966 /* Converts the IP number string from numbers-and-dots notation to
967    binary form. */
968
969 SilcBool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len)
970 {
971   int ret = 0;
972
973   if (silc_net_is_ip4(addr)) {
974     /* IPv4 address */
975     struct in_addr tmp;
976
977     if (bin_len < 4) {
978       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
979       return FALSE;
980     }
981
982     ret = inet_aton(addr, &tmp);
983     if (!ret) {
984       silc_set_errno_posix(errno);
985       return FALSE;
986     }
987
988     memcpy(bin, (unsigned char *)&tmp.s_addr, 4);
989 #ifdef HAVE_IPV6
990   } else {
991     struct addrinfo hints, *ai;
992     SilcSockaddr *s;
993
994     /* IPv6 address */
995     if (bin_len < 16) {
996       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
997       return FALSE;
998     }
999
1000     memset(&hints, 0, sizeof(hints));
1001     hints.ai_family = AF_INET6;
1002     if (getaddrinfo(addr, NULL, &hints, &ai))
1003       return FALSE;
1004
1005     if (ai) {
1006       s = (SilcSockaddr *)ai->ai_addr;
1007       memcpy(bin, &s->sin6.sin6_addr, sizeof(s->sin6.sin6_addr));
1008       freeaddrinfo(ai);
1009     }
1010
1011     ret = TRUE;
1012 #endif /* HAVE_IPV6 */
1013   }
1014
1015   return ret != 0;
1016 }