Added SILC Server library.
[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 - 2005 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 #ifdef HAVE_IPV6
25 #define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ?    \
26   sizeof(so.sin6) : sizeof(so.sin))
27 #else
28 #define SIZEOF_SOCKADDR(so) (sizeof(so.sin))
29 #endif
30
31 typedef union {
32   struct sockaddr sa;
33   struct sockaddr_in sin;
34 #ifdef HAVE_IPV6
35   struct sockaddr_in6 sin6;
36 #endif
37 } SilcSockaddr;
38
39 static SilcBool silc_net_set_sockaddr(SilcSockaddr *addr, const char *ip_addr,
40                                   int port)
41 {
42   int len;
43
44   memset(addr, 0, sizeof(*addr));
45
46   /* Check for IPv4 and IPv6 addresses */
47   if (ip_addr) {
48     if (!silc_net_is_ip(ip_addr)) {
49       SILC_LOG_ERROR(("%s is not IP address", ip_addr));
50       return FALSE;
51     }
52
53     if (silc_net_is_ip4(ip_addr)) {
54       /* IPv4 address */
55       len = sizeof(addr->sin.sin_addr);
56       silc_net_addr2bin(ip_addr,
57                         (unsigned char *)&addr->sin.sin_addr.s_addr, len);
58       addr->sin.sin_family = AF_INET;
59       addr->sin.sin_port = port ? htons(port) : 0;
60     } else {
61 #ifdef HAVE_IPV6
62       /* IPv6 address */
63       len = sizeof(addr->sin6.sin6_addr);
64       silc_net_addr2bin(ip_addr,
65                         (unsigned char *)&addr->sin6.sin6_addr, len);
66       addr->sin6.sin6_family = AF_INET6;
67       addr->sin6.sin6_port = port ? htons(port) : 0;
68 #else
69       SILC_LOG_ERROR(("IPv6 support is not compiled in"));
70       return FALSE;
71 #endif
72     }
73   } else {
74     /* Any address */
75     addr->sin.sin_family = AF_INET;
76     addr->sin.sin_addr.s_addr = INADDR_ANY;
77     if (port)
78       addr->sin.sin_port = htons(port);
79   }
80
81   return TRUE;
82 }
83
84 /* Deliver new stream to upper layer */
85
86 static void silc_net_accept_stream(SilcSocketStreamStatus status,
87                                    SilcStream stream, void *context)
88 {
89   SilcNetServer server = context;
90
91   if (status != SILC_SOCKET_OK)
92     return;
93
94   server->callback(SILC_NET_OK, stream, server->context);
95 }
96
97 /* Accept incoming connection and notify upper layer */
98
99 SILC_TASK_CALLBACK(silc_net_accept)
100 {
101   SilcNetServer server = context;
102   int sock;
103
104   SILC_LOG_DEBUG(("Accepting new connection"));
105
106   sock = silc_net_accept_connection(fd);
107   if (sock < 0)
108     return;
109
110   /* Set socket options */
111   silc_net_set_socket_nonblock(sock);
112   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
113
114   /* Create socket stream */
115   silc_socket_stream_create(sock, TRUE, server->require_fqdn, schedule,
116                             silc_net_accept_stream, server);
117 }
118
119 /* Create network listener */
120
121 SilcNetServer
122 silc_net_create_server(const char **local_ip_addr, SilcUInt32 local_ip_count,
123                        int port, SilcBool require_fqdn, SilcSchedule schedule,
124                        SilcNetCallback callback, void *context)
125 {
126   SilcNetServer netserver = NULL;
127   SilcSockaddr server;
128   int i, sock, rval;
129   const char *ipany = "0.0.0.0";
130
131   SILC_LOG_DEBUG(("Creating new network listener"));
132
133   if (port < 1 || !schedule || !callback)
134     goto err;
135
136   netserver = silc_calloc(1, sizeof(*netserver));
137   if (!netserver) {
138     callback(SILC_NET_NO_MEMORY, NULL, context);
139     return NULL;
140   }
141   netserver->schedule = schedule;
142   netserver->callback = callback;
143   netserver->context = context;
144
145   if (local_ip_count > 0) {
146     netserver->socks = silc_calloc(local_ip_count, sizeof(*netserver->socks));
147     if (!netserver->socks) {
148       callback(SILC_NET_NO_MEMORY, NULL, context);
149       return NULL;
150     }
151   } else {
152     netserver->socks = silc_calloc(1, sizeof(*netserver->socks));
153     if (!netserver->socks) {
154       callback(SILC_NET_NO_MEMORY, NULL, context);
155       return NULL;
156     }
157
158     local_ip_count = 1;
159   }
160
161   /* Bind to local addresses */
162   for (i = 0; i < local_ip_count; i++) {
163     SILC_LOG_DEBUG(("Binding to local address %s",
164                     local_ip_addr ? local_ip_addr[i] : ipany));
165
166     /* Set sockaddr for server */
167     if (!silc_net_set_sockaddr(&server,
168                                local_ip_addr ? local_ip_addr[i] : ipany,
169                                port))
170       goto err;
171
172     /* Create the socket */
173     sock = socket(server.sin.sin_family, SOCK_STREAM, 0);
174     if (sock < 0) {
175       SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
176       goto err;
177     }
178
179     /* Set the socket options */
180     rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
181     if (rval < 0) {
182       SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
183       goto err;
184     }
185
186     /* Bind the server socket */
187     rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server));
188     if (rval < 0) {
189       SILC_LOG_DEBUG(("Cannot bind socket: %s", strerror(errno)));
190       goto err;
191     }
192
193     /* Specify that we are listenning */
194     rval = listen(sock, 5);
195     if (rval < 0) {
196       SILC_LOG_ERROR(("Cannot set socket listenning: %s", strerror(errno)));
197       goto err;
198     }
199
200     /* Set the server socket to non-blocking mode */
201     silc_net_set_socket_nonblock(sock);
202
203     /* Schedule for incoming connections */
204     silc_schedule_task_add_fd(schedule, sock, silc_net_accept, netserver);
205
206     SILC_LOG_DEBUG(("Network listener created, fd=%d", sock));
207     netserver->socks[i] = sock;
208     netserver->socks_count++;
209   }
210
211   return netserver;
212
213  err:
214   if (callback)
215     callback(SILC_NET_ERROR, NULL, context);
216   if (netserver)
217     silc_net_close_server(netserver);
218   return NULL;
219 }
220
221 /* Close network listener */
222
223 void silc_net_close_server(SilcNetServer server)
224 {
225   int i;
226
227   SILC_LOG_DEBUG(("Closing network listener"));
228
229   for (i = 0; i < server->socks_count; i++) {
230     silc_schedule_task_del_by_fd(server->schedule, server->socks[i]);
231     shutdown(server->socks[i], 2);
232     close(server->socks[i]);
233   }
234
235   silc_free(server->socks);
236   silc_free(server);
237 }
238
239 /* Asynchronous TCP/IP connecting */
240
241 typedef struct {
242   SilcNetStatus status;
243   SilcSocketStreamStatus stream_status;
244   SilcStream stream;
245   SilcFSMStruct fsm;
246   SilcFSMSemaStruct sema;
247   SilcAsyncOperation op;
248   SilcAsyncOperation sop;
249   char *local_ip;
250   char *remote;
251   char ip_addr[64];
252   int sock;
253   SilcNetCallback callback;
254   void *context;
255   unsigned int port     : 24;
256   unsigned int retry    : 7;
257   unsigned int aborted  : 1;
258 } *SilcNetConnect;
259
260 SILC_FSM_STATE(silc_net_connect_st_start);
261 SILC_FSM_STATE(silc_net_connect_st_connected);
262 SILC_FSM_STATE(silc_net_connect_st_stream);
263 SILC_FSM_STATE(silc_net_connect_st_finish);
264
265 SILC_TASK_CALLBACK(silc_net_connect_wait)
266 {
267   SilcNetConnect conn = context;
268   SILC_FSM_SEMA_POST(&conn->sema);
269 }
270
271 SILC_FSM_STATE(silc_net_connect_st_start)
272 {
273   SilcNetConnect conn = fsm_context;
274   int sock, rval;
275   SilcSockaddr desthost;
276   SilcBool prefer_ipv6 = TRUE;
277
278   if (conn->aborted) {
279     /** Aborted */
280     silc_fsm_next(fsm, silc_net_connect_st_finish);
281     return SILC_FSM_CONTINUE;
282   }
283
284   /* Do host lookup */
285  retry:
286   if (!silc_net_gethostbyname(conn->remote, prefer_ipv6,
287                               conn->ip_addr, sizeof(conn->ip_addr))) {
288     SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
289                     "host", conn->remote));
290
291     /** Network unreachable */
292     conn->status = SILC_NET_HOST_UNREACHABLE;
293     silc_fsm_next(fsm, silc_net_connect_st_finish);
294     return SILC_FSM_CONTINUE;
295   }
296
297   /* Set sockaddr for this connection */
298   if (!silc_net_set_sockaddr(&desthost, conn->ip_addr, conn->port)) {
299     /** Sockaddr failed */
300     silc_fsm_next(fsm, silc_net_connect_st_finish);
301     return SILC_FSM_CONTINUE;
302   }
303
304   /* Create the connection socket */
305   sock = socket(desthost.sin.sin_family, SOCK_STREAM, 0);
306   if (sock < 0) {
307     /* If address is IPv6, then fallback to IPv4 and see whether we can do
308        better with that on socket creation. */
309     if (prefer_ipv6 && silc_net_is_ip6(conn->ip_addr)) {
310       prefer_ipv6 = FALSE;
311       goto retry;
312     }
313
314     /** Cannot create socket */
315     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
316     silc_fsm_next(fsm, silc_net_connect_st_finish);
317     return SILC_FSM_CONTINUE;
318   }
319
320   /* Bind to the local address if provided */
321   if (conn->local_ip) {
322     SilcSockaddr local;
323
324     /* Set sockaddr for local listener, and try to bind it. */
325     if (silc_net_set_sockaddr(&local, conn->local_ip, 0))
326       bind(sock, &local.sa, SIZEOF_SOCKADDR(local));
327   }
328
329   /* Set the socket to non-blocking mode */
330   silc_net_set_socket_nonblock(sock);
331
332   /* Connect to the host */
333   rval = connect(sock, &desthost.sa, SIZEOF_SOCKADDR(desthost));
334   if (rval < 0) {
335     if (errno != EINPROGRESS) {
336       /* retry using an IPv4 adress, if IPv6 didn't work */
337       if (prefer_ipv6 && silc_net_is_ip6(conn->ip_addr)) {
338         shutdown(sock, 2);
339         close(sock);
340
341         prefer_ipv6 = FALSE;
342         goto retry;
343       }
344
345       shutdown(sock, 2);
346       close(sock);
347
348       /** Cannot connect to remote host */
349       SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
350       silc_fsm_next(fsm, silc_net_connect_st_finish);
351       return SILC_FSM_CONTINUE;
352     }
353   }
354
355   /* Set appropriate options */
356 #if defined(TCP_NODELAY)
357   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
358 #endif
359   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
360
361   SILC_LOG_DEBUG(("Connection operation in progress"));
362
363   conn->sock = sock;
364
365   /** Wait for connection */
366   silc_fsm_next(fsm, silc_net_connect_st_connected);
367   silc_fsm_sema_init(&conn->sema, fsm, 0);
368   silc_schedule_task_add_fd(silc_fsm_get_schedule(fsm), sock,
369                             silc_net_connect_wait, conn);
370   silc_schedule_set_listen_fd(silc_fsm_get_schedule(fsm), sock,
371                               SILC_TASK_WRITE, FALSE);
372   SILC_FSM_SEMA_WAIT(&conn->sema);
373   return SILC_FSM_CONTINUE;
374 }
375
376 static void silc_net_connect_wait_stream(SilcSocketStreamStatus status,
377                                          SilcStream stream, void *context)
378 {
379   SilcNetConnect conn = context;
380   conn->stream_status = status;
381   conn->stream = stream;
382   SILC_FSM_CALL_CONTINUE(&conn->fsm);
383 }
384
385 SILC_FSM_STATE(silc_net_connect_st_connected)
386 {
387   SilcNetConnect conn = fsm_context;
388   SilcSchedule schedule = silc_fsm_get_schedule(fsm);
389   int opt = EINVAL, optlen = sizeof(opt), ret;
390
391   if (conn->aborted) {
392     /** Aborted */
393     silc_fsm_next(fsm, silc_net_connect_st_finish);
394     return SILC_FSM_CONTINUE;
395   }
396
397   ret = silc_net_get_socket_opt(conn->sock, SOL_SOCKET, SO_ERROR,
398                                 &opt, &optlen);
399
400   silc_schedule_task_del_by_fd(schedule, conn->sock);
401   silc_schedule_unset_listen_fd(schedule, conn->sock);
402
403   if (ret != 0 || opt != 0) {
404     if (conn->retry) {
405       /** Retry connecting */
406       SILC_LOG_DEBUG(("Retry connecting"));
407       conn->retry--;
408       silc_net_close_connection(conn->sock);
409       silc_fsm_next(fsm, silc_net_connect_st_start);
410       return SILC_FSM_CONTINUE;
411     }
412
413 #if defined(ECONNREFUSED)
414     if (errno == ECONNREFUSED)
415       conn->status = SILC_NET_CONNECTION_REFUSED;
416 #endif /* ECONNREFUSED */
417 #if defined(ETIMEDOUT)
418     if (errno == ETIMEDOUT)
419       conn->status = SILC_NET_CONNECTION_TIMEOUT;
420 #endif /* ETIMEDOUT */
421 #if defined(ENETUNREACH)
422     if (errno == ENETUNREACH)
423       conn->status = SILC_NET_HOST_UNREACHABLE;
424 #endif /* ENETUNREACH */
425
426     /** Connecting failed */
427     SILC_LOG_DEBUG(("Connecting failed"));
428     silc_fsm_next(fsm, silc_net_connect_st_finish);
429     return SILC_FSM_CONTINUE;
430   }
431
432   /** Connection created */
433   silc_fsm_next(fsm, silc_net_connect_st_stream);
434   SILC_FSM_CALL((conn->sop = silc_socket_stream_create(
435                                      conn->sock, FALSE, FALSE,
436                                      schedule,
437                                      silc_net_connect_wait_stream, conn)));
438 }
439
440 SILC_FSM_STATE(silc_net_connect_st_stream)
441 {
442   SilcNetConnect conn = fsm_context;
443
444   if (conn->aborted) {
445     /** Aborted */
446     silc_fsm_next(fsm, silc_net_connect_st_finish);
447     return SILC_FSM_CONTINUE;
448   }
449
450   if (conn->stream_status != SILC_SOCKET_OK) {
451     /** Stream creation failed */
452     if (conn->stream_status == SILC_SOCKET_UNKNOWN_IP)
453       conn->status = SILC_NET_UNKNOWN_IP;
454     else if (conn->stream_status == SILC_SOCKET_UNKNOWN_HOST)
455       conn->status = SILC_NET_UNKNOWN_HOST;
456     else
457       conn->status = SILC_NET_ERROR;
458     silc_fsm_next(fsm, silc_net_connect_st_finish);
459     return SILC_FSM_CONTINUE;
460   }
461
462   /* Set stream information */
463   silc_socket_stream_set_info(conn->stream,
464                               !silc_net_is_ip(conn->remote) ? conn->remote :
465                               conn->ip_addr, conn->ip_addr, conn->port);
466
467   /** Stream created successfully */
468   SILC_LOG_DEBUG(("Connected successfully"));
469   conn->status = SILC_NET_OK;
470   silc_fsm_next(fsm, silc_net_connect_st_finish);
471   return SILC_FSM_CONTINUE;
472 }
473
474 SILC_FSM_STATE(silc_net_connect_st_finish)
475 {
476   SilcNetConnect conn = fsm_context;
477
478   /* Deliver error or new stream */
479   if (!conn->aborted) {
480     conn->callback(conn->status, conn->stream, conn->context);
481     if (conn->op)
482       silc_async_free(conn->op);
483     if (conn->sop)
484       silc_async_free(conn->sop);
485   }
486
487   return SILC_FSM_FINISH;
488 }
489
490 static void silc_net_connect_abort(SilcAsyncOperation op, void *context)
491 {
492   SilcNetConnect conn = context;
493   conn->aborted = TRUE;
494
495   /* Abort underlaying stream creation too */
496   if (conn->sop)
497     silc_async_abort(conn->op, NULL, NULL);
498 }
499
500 static void silc_net_connect_destructor(SilcFSM fsm, void *fsm_context,
501                                         void *destructor_context)
502 {
503   SilcNetConnect conn = fsm_context;
504   silc_free(conn->local_ip);
505   silc_free(conn->remote);
506   silc_free(conn);
507 }
508
509 /* Create asynchronous TCP/IP connection. */
510
511 SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
512                                         const char *remote_ip_addr,
513                                         int remote_port,
514                                         SilcSchedule schedule,
515                                         SilcNetCallback callback,
516                                         void *context)
517 {
518   SilcNetConnect conn;
519
520   if (!remote_ip_addr || remote_port < 1 || !schedule || !callback)
521     return NULL;
522
523   SILC_LOG_DEBUG(("Creating connection to host %s port %d",
524                   remote_ip_addr, remote_port));
525
526   conn = silc_calloc(1, sizeof(*conn));
527   if (!conn) {
528     callback(SILC_NET_NO_MEMORY, NULL, context);
529     return NULL;
530   }
531
532   /* Start async operation */
533   conn->op = silc_async_alloc(silc_net_connect_abort, NULL, conn);
534   if (!conn->op) {
535     callback(SILC_NET_NO_MEMORY, NULL, context);
536     return NULL;
537   }
538
539   if (local_ip_addr)
540     conn->local_ip = strdup(local_ip_addr);
541   conn->remote = strdup(remote_ip_addr);
542   if (!conn->remote) {
543     callback(SILC_NET_NO_MEMORY, NULL, context);
544     return NULL;
545   }
546   conn->port = remote_port;
547   conn->callback = callback;
548   conn->context = context;
549   conn->retry = 1;
550   conn->status = SILC_NET_ERROR;
551
552   silc_fsm_init(&conn->fsm, conn, silc_net_connect_destructor, NULL, schedule);
553   silc_fsm_start(&conn->fsm, silc_net_connect_st_start);
554
555   return conn->op;
556 }
557
558 /* Closes the connection by closing the socket connection. */
559
560 void silc_net_close_connection(int sock)
561 {
562   close(sock);
563 }
564
565 /* Set's the socket to non-blocking mode. */
566
567 int silc_net_set_socket_nonblock(int sock)
568 {
569   return fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
570 }
571
572 /* Converts the IP number string from numbers-and-dots notation to
573    binary form. */
574
575 SilcBool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len)
576 {
577   int ret = 0;
578
579   if (silc_net_is_ip4(addr)) {
580     /* IPv4 address */
581     struct in_addr tmp;
582     ret = inet_aton(addr, &tmp);
583     if (bin_len < 4)
584       return FALSE;
585
586     memcpy(bin, (unsigned char *)&tmp.s_addr, 4);
587 #ifdef HAVE_IPV6
588   } else {
589     struct addrinfo hints, *ai;
590     SilcSockaddr *s;
591
592     /* IPv6 address */
593     if (bin_len < 16)
594       return FALSE;
595
596     memset(&hints, 0, sizeof(hints));
597     hints.ai_family = AF_INET6;
598     if (getaddrinfo(addr, NULL, &hints, &ai))
599       return FALSE;
600
601     if (ai) {
602       s = (SilcSockaddr *)ai->ai_addr;
603       memcpy(bin, &s->sin6.sin6_addr, sizeof(s->sin6.sin6_addr));
604       freeaddrinfo(ai);
605     }
606
607     ret = TRUE;
608 #endif /* HAVE_IPV6 */
609   }
610
611   return ret != 0;
612 }