Merged silc_1_1_branch to trunk.
[silc.git] / lib / silcutil / unix / silcunixnet.c
index 805feb8700a1a706cba5cf5c548c148c29aadd3b..e0befdc19979ca325a83cd39b6da9015e34a28f8 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2006 Pekka Riikonen
+  Copyright (C) 1997 - 2007 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -21,6 +21,8 @@
 #include "silc.h"
 #include "silcnet.h"
 
+/************************** Types and definitions ***************************/
+
 #ifdef HAVE_IPV6
 #define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ?   \
   sizeof(so.sin6) : sizeof(so.sin))
@@ -36,6 +38,8 @@ typedef union {
 #endif
 } SilcSockaddr;
 
+/************************ Static utility functions **************************/
+
 static SilcBool silc_net_set_sockaddr(SilcSockaddr *addr, const char *ip_addr,
                                      int port)
 {
@@ -81,6 +85,8 @@ static SilcBool silc_net_set_sockaddr(SilcSockaddr *addr, const char *ip_addr,
   return TRUE;
 }
 
+/****************************** TCP Listener ********************************/
+
 /* Deliver new stream to upper layer */
 
 static void silc_net_accept_stream(SilcSocketStreamStatus status,
@@ -108,7 +114,6 @@ SILC_TASK_CALLBACK(silc_net_accept)
     return;
 
   /* Set socket options */
-  silc_net_set_socket_nonblock(sock);
   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
 
   /* Create socket stream */
@@ -137,10 +142,8 @@ silc_net_tcp_create_listener(const char **local_ip_addr,
     goto err;
 
   listener = silc_calloc(1, sizeof(*listener));
-  if (!listener) {
-    callback(SILC_NET_NO_MEMORY, NULL, context);
+  if (!listener)
     return NULL;
-  }
   listener->schedule = schedule;
   listener->callback = callback;
   listener->context = context;
@@ -149,24 +152,20 @@ silc_net_tcp_create_listener(const char **local_ip_addr,
 
   if (local_ip_count > 0) {
     listener->socks = silc_calloc(local_ip_count, sizeof(*listener->socks));
-    if (!listener->socks) {
-      callback(SILC_NET_NO_MEMORY, NULL, context);
+    if (!listener->socks)
       return NULL;
-    }
   } else {
     listener->socks = silc_calloc(1, sizeof(*listener->socks));
-    if (!listener->socks) {
-      callback(SILC_NET_NO_MEMORY, NULL, context);
+    if (!listener->socks)
       return NULL;
-    }
 
     local_ip_count = 1;
   }
 
   /* Bind to local addresses */
   for (i = 0; i < local_ip_count; i++) {
-    SILC_LOG_DEBUG(("Binding to local address %s",
-                   local_ip_addr ? local_ip_addr[i] : ipany));
+    SILC_LOG_DEBUG(("Binding to local address %s:%d",
+                   local_ip_addr ? local_ip_addr[i] : ipany, port));
 
     /* Set sockaddr for server */
     if (!silc_net_set_sockaddr(&server,
@@ -185,20 +184,23 @@ silc_net_tcp_create_listener(const char **local_ip_addr,
     rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
     if (rval < 0) {
       SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
+      close(sock);
       goto err;
     }
 
     /* Bind the listener socket */
     rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server));
     if (rval < 0) {
-      SILC_LOG_DEBUG(("Cannot bind socket: %s", strerror(errno)));
+      SILC_LOG_ERROR(("Cannot bind socket: %s", strerror(errno)));
+      close(sock);
       goto err;
     }
 
     /* Specify that we are listenning */
-    rval = listen(sock, 5);
+    rval = listen(sock, 64);
     if (rval < 0) {
       SILC_LOG_ERROR(("Cannot set socket listenning: %s", strerror(errno)));
+      close(sock);
       goto err;
     }
 
@@ -216,8 +218,6 @@ silc_net_tcp_create_listener(const char **local_ip_addr,
   return listener;
 
  err:
-  if (callback)
-    callback(SILC_NET_ERROR, NULL, context);
   if (listener)
     silc_net_close_listener(listener);
   return NULL;
@@ -241,6 +241,8 @@ void silc_net_close_listener(SilcNetListener listener)
   silc_free(listener);
 }
 
+/******************************* UDP Stream *********************************/
+
 /* Create UDP stream */
 
 SilcStream
@@ -295,9 +297,6 @@ silc_net_udp_connect(const char *local_ip_addr, int local_port,
     goto err;
   }
 
-  /* Set socket to non-blocking mode */
-  silc_net_set_socket_nonblock(sock);
-
   /* Set to connected state if remote address is provided. */
   if (remote_ip_addr && remote_port) {
     if (!silc_net_set_sockaddr(&server, remote_ip_addr, remote_port))
@@ -312,17 +311,23 @@ silc_net_udp_connect(const char *local_ip_addr, int local_port,
 
   /* Set send and receive buffer size */
 #ifdef SO_SNDBUF
-  rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_SNDBUF, 65535);
+  rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_SNDBUF, 765535);
   if (rval < 0) {
-    SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
-    goto err;
+    rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_SNDBUF, 65535);
+    if (rval < 0) {
+      SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
+      goto err;
+    }
   }
 #endif /* SO_SNDBUF */
 #ifdef SO_RCVBUF
-  rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_RCVBUF, 65535);
+  rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_RCVBUF, 765535);
   if (rval < 0) {
-    SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
-    goto err;
+    rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_RCVBUF, 65535);
+    if (rval < 0) {
+      SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
+      goto err;
+    }
   }
 #endif /* SO_RCVBUF */
 
@@ -399,7 +404,7 @@ int silc_net_udp_receive(SilcStream stream, char *remote_ip_addr,
                remote_ip_addr_size);
     }
 
-    SILC_LOG_DEBUG(("UDP packet from %s:%d", remote_ip_addr, remote_port));
+    SILC_LOG_DEBUG(("UDP packet from %s:%d", remote_ip_addr, *remote_port));
   }
 
   return len;
