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