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