Added silc_net_tcp_create_listener2
[crypto.git] / lib / silcutil / win32 / silcwin32net.c
index 87fc12f84e025fcf56d533ca55016c38b0ec2bf8..5c4181f66dafe91954c7f591af1b6acf34c913a8 100644 (file)
@@ -57,16 +57,19 @@ static SilcBool silc_net_set_sockaddr(SilcSockaddr *addr, const char *ip_addr,
     if (silc_net_is_ip4(ip_addr)) {
       /* IPv4 address */
       len = sizeof(addr->sin.sin_addr);
-      silc_net_addr2bin(ip_addr,
-                       (unsigned char *)&addr->sin.sin_addr.s_addr, len);
+      if (!silc_net_addr2bin(ip_addr,
+                            (unsigned char *)&addr->sin.sin_addr.s_addr,
+                            len))
+       return FALSE;
       addr->sin.sin_family = AF_INET;
       addr->sin.sin_port = port ? htons(port) : 0;
     } else {
 #ifdef HAVE_IPV6
       /* IPv6 address */
       len = sizeof(addr->sin6.sin6_addr);
-      silc_net_addr2bin(ip_addr,
-                       (unsigned char *)&addr->sin6.sin6_addr, len);
+      if (!silc_net_addr2bin(ip_addr,
+                            (unsigned char *)&addr->sin6.sin6_addr, len))
+       return FALSE;
       addr->sin6.sin6_family = AF_INET6;
       addr->sin6.sin6_port = port ? htons(port) : 0;
 #else
@@ -115,7 +118,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 */
@@ -179,14 +181,15 @@ silc_net_tcp_create_listener(const char **local_ip_addr,
     /* Create the socket */
     sock = socket(server.sin.sin_family, SOCK_STREAM, 0);
     if (sock == INVALID_SOCKET) {
-      SILC_LOG_ERROR(("Cannot create socket"));
+      SILC_LOG_ERROR(("Cannot create socket, error %d", WSAGetLastError()));
       goto err;
     }
 
     /* Set the socket options */
     rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
     if (rval == SOCKET_ERROR) {
-      SILC_LOG_ERROR(("Cannot set socket options"));
+      SILC_LOG_ERROR(("Cannot set socket options, error %d",
+                    WSAGetLastError()));
       closesocket(sock);
       goto err;
     }
@@ -194,7 +197,7 @@ silc_net_tcp_create_listener(const char **local_ip_addr,
     /* Bind the listener socket */
     rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server));
     if (rval == SOCKET_ERROR) {
-      SILC_LOG_ERROR(("Cannot bind socket"));
+      SILC_LOG_ERROR(("Cannot bind socket, error %d", WSAGetLastError()));
       closesocket(sock);
       goto err;
     }
@@ -202,13 +205,125 @@ silc_net_tcp_create_listener(const char **local_ip_addr,
     /* Specify that we are listenning */
     rval = listen(sock, SOMAXCONN);
     if (rval == SOCKET_ERROR) {
-      SILC_LOG_ERROR(("Cannot set socket listenning"));
+      SILC_LOG_ERROR(("Cannot set socket listenning, error %d",
+                    WSAGetLastError()));
       closesocket(sock);
       goto err;
     }
 
-    /* Set the server socket to non-blocking mode.  Dunno if this works. */
-    silc_net_set_socket_nonblock(sock);
+    /* Schedule for incoming connections */
+    silc_schedule_task_add_fd(schedule, sock, silc_net_accept, listener);
+
+    SILC_LOG_DEBUG(("TCP listener created, fd=%d", sock));
+    listener->socks[i] = sock;
+    listener->socks_count++;
+  }
+
+  return listener;
+
+ err:
+  if (listener)
+    silc_net_close_listener(listener);
+  return NULL;
+}
+
+/* Create TCP network, multiple ports */
+
+SilcNetListener
+silc_net_tcp_create_listener2(const char *local_ip_addr, int *ports,
+                             SilcUInt32 port_count,
+                             SilcBool ignore_port_error,
+                             SilcBool lookup, SilcBool require_fqdn,
+                             SilcSchedule schedule,
+                             SilcNetCallback callback, void *context)
+{
+  SilcNetListener listener = NULL;
+  SOCKET sock;
+  SilcSockaddr server;
+  int i, rval;
+  const char *ipany = "0.0.0.0";
+
+  SILC_LOG_DEBUG(("Creating TCP listener"));
+
+  if (!schedule || !callback)
+    goto err;
+
+  listener = silc_calloc(1, sizeof(*listener));
+  if (!listener)
+    return NULL;
+  listener->schedule = schedule;
+  listener->callback = callback;
+  listener->context = context;
+  listener->require_fqdn = require_fqdn;
+  listener->lookup = lookup;
+
+  if (port_count > 0) {
+    listener->socks = silc_calloc(port_count, sizeof(*listener->socks));
+    if (!listener->socks)
+      return NULL;
+  } else {
+    listener->socks = silc_calloc(1, sizeof(*listener->socks));
+    if (!listener->socks)
+      return NULL;
+
+    port_count = 1;
+  }
+
+  /* Bind to local addresses */
+  for (i = 0; i < local_ip_count; i++) {
+    SILC_LOG_DEBUG(("Binding to local address %s:%d",
+                   local_ip_addr ? local_ip_addr : ipany,
+                   ports ? ports[i] : 0));
+
+    /* Set sockaddr for server */
+    if (!silc_net_set_sockaddr(&server,
+                              local_ip_addr ? local_ip_addr : ipany,
+                              ports ? ports[i] : 0)) {
+      if (ignore_port_error)
+       continue;
+      goto err;
+    }
+
+    /* Create the socket */
+    sock = socket(server.sin.sin_family, SOCK_STREAM, 0);
+    if (sock == INVALID_SOCKET) {
+      if (ignore_port_error)
+       continue;
+      SILC_LOG_ERROR(("Cannot create socket, error %d", WSAGetLastError()));
+      goto err;
+    }
+
+    /* Set the socket options */
+    rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+    if (rval == SOCKET_ERROR) {
+      closesocket(sock);
+      if (ignore_port_error)
+       continue;
+      SILC_LOG_ERROR(("Cannot set socket options, error %d",
+                    WSAGetLastError()));
+      goto err;
+    }
+
+    /* Bind the listener socket */
+    rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server));
+    if (rval == SOCKET_ERROR) {
+      closesocket(sock);
+      if (ignore_port_error)
+       continue;
+      SILC_LOG_ERROR(("Cannot bind socket, error %d", WSAGetLastError()));
+      goto err;
+    }
+
+    /* Specify that we are listenning */
+    rval = listen(sock, SOMAXCONN);
+    if (rval == SOCKET_ERROR) {
+      closesocket(sock);
+      if (ignore_port_error)
+       continue;
+      SILC_LOG_ERROR(("Cannot set socket listenning, error %d",
+                    WSAGetLastError()));
+      goto err;
+    }
 
     /* Schedule for incoming connections */
     silc_schedule_task_add_fd(schedule, sock, silc_net_accept, listener);
@@ -218,6 +333,9 @@ silc_net_tcp_create_listener(const char **local_ip_addr,
     listener->socks_count++;
   }
 
+  if (ignore_port_error && !listener->socks_count)
+    goto err;
+
   return listener;
 
  err:
@@ -234,6 +352,9 @@ void silc_net_close_listener(SilcNetListener listener)
 
   SILC_LOG_DEBUG(("Closing network listener"));
 
+  if (!listener)
+    return;
+
   for (i = 0; i < listener->socks_count; i++) {
     silc_schedule_task_del_by_fd(listener->schedule, listener->socks[i]);
     shutdown(listener->socks[i], 2);
@@ -301,9 +422,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))
@@ -472,9 +590,10 @@ 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);
+  SILC_FSM_CALL_CONTINUE(&conn->thread);
 }
 
 /* Start connecting.  Create a real thread where we connect. */
