Added SILC Thread Queue API
[silc.git] / lib / silcutil / symbian / silcsymbiannet.cpp
index 6cc8d736e839deb236d3ae948289fa21b8ca9130..c665b3d339632fb6655a6b0484f2e726c1e1def3 100644 (file)
 #include "silc.h"
 #include "silcsymbiansocketstream.h"
 
+/************************ Static utility functions **************************/
+
+static SilcBool silc_net_set_sockaddr(TInetAddr *addr, const char *ip_addr,
+                                      int port)
+{
+  /* Check for IPv4 and IPv6 addresses */
+  if (ip_addr) {
+    if (!silc_net_is_ip(ip_addr)) {
+      SILC_LOG_ERROR(("%s is not IP address", ip_addr));
+      return FALSE;
+    }
+
+    if (silc_net_is_ip4(ip_addr)) {
+      /* IPv4 address */
+      unsigned char buf[4];
+      TUint32 a;
+
+      if (!silc_net_addr2bin(ip_addr, buf, sizeof(buf)))
+        return FALSE;
+
+      SILC_GET32_MSB(a, buf);
+      addr->SetAddress(a);
+      addr->SetPort(port);
+    } else {
+#ifdef HAVE_IPV6
+      SILC_LOG_ERROR(("IPv6 not supported"));
+      return FALSE;
+#else
+      SILC_LOG_ERROR(("Operating System does not support IPv6"));
+      return FALSE;
+#endif
+    }
+  } else {
+    addr->SetAddress(0);
+    addr->SetPort(port);
+  }
+
+  return TRUE;
+}
+
 /****************************** TCP Listener ********************************/
 
 class SilcSymbianTCPListener;
 
