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