@@ -483,7 +602,7 @@ SILC_FSM_STATE(silc_net_connect_st_thread)
 {
   SilcNetConnect conn = fsm_context;
 
-  /* Connect in real thread as as to not block the application. */
+  /* Connect in real thread so as to not block the application. */
   silc_fsm_thread_init(&conn->thread, fsm, conn, NULL, NULL, TRUE);
   silc_fsm_start(&conn->thread, silc_net_connect_st_start);
 
@@ -510,7 +629,7 @@ SILC_FSM_STATE(silc_net_connect_st_start)
   if (!silc_net_gethostbyname(conn->remote, prefer_ipv6,
                              conn->ip_addr, sizeof(conn->ip_addr))) {
     SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
-                   "host", conn->remote));
+                   "host, error %d", conn->remote, WSAGetLastError()));
 
     /** Network unreachable */
     conn->status = SILC_NET_HOST_UNREACHABLE;
@@ -532,7 +651,7 @@ SILC_FSM_STATE(silc_net_connect_st_start)
     }
 
     /** Cannot create socket */
-    SILC_LOG_ERROR(("Cannot create socket"));
+    SILC_LOG_ERROR(("Cannot create socket, error %d", WSAGetLastError()));
     return SILC_FSM_FINISH;
   }
 
@@ -553,7 +672,7 @@ SILC_FSM_STATE(silc_net_connect_st_start)
       shutdown(sock, 2);
       closesocket(sock);
 