+extern "C" {
+
 /* Deliver new stream to upper layer */
 
-static void silc_net_accept_stream(SilcSocketStreamStatus status,
+static void silc_net_accept_stream(SilcResult status,
                                   SilcStream stream, void *context)
 {
   SilcNetListener listener = (SilcNetListener)context;
 
-  /* In case of error, the socket has been destroyed already */
-  if (status != SILC_SOCKET_OK)
+  /* In case of error, the socket has been destroyed already via
+     silc_stream_destroy. */
+  if (status != SILC_OK)
     return;
 
-  listener->callback(SILC_NET_OK, stream, listener->context);
+  listener->callback(SILC_OK, stream, listener->context);
 }
 
+} /* extern "C" */
+
 /* TCP Listener class */
 
 class SilcSymbianTCPListener : public CActive {
@@ -57,6 +102,8 @@ public:
   /* Listen for connection */
   void Listen()
   {
+    SILC_LOG_DEBUG(("Listen()"));
+
     new_conn = new RSocket;
     if (!new_conn)
       return;
@@ -73,6 +120,8 @@ public:
   /* Listener callback */
   virtual void RunL()
   {
+    SILC_LOG_DEBUG(("RunL(), iStatus=%d", iStatus));
+
     if (iStatus != KErrNone) {
       if (new_conn)
        delete new_conn;
@@ -81,6 +130,8 @@ public:
       return;
     }
 
+    SILC_LOG_DEBUG(("Accept new connection"));
+
     /* Set socket options */
     new_conn->SetOpt(KSoReuseAddr, KSolInetIp, 1);
 
@@ -111,6 +162,8 @@ public:
   SilcNetListener listener;
 };
 
+extern "C" {
+
 /* Create TCP listener */
 
 SilcNetListener
@@ -124,17 +177,26 @@ silc_net_tcp_create_listener(const char **local_ip_addr,
   SilcSymbianTCPListener *l = NULL;
   TInetAddr server;
   TInt ret;
-  TBuf<64> tmp;
   int i;
 
   SILC_LOG_DEBUG(("Creating TCP listener"));
 
-  if (port < 0 || !schedule || !callback)
+  if (!schedule) {
+    schedule = silc_schedule_get_global();
+    if (!schedule) {
+      silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+      goto err;
+    }
+  }
+
+  if (port < 0 || !callback) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
     goto err;
+  }
 
   listener = (SilcNetListener)silc_calloc(1, sizeof(*listener));
   if (!listener) {
-    callback(SILC_NET_NO_MEMORY, NULL, context);
+    callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
     return NULL;
   }
   listener->schedule = schedule;
@@ -145,15 +207,15 @@ silc_net_tcp_create_listener(const char **local_ip_addr,
 
   if (local_ip_count > 0) {
     listener->socks = (SilcSocket *)silc_calloc(local_ip_count,
-                                             sizeof(*listener->socks));
+                                               sizeof(*listener->socks));
     if (!listener->socks) {
-      callback(SILC_NET_NO_MEMORY, NULL, context);
+      callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
       return NULL;
     }
   } else {
     listener->socks = (SilcSocket *)silc_calloc(1, sizeof(*listener->socks));
     if (!listener->socks) {
-      callback(SILC_NET_NO_MEMORY, NULL, context);
+      callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
       return NULL;
     }
 
@@ -174,42 +236,192 @@ silc_net_tcp_create_listener(const char **local_ip_addr,
     if (ret != KErrNone)
       goto err;
 
+#ifdef SILC_THREADS
+    /* Make our socket shareable between threads */
+    l->ss.ShareAuto();
+#endif /* SILC_THREADS */
+
     /* Set listener address */
-    if (local_ip_addr) {
-      server = TInetAddr(port);
-      tmp = (TText *)local_ip_addr[i];
-      ret = server.Input(tmp);
-      if (ret != KErrNone)
-       goto err;
-    } else {
-      server = TInetAddr(KInetAddrAny, port);
+    if (!silc_net_set_sockaddr(&server, local_ip_addr[i], port))
+      goto err;
+
+    /* Create the socket */
+    ret = l->sock.Open(l->ss, KAfInet, KSockStream, KProtocolInetTcp);
+    if (ret != KErrNone) {
+      SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
+      goto err;
+    }
+
+    /* Set the socket options */
+    ret = l->sock.SetOpt(KSoReuseAddr, KSolInetIp, 1);
+    if (ret != KErrNone) {
+      SILC_LOG_ERROR(("Cannot set socket options, error %d", ret));
+      goto err;
+    }
+
+    /* Bind the listener socket */
+    ret = l->sock.Bind(server);
+    if (ret != KErrNone) {
+      SILC_LOG_DEBUG(("Cannot bind socket, error %d", ret));
+      goto err;
+    }
+
+    /* Specify that we are listenning */
+    ret = l->sock.Listen(5);
+    if (ret != KErrNone) {
+      SILC_LOG_ERROR(("Cannot set socket listenning, error %d", ret));
+      goto err;
+    }
+    l->Listen();
+
+    l->listener = listener;
+    listener->socks[i] = (SilcSocket)l;
+    listener->socks_count++;
+  }
+
+  SILC_LOG_DEBUG(("TCP listener created"));
+
+  return listener;
+
+ err:
+  if (l)
+    delete l;
+  if (callback)
+    callback(SILC_ERR, NULL, context);
+  if (listener)
+    silc_net_close_listener(listener);
+  return NULL;
+}
+
+/* Create TCP listener, 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;
+  SilcSymbianTCPListener *l = NULL;
+  TInetAddr server;
+  TInt ret;
+  int i;
+
+  SILC_LOG_DEBUG(("Creating TCP listener"));
+
+  if (!schedule) {
+    schedule = silc_schedule_get_global();
+    if (!schedule) {
+      silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+      goto err;
+    }
+  }
+
+  if (!callback) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+    goto err;
+  }
+
+  listener = (SilcNetListener)silc_calloc(1, sizeof(*listener));
+  if (!listener) {
+    callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
+    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 = (SilcSocket *)silc_calloc(port_count,
+                                               sizeof(*listener->socks));
+    if (!listener->socks) {
+      callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
+      return NULL;
+    }
+  } else {
+    listener->socks = (SilcSocket *)silc_calloc(1, sizeof(*listener->socks));
+    if (!listener->socks) {
+      callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
+      return NULL;
+    }
+
+    port_count = 1;
+  }
+
+  /* Bind to ports */
+  for (i = 0; i < port_count; i++) {
+    SILC_LOG_DEBUG(("Binding to local address %s:%d",
+                   local_ip_addr ? local_ip_addr : "0.0.0.0",
+                   ports ? ports[i] : 0));
+
+    l = new SilcSymbianTCPListener;
+    if (!l)
+      goto err;
+
+    /* Connect to socket server */
+    ret = l->ss.Connect();
+    if (ret != KErrNone)
+      goto err;
+
+#ifdef SILC_THREADS
+    /* Make our socket shareable between threads */
+    l->ss.ShareAuto();
+#endif /* SILC_THREADS */
+
+    /* Set listener address */
+    if (!silc_net_set_sockaddr(&server, local_ip_addr, ports ? ports[i] : 0)) {
+      if (ignore_port_error) {
+       delete l;
+       continue;
+      }
+      goto err;
     }
 
     /* Create the socket */
     ret = l->sock.Open(l->ss, KAfInet, KSockStream, KProtocolInetTcp);
     if (ret != KErrNone) {
-      SILC_LOG_ERROR(("Cannot create socket"));
+      if (ignore_port_error) {
+       delete l;
+       continue;
+      }
+      SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
       goto err;
     }
 
     /* Set the socket options */
     ret = l->sock.SetOpt(KSoReuseAddr, KSolInetIp, 1);
     if (ret != KErrNone) {
-      SILC_LOG_ERROR(("Cannot set socket options"));
+      if (ignore_port_error) {
+       delete l;
+       continue;
+      }
+      SILC_LOG_ERROR(("Cannot set socket options, error %d", ret));
       goto err;
     }
 
     /* Bind the listener socket */
     ret = l->sock.Bind(server);
     if (ret != KErrNone) {
-      SILC_LOG_DEBUG(("Cannot bind socket"));
+      if (ignore_port_error) {
+       delete l;
+       continue;
+      }
+      SILC_LOG_DEBUG(("Cannot bind socket, error %d", ret));
       goto err;
     }
 
     /* Specify that we are listenning */
     ret = l->sock.Listen(5);
     if (ret != KErrNone) {
-      SILC_LOG_ERROR(("Cannot set socket listenning"));
+      if (ignore_port_error) {
+       delete l;
+       continue;
+      }
+      SILC_LOG_ERROR(("Cannot set socket listenning, error %d", ret));
       goto err;
     }
     l->Listen();
@@ -219,6 +431,11 @@ silc_net_tcp_create_listener(const char **local_ip_addr,
     listener->socks_count++;
   }
 
+  if (ignore_port_error && !listener->socks_count) {
+    l = NULL;
+    goto err;
+  }
+
   SILC_LOG_DEBUG(("TCP listener created"));
 
   return listener;
@@ -227,7 +444,7 @@ silc_net_tcp_create_listener(const char **local_ip_addr,
   if (l)
     delete l;
   if (callback)
-    callback(SILC_NET_ERROR, NULL, context);
+    callback(SILC_ERR, NULL, context);
   if (listener)
     silc_net_close_listener(listener);
   return NULL;
@@ -241,6 +458,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++) {
     SilcSymbianTCPListener *l = (SilcSymbianTCPListener *)listener->socks[i];
     l->sock.CancelAll();
@@ -255,11 +475,14 @@ void silc_net_close_listener(SilcNetListener listener)
   silc_free(listener);
 }
 
+
 /**************************** TCP/IP connecting *****************************/
 
-static void silc_net_connect_stream(SilcSocketStreamStatus status,
+static void silc_net_connect_stream(SilcResult status,
                                    SilcStream stream, void *context);
 
+} /* extern "C" */
+
 /* TCP connecting class */
 
 class SilcSymbianTCPConnect : public CActive {
@@ -282,6 +505,7 @@ public:
   /* Connect to remote host */
   void Connect(TSockAddr &addr)
   {
+    SILC_LOG_DEBUG(("Connect()"));
     sock->Connect(addr, iStatus);
     SetActive();
   }
@@ -289,9 +513,11 @@ public:
   /* Connection callback */
   virtual void RunL()
   {
+    SILC_LOG_DEBUG(("RunL(), iStatus=%d", iStatus));
+
     if (iStatus != KErrNone) {
       if (callback)
-       callback(SILC_NET_ERROR, NULL, context);
+       callback(SILC_ERR, NULL, context);
       sock->CancelConnect();
       delete sock;
       ss->Close();
@@ -302,12 +528,16 @@ public:
       return;
     }
 
+    SILC_LOG_DEBUG(("Connected to host %s on %d", remote_ip, port));
+
     /* Create stream */
     if (callback) {
       silc_socket_tcp_stream_create(
                             (SilcSocket)silc_create_symbian_socket(sock, ss),
-                            FALSE, FALSE, schedule, silc_net_connect_stream,
+                            TRUE, FALSE, schedule, silc_net_connect_stream,
                             (void *)this);
+      sock = NULL;
+      ss = NULL;
     } else {
       sock->Close();
       delete sock;
@@ -315,9 +545,8 @@ public:
       delete ss;
       sock = NULL;
       ss = NULL;
+      delete this;
     }
-
-    delete this;
   }
 
   /* Cancel */
@@ -344,33 +573,20 @@ public:
   void *context;
 };
 
-/* Stream creation callback */
+extern "C" {
+
+/* TCP stream creation callback */
 
-static void silc_net_connect_stream(SilcSocketStreamStatus status,
+static void silc_net_connect_stream(SilcResult status,
                                    SilcStream stream, void *context)
 {
   SilcSymbianTCPConnect *conn = (SilcSymbianTCPConnect *)context;
-  SilcNetStatus net_status = SILC_NET_OK;
-
-  if (status != SILC_SOCKET_OK) {
-    /* In case of error, the socket has been destroyed already */
-    if (status == SILC_SOCKET_UNKNOWN_IP)
-      net_status = SILC_NET_UNKNOWN_IP;
-    else if (status == SILC_SOCKET_UNKNOWN_HOST)
-      net_status = SILC_NET_UNKNOWN_HOST;
-    else
-      net_status = SILC_NET_ERROR;
-  }
 
-  /* Set stream information */
-  if (stream && conn->callback)
-    silc_socket_stream_set_info(stream,
-                               !silc_net_is_ip(conn->remote) ? conn->remote :
-                               conn->remote_ip, conn->remote_ip, conn->port);
+  SILC_LOG_DEBUG(("Socket stream creation status %d", status));
 
   /* Call connection callback */
   if (conn->callback)
-    conn->callback(net_status, stream, conn->context);
+    conn->callback(status, stream, conn->context);
   else if (stream)
     silc_stream_destroy(stream);
 
@@ -387,7 +603,7 @@ static void silc_net_connect_abort(SilcAsyncOperation op, void *context)
   conn->callback = NULL;
   conn->op = NULL;
   if (conn->sock)
-    sock->CancelConnect();
+    conn->sock->CancelConnect();
 }
 
 /* Create TCP/IP connection */
@@ -401,19 +617,28 @@ SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
 {
   SilcSymbianTCPConnect *conn;
   TInetAddr local, remote;
-  SilcNetStatus status;
-  TBuf<64> tmp;
+  SilcResult status;
   TInt ret;
 
-  if (!remote_ip_addr || remote_port < 1 || !schedule || !callback)
+  if (!schedule) {
+    schedule = silc_schedule_get_global();
+    if (!schedule) {
+      silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+      return NULL;
+    }
+  }
+
+  if (!remote_ip_addr || remote_port < 1 || !callback) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
     return NULL;
+  }
 
   SILC_LOG_DEBUG(("Creating connection to host %s port %d",
                  remote_ip_addr, remote_port));
 
   conn = new SilcSymbianTCPConnect;
   if (!conn) {
-    callback(SILC_NET_NO_MEMORY, NULL, context);
+    callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
     return NULL;
   }
   conn->schedule = schedule;
@@ -422,56 +647,58 @@ SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
   conn->port = remote_port;
   conn->remote = strdup(remote_ip_addr);
   if (!conn->remote) {
-    status = SILC_NET_NO_MEMORY;
+    status = SILC_ERR_OUT_OF_MEMORY;
     goto err;
   }
 
   /* Allocate socket */
   conn->sock = new RSocket;
   if (!conn->sock) {
-    status = SILC_NET_NO_MEMORY;
+    status = SILC_ERR_OUT_OF_MEMORY;
     goto err;
   }
 
   /* Allocate socket server */
   conn->ss = new RSocketServ;
   if (!conn->ss) {
-    status = SILC_NET_NO_MEMORY;
+    status = SILC_ERR_OUT_OF_MEMORY;
     goto err;
   }
 
   /* Connect to socket server */
   ret = conn->ss->Connect();
   if (ret != KErrNone) {
-    status = SILC_NET_ERROR;
+    SILC_LOG_ERROR(("Error connecting to socket server, error %d", ret));
+    status = SILC_ERR;
     goto err;
   }
 
+#ifdef SILC_THREADS
+  /* Make our socket shareable between threads */
+  conn->ss->ShareAuto();
+#endif /* SILC_THREADS */
+
   /* Start async operation */
   conn->op = silc_async_alloc(silc_net_connect_abort, NULL, (void *)conn);
   if (!conn->op) {
-    status = SILC_NET_NO_MEMORY;
+    status = SILC_ERR_OUT_OF_MEMORY;
     goto err;
   }
 
   /* Do host lookup */
-  if (!silc_net_is_ip(remote_ip_addr)) {
-    if (!silc_net_gethostbyname(remote_ip_addr, FALSE, conn->remote_ip,
-                               sizeof(conn->remote_ip))) {
-      SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
-                     "host", conn->remote));
-      status = SILC_NET_HOST_UNREACHABLE;
-      goto err;
-    }
-  } else {
-    strcpy(conn->remote_ip, remote_ip_addr);
+  if (!silc_net_gethostbyname(remote_ip_addr, FALSE, conn->remote_ip,
+                             sizeof(conn->remote_ip))) {
+    SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
+                   "host", conn->remote));
+    status = SILC_ERR_UNREACHABLE;
+    goto err;
   }
 
   /* Create the connection socket */
   ret = conn->sock->Open(*conn->ss, KAfInet, KSockStream, KProtocolInetTcp);
   if (ret != KErrNone) {
-    SILC_LOG_ERROR(("Cannot create socket"));
-    status = SILC_NET_ERROR;
+    SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
+    status = SILC_ERR;
     goto err;
   }
 
@@ -480,21 +707,14 @@ SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
   conn->sock->SetOpt(KSoTcpKeepAlive, KSolInetTcp, 1);
 
   /* Bind to the local address if provided */
-  if (local_ip_addr) {
-    local = TInetAddr(0);
-    tmp = (TText *)local_ip_addr;
-    ret = local.Input(tmp);
-    if (ret == KErrNone)
-      ret = conn->sock->Bind(local);
-  }
+  if (local_ip_addr)
+    if (silc_net_set_sockaddr(&local, local_ip_addr, 0))
+      conn->sock->Bind(local);
 
   /* Connect to the host */
-  remote = TInetAddr(remote_port);
-  tmp = (TText *)conn->remote_ip;
-  ret = remote.Input(tmp);
-  if (ret != KErrNone) {
+  if (!silc_net_set_sockaddr(&remote, conn->remote_ip, remote_port)) {
     SILC_LOG_ERROR(("Cannot connect (cannot set address)"));
-    status = SILC_NET_ERROR;
+    status = SILC_ERR;
     goto err;
   }
   conn->Connect(remote);
@@ -533,13 +753,17 @@ SilcStream silc_net_udp_connect(const char *local_ip_addr, int local_port,
   TRequestStatus status;
   RSocket *sock = NULL;
   RSocketServ *ss = NULL;
-  TBuf<64> tmp;
   TInt ret;
 
   SILC_LOG_DEBUG(("Creating UDP stream"));
 
-  if (!schedule)
-    goto err;
+  if (!schedule) {
+    schedule = silc_schedule_get_global();
+    if (!schedule) {
+      silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+      goto err;
+    }
+  }
 
   SILC_LOG_DEBUG(("Binding to local address %s",
                  local_ip_addr ? local_ip_addr : "0.0.0.0"));
@@ -557,16 +781,14 @@ SilcStream silc_net_udp_connect(const char *local_ip_addr, int local_port,
   if (ret != KErrNone)
     goto err;
 
+#ifdef SILC_THREADS
+  /* Make our socket shareable between threads */
+  ss->ShareAuto();
+#endif /* SILC_THREADS */
+
   /* Get local bind address */
-  if (local_ip_addr) {
-    local = TInetAddr(local_port);
-    tmp = (TText *)local_ip_addr;
-    ret = local.Input(tmp);
-    if (ret != KErrNone)
-      goto err;
-  } else {
-    local = TInetAddr(KInetAddrAny, local_port);
-  }
+  if (!silc_net_set_sockaddr(&local, local_ip_addr, local_port))
+    goto err;
 
   /* Create the socket */
   ret = sock->Open(*ss, KAfInet, KSockDatagram, KProtocolInetUdp);
@@ -587,16 +809,12 @@ SilcStream silc_net_udp_connect(const char *local_ip_addr, int local_port,
 
   /* Set to connected state if remote address is provided. */
   if (remote_ip_addr && remote_port) {
-    remote = TInetAddr(remote_port);
-    tmp = (TText *)remote_ip_addr;
-    ret = remote.Input(tmp);
-    if (ret != KErrNone)
-      goto err;
-
-    sock->Connect(remote, status);
-    if (status != KErrNone) {
-      SILC_LOG_DEBUG(("Cannot connect UDP stream"));
-      goto err;
+    if (silc_net_set_sockaddr(&remote, remote_ip_addr, remote_port)) {
+      sock->Connect(remote, status);
+      if (status != KErrNone) {
+       SILC_LOG_DEBUG(("Cannot connect UDP stream"));
+       goto err;
+      }
     }
   }
 
@@ -639,8 +857,8 @@ int silc_net_set_socket_nonblock(SilcSocket sock)
 SilcBool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len)
 {
   int ret = 0;
-
   struct in_addr tmp;
+
   ret = inet_aton(addr, &tmp);
   if (bin_len < 4)
     return FALSE;
@@ -755,3 +973,5 @@ SilcUInt16 silc_net_get_local_port(SilcSocket sock)
   s->sock->LocalName(addr);
   return (SilcUInt16)addr.Port();
 }
+
+} /* extern "C" */