@@ -438,12 +443,16 @@ int silc_net_udp_send(SilcStream stream,
   }
 
   SILC_LOG_DEBUG(("Sent data %d bytes", ret));
-  silc_schedule_set_listen_fd(sock->schedule, sock->sock,
-                             SILC_TASK_READ, FALSE);
+  if (silc_schedule_get_fd_events(sock->schedule, sock->sock) &
+      SILC_TASK_WRITE)
+    silc_schedule_set_listen_fd(sock->schedule, sock->sock,
+                               SILC_TASK_READ, FALSE);
 
   return ret;
 }
 
+/******************************* TCP Stream *********************************/
+
 /* Asynchronous TCP/IP connecting */
 
 typedef struct {
@@ -451,7 +460,7 @@ typedef struct {
   SilcSocketStreamStatus stream_status;
   SilcStream stream;
   SilcFSMStruct fsm;
-  SilcFSMSemaStruct sema;
+  SilcFSMEventStruct event;
   SilcAsyncOperation op;
   SilcAsyncOperation sop;
   char *local_ip;
@@ -473,8 +482,7 @@ SILC_FSM_STATE(silc_net_connect_st_finish);
 SILC_TASK_CALLBACK(silc_net_connect_wait)
 {
   SilcNetConnect conn = context;
-  SILC_FSM_SEMA_POST(&conn->sema);
-  silc_schedule_task_del_by_fd(schedule, conn->sock);
+  SILC_FSM_EVENT_SIGNAL(&conn->event);
 }
 
 SILC_FSM_STATE(silc_net_connect_st_start)
@@ -542,18 +550,15 @@ SILC_FSM_STATE(silc_net_connect_st_start)
   rval = connect(sock, &desthost.sa, SIZEOF_SOCKADDR(desthost));
   if (rval < 0) {
     if (errno != EINPROGRESS) {
-      /* retry using an IPv4 adress, if IPv6 didn't work */
-      if (prefer_ipv6 && silc_net_is_ip6(conn->ip_addr)) {
-        shutdown(sock, 2);
-        close(sock);
+      shutdown(sock, 2);
+      close(sock);
 
+      /* Retry using an IPv4 adress, if IPv6 didn't work */
+      if (prefer_ipv6 && silc_net_is_ip6(conn->ip_addr)) {
         prefer_ipv6 = FALSE;
         goto retry;
       }
 
-      shutdown(sock, 2);
-      close(sock);
-
       /** Cannot connect to remote host */
       SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
       silc_fsm_next(fsm, silc_net_connect_st_finish);
@@ -573,12 +578,12 @@ SILC_FSM_STATE(silc_net_connect_st_start)
 
   /** Wait for connection */
   silc_fsm_next(fsm, silc_net_connect_st_connected);
-  silc_fsm_sema_init(&conn->sema, fsm, 0);
+  silc_fsm_event_init(&conn->event, fsm);
   silc_schedule_task_add_fd(silc_fsm_get_schedule(fsm), sock,
                            silc_net_connect_wait, conn);
   silc_schedule_set_listen_fd(silc_fsm_get_schedule(fsm), sock,
                              SILC_TASK_WRITE, FALSE);
-  SILC_FSM_SEMA_WAIT(&conn->sema);
+  SILC_FSM_EVENT_WAIT(&conn->event);
   return SILC_FSM_CONTINUE;
 }
 
@@ -586,6 +591,7 @@ static void silc_net_connect_wait_stream(SilcSocketStreamStatus status,
                                         SilcStream stream, void *context)
 {
   SilcNetConnect conn = context;
+  conn->sop = NULL;
   conn->stream_status = status;
   conn->stream = stream;
   SILC_FSM_CALL_CONTINUE(&conn->fsm);
@@ -599,6 +605,8 @@ SILC_FSM_STATE(silc_net_connect_st_connected)
 
   if (conn->aborted) {
     /** Aborted */
+    silc_schedule_unset_listen_fd(schedule, conn->sock);
+    silc_schedule_task_del_by_fd(schedule, conn->sock);
     silc_fsm_next(fsm, silc_net_connect_st_finish);
     return SILC_FSM_CONTINUE;
   }
@@ -606,8 +614,8 @@ SILC_FSM_STATE(silc_net_connect_st_connected)
   ret = silc_net_get_socket_opt(conn->sock, SOL_SOCKET, SO_ERROR,
                                &opt, &optlen);
 
-  silc_schedule_task_del_by_fd(schedule, conn->sock);
   silc_schedule_unset_listen_fd(schedule, conn->sock);
+  silc_schedule_task_del_by_fd(schedule, conn->sock);
 
   if (ret != 0 || opt != 0) {
     if (conn->retry) {
@@ -620,29 +628,30 @@ SILC_FSM_STATE(silc_net_connect_st_connected)
     }
 
 #if defined(ECONNREFUSED)
-    if (errno == ECONNREFUSED)
+    if (opt == ECONNREFUSED)
       conn->status = SILC_NET_CONNECTION_REFUSED;
 #endif /* ECONNREFUSED */
 #if defined(ETIMEDOUT)
-    if (errno == ETIMEDOUT)
+    if (opt == ETIMEDOUT)
       conn->status = SILC_NET_CONNECTION_TIMEOUT;
 #endif /* ETIMEDOUT */
 #if defined(ENETUNREACH)
-    if (errno == ENETUNREACH)
+    if (opt == ENETUNREACH)
       conn->status = SILC_NET_HOST_UNREACHABLE;
 #endif /* ENETUNREACH */
 
     /** Connecting failed */
-    SILC_LOG_DEBUG(("Connecting failed"));
+    SILC_LOG_DEBUG(("Connecting failed, error %s", strerror(opt)));
     silc_fsm_next(fsm, silc_net_connect_st_finish);
     return SILC_FSM_CONTINUE;
   }
 
+  SILC_LOG_DEBUG(("TCP connection established"));
+
   /** Connection created */
   silc_fsm_next(fsm, silc_net_connect_st_stream);
   SILC_FSM_CALL((conn->sop = silc_socket_tcp_stream_create(
-                                    conn->sock, FALSE, FALSE,
-                                    schedule,
+                                    conn->sock, TRUE, FALSE, schedule,
                                     silc_net_connect_wait_stream, conn)));
 }
 
@@ -668,13 +677,8 @@ SILC_FSM_STATE(silc_net_connect_st_stream)
     return SILC_FSM_CONTINUE;
   }
 
-  /* Set stream information */
-  silc_socket_stream_set_info(conn->stream,
-                             !silc_net_is_ip(conn->remote) ? conn->remote :
-                             conn->ip_addr, conn->ip_addr, conn->port);
-
   /** Stream created successfully */
-  SILC_LOG_DEBUG(("Connected successfully"));
+  SILC_LOG_DEBUG(("Connected successfully, sock %d", conn->sock));
   conn->status = SILC_NET_OK;
   silc_fsm_next(fsm, silc_net_connect_st_finish);
   return SILC_FSM_CONTINUE;
@@ -689,8 +693,6 @@ SILC_FSM_STATE(silc_net_connect_st_finish)
     conn->callback(conn->status, conn->stream, conn->context);
     if (conn->op)
       silc_async_free(conn->op);
-    if (conn->sop)
-      silc_async_free(conn->sop);
   }
 
   return SILC_FSM_FINISH;
@@ -702,8 +704,10 @@ static void silc_net_connect_abort(SilcAsyncOperation op, void *context)
   conn->aborted = TRUE;
 
   /* Abort underlaying stream creation too */
-  if (conn->sop)
-    silc_async_abort(conn->op, NULL, NULL);
+  if (conn->sop) {
+    silc_async_abort(conn->sop, NULL, NULL);
+    conn->sop = NULL;
+  }
 }
 
 static void silc_net_connect_destructor(SilcFSM fsm, void *fsm_context,
@@ -741,6 +745,7 @@ SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
   /* Start async operation */
   conn->op = silc_async_alloc(silc_net_connect_abort, NULL, conn);
   if (!conn->op) {
+    silc_free(conn);
     callback(SILC_NET_NO_MEMORY, NULL, context);
     return NULL;
   }
@@ -749,6 +754,9 @@ SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
     conn->local_ip = strdup(local_ip_addr);
   conn->remote = strdup(remote_ip_addr);
   if (!conn->remote) {
+    silc_async_free(conn->op);
+    silc_free(conn->local_ip);
+    silc_free(conn);
     callback(SILC_NET_NO_MEMORY, NULL, context);
     return NULL;
   }
@@ -768,14 +776,15 @@ SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
 
 void silc_net_close_connection(int sock)
 {
+  SILC_LOG_DEBUG(("Closing sock %d", sock));
   close(sock);
 }
 
 /* Set's the socket to non-blocking mode. */
 
-int silc_net_set_socket_nonblock(int sock)
+int silc_net_set_socket_nonblock(SilcSocket sock)
 {
-  return fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
+  return fcntl((int)sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
 }
 
 /* Converts the IP number string from numbers-and-dots notation to