-      /* Retry using an IPv4 adress, if IPv6 didn't work */
+      /* Retry using an IPv4 address, if IPv6 didn't work */
       if (prefer_ipv6 && silc_net_is_ip6(conn->ip_addr)) {
        prefer_ipv6 = FALSE;
        goto retry;
@@ -573,7 +692,8 @@ SILC_FSM_STATE(silc_net_connect_st_start)
        break;
       }
 
-      SILC_LOG_ERROR(("Cannot connect to remote host"));
+      SILC_LOG_ERROR(("Cannot connect to remote host, error %d",
+                     WSAGetLastError()));
       return SILC_FSM_FINISH;
     }
   }
@@ -594,8 +714,8 @@ SILC_FSM_STATE(silc_net_connect_st_start)
   /** 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,
-                                    silc_fsm_get_schedule(fsm),
+                                    conn->sock, TRUE, FALSE,
+                                    silc_fsm_get_schedule(&conn->fsm),
                                     silc_net_connect_wait_stream, conn)));
 }
 
@@ -620,11 +740,6 @@ SILC_FSM_STATE(silc_net_connect_st_stream)
     return SILC_FSM_FINISH;
   }
 
-  /* 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, sock %d", conn->sock));
   conn->status = SILC_NET_OK;
@@ -640,8 +755,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;
@@ -653,8 +766,10 @@ static void silc_net_connect_abort(SilcAsyncOperation op, void *context)
   conn->aborted = TRUE;
 
   /* Abort underlaying stream creation too */
-  if (conn->sop)
+  if (conn->sop) {
     silc_async_abort(conn->sop, NULL, NULL);
+    conn->sop = NULL;
+  }
 }
 
 static void silc_net_connect_destructor(SilcFSM fsm, void *fsm_context,
@@ -732,17 +847,37 @@ void silc_net_close_connection(int sock)
 
 SilcBool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len)
 {
-  unsigned long ret;
-
   if (silc_net_is_ip4(addr)) {
     /* IPv4 address */
-    ret = inet_addr(addr);
+    int i = 0, c = 0, d = 0, len = strlen(addr);
+    unsigned char ret[4];
+
+    memset(ret, 0, sizeof(ret));
+    while (len-- > 0) {
+      if (addr[i++] == '.') {
+       ret[c++] = d;
+       d = 0;
+       if (c > 3)
+         return FALSE;
+       continue;
+      }
 
-    if (bin_len < 4)
+      if (!isdigit((int)addr[i - 1]))
+       return FALSE;
+
+      d = 10 * d + addr[i - 1] - '0';
+      if (d > 255)
+       return FALSE;
+    }
+    if (c != 3)
       return FALSE;
+    ret[c] = d;
 
-    memcpy(bin, (unsigned char *)&ret, 4);
-    return ret != INADDR_NONE;
+    if (bin_len < sizeof(ret))
+      return FALSE;
+
+    memcpy(bin, ret, sizeof(ret));
+    return TRUE;
   } else {
 #ifdef HAVE_IPV6
     struct addrinfo hints, *ai;
@@ -777,32 +912,3 @@ int silc_net_set_socket_nonblock(SilcSocket sock)
   unsigned long on = 1;
   return ioctlsocket(sock, FIONBIO, &on);
 }
-
-/* Init Winsock2. */
-
-SilcBool silc_net_win32_init(void)
-{
-  int ret, sopt = SO_SYNCHRONOUS_NONALERT;
-  WSADATA wdata;
-  WORD ver = MAKEWORD(1, 1);
-
-  ret = WSAStartup(ver, &wdata);
-  if (ret)
-    return FALSE;
-
-  /* Allow using the SOCKET's as file descriptors so that we can poll
-     them with SILC Scheduler. */
-  ret = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&sopt,
-                  sizeof(sopt));
-  if (ret)
-    return FALSE;
-
-  return TRUE;
-}
-
-/* Uninit Winsock2 */
-
-void silc_net_win32_uninit(void)
-{
-  WSACleanup();
-}