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