Added SILC Thread Queue API
[silc.git] / lib / silcutil / silcsocketstream.c
index e3521a1f48d3c65f3ccdc0cb3487a78997be111d..e11fff73a5a5b29fdd1922b6d1b2b60476fedc97 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2005 Pekka Riikonen
+  Copyright (C) 2005 - 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
 
 */
 
-#include "silcincludes.h"
+#include "silc.h"
 
-#define SILC_IS_SOCKET_STREAM(s) (s->ops == &silc_socket_stream_ops)
+/************************** Types and definitions ***************************/
 
-const SilcStreamOps silc_socket_stream_ops;
-
-/* Platform specific functions */
+/* Stream operation functions (platform specific) */
 int silc_socket_stream_read(SilcStream stream, unsigned char *buf,
                            SilcUInt32 buf_len);
 int silc_socket_stream_write(SilcStream stream, const unsigned char *data,
                             SilcUInt32 data_len);
-bool silc_socket_stream_close(SilcStream stream);
+SilcBool silc_socket_stream_close(SilcStream stream);
+void silc_socket_stream_destroy(SilcStream stream);
+int silc_socket_udp_stream_read(SilcStream stream, unsigned char *buf,
+                               SilcUInt32 buf_len);
+int silc_socket_udp_stream_write(SilcStream stream, const unsigned char *data,
+                                SilcUInt32 data_len);
+SilcBool silc_socket_stream_close(SilcStream stream);
 void silc_socket_stream_destroy(SilcStream stream);
+SilcBool silc_socket_stream_notifier(SilcStream stream,
+                                    SilcSchedule schedule,
+                                    SilcStreamNotifier callback,
+                                    void *context);
+SilcSchedule silc_socket_stream_get_schedule(SilcStream stream);
 
 /* Internal async host lookup context. */
 typedef struct {
   SilcSocketStream stream;
-  SilcSocketStreamStatus status;
+  SilcResult status;
   SilcSocketStreamCallback callback;
   SilcAsyncOperation op;
   void *context;
@@ -42,29 +51,8 @@ typedef struct {
   unsigned int aborted      : 1;
 } *SilcSocketHostLookup;
 
-/* The IO process callback that calls the notifier callback to upper
-   layer. */
-
-SILC_TASK_CALLBACK(silc_socket_stream_io)
-{
-  SilcSocketStream stream = context;
-
-  if (!stream->notifier)
-    return;
-
-  switch (type) {
-  case SILC_TASK_WRITE:
-    stream->notifier(stream, SILC_STREAM_CAN_WRITE, stream->notifier_context);
-    break;
 
-  case SILC_TASK_READ:
-    stream->notifier(stream, SILC_STREAM_CAN_READ, stream->notifier_context);
-    break;
-
-  default:
-    break;
-  }
-}
+/************************ Static utility functions **************************/
 
 /* Finishing timeout callback that will actually call the user specified
    host lookup callback.  This is executed back in the calling thread and
@@ -77,33 +65,19 @@ SILC_TASK_CALLBACK(silc_socket_host_lookup_finish)
 
   if (lookup->aborted) {
     SILC_LOG_DEBUG(("Socket stream creation was aborted"));
-    silc_net_close_connection(stream->sock);
-    silc_free(stream->ip);
-    silc_free(stream->hostname);
-    silc_free(stream);
+    stream->schedule = NULL;
+    silc_socket_stream_destroy(stream);
     silc_free(lookup);
     return;
   }
 
-  if (lookup->status != SILC_SOCKET_OK) {
-    SILC_LOG_DEBUG(("Socket stream failed"));
-    silc_net_close_connection(stream->sock);
-    silc_free(stream->ip);
-    silc_free(stream->hostname);
-    silc_free(stream);
+  if (lookup->status != SILC_OK) {
+    SILC_LOG_DEBUG(("Socket stream lookup failed"));
+    stream->schedule = NULL;
+    silc_socket_stream_destroy(stream);
     stream = lookup->stream = NULL;
   }
 
-  /* Add the socket to scheduler */
-  if (stream) {
-    silc_schedule_task_add_fd(stream->schedule, stream->sock,
-                             silc_socket_stream_io, stream);
-
-    /* Initially set socket for reading */
-    silc_schedule_set_listen_fd(stream->schedule, stream->sock,
-                               SILC_TASK_READ, FALSE);
-  }
-
   /* Return the created socket stream to the caller */
   if (lookup->callback)
     lookup->callback(lookup->status, stream, lookup->context);
@@ -125,28 +99,28 @@ static void *silc_socket_host_lookup_start(void *context)
 
   silc_net_check_host_by_sock(stream->sock, &stream->hostname, &stream->ip);
   if (!stream->ip) {
-    lookup->status = SILC_SOCKET_UNKNOWN_IP;
+    lookup->status = SILC_ERR_UNKNOWN_IP;
     goto out;
   }
 
   if (!stream->hostname && lookup->require_fqdn) {
-    lookup->status = SILC_SOCKET_UNKNOWN_HOST;
+    lookup->status = SILC_ERR_UNKNOWN_HOST;
     goto out;
   }
 
   if (!stream->hostname) {
-    stream->hostname = strdup(stream->ip);
+    stream->hostname = silc_strdup(stream->ip);
     if (!stream->hostname) {
-      lookup->status = SILC_SOCKET_NO_MEMORY;
+      lookup->status = SILC_ERR_OUT_OF_MEMORY;
       goto out;
     }
   }
 
-  lookup->status = SILC_SOCKET_OK;
+  lookup->status = SILC_OK;
 
  out:
   silc_schedule_task_add_timeout(schedule, silc_socket_host_lookup_finish,
-                                lookup, 0, 1);
+                                lookup, 0, 0);
   silc_schedule_wakeup(schedule);
   return NULL;
 }
@@ -163,35 +137,57 @@ static void silc_socket_host_lookup_abort(SilcAsyncOperation op,
   lookup->aborted = TRUE;
 }
 
-/* Creates socket stream */
+
+/******************************* Public API *********************************/
+
+/* Creates TCP socket stream */
 
 SilcAsyncOperation
-silc_socket_stream_create(int sock, bool lookup, bool require_fqdn,
-                         SilcSchedule schedule,
-                         SilcSocketStreamCallback callback,
-                         void *context)
+silc_socket_tcp_stream_create(SilcSocket sock, SilcBool lookup,
+                             SilcBool require_fqdn,
+                             SilcSchedule schedule,
+                             SilcSocketStreamCallback callback,
+                             void *context)
 {
   SilcSocketStream stream;
   SilcSocketHostLookup l;
 
+  if (!schedule) {
+    schedule = silc_schedule_get_global();
+    if (!schedule) {
+      silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+      if (callback)
+       callback(silc_errno, NULL, context);
+      return NULL;
+    }
+  }
+
+  if (!sock) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+    if (callback)
+      callback(silc_errno, NULL, context);
+    return NULL;
+  }
+
   stream = silc_calloc(1, sizeof(*stream));
   if (!stream) {
     if (callback)
-      callback(SILC_SOCKET_NO_MEMORY, NULL, context);
+      callback(silc_errno, NULL, context);
     return NULL;
   }
 
-  SILC_LOG_DEBUG(("Creating new socket stream %p", stream));
+  SILC_LOG_DEBUG(("Creating TCP socket stream %p, sock %lu", stream, sock));
 
   stream->ops = &silc_socket_stream_ops;
   stream->sock = sock;
   stream->schedule = schedule;
+  stream->connected = TRUE;
 
   l = silc_calloc(1, sizeof(*l));
   if (!l) {
     silc_free(stream);
     if (callback)
-      callback(SILC_SOCKET_NO_MEMORY, NULL, context);
+      callback(silc_errno, NULL, context);
     return NULL;
   }
 
@@ -207,7 +203,7 @@ silc_socket_stream_create(int sock, bool lookup, bool require_fqdn,
       silc_free(stream);
       silc_free(l);
       if (callback)
-       callback(SILC_SOCKET_ERROR, NULL, context);
+       callback(silc_errno, NULL, context);
       return NULL;
     }
 
@@ -217,7 +213,7 @@ silc_socket_stream_create(int sock, bool lookup, bool require_fqdn,
     return l->op;
   } else {
     /* No lookup */
-    l->status = SILC_SOCKET_OK;
+    l->status = SILC_OK;
     silc_socket_host_lookup_finish(schedule,
                                   silc_schedule_get_context(schedule),
                                   0, 0, l);
@@ -225,51 +221,113 @@ silc_socket_stream_create(int sock, bool lookup, bool require_fqdn,
   }
 }
 
+/* Creates UDP socket stream */
+
+SilcStream silc_socket_udp_stream_create(SilcSocket sock, SilcBool ipv6,
+                                        SilcBool connected,
+                                        SilcSchedule schedule)
+{
+  SilcSocketStream stream;
+
+  if (!schedule) {
+    schedule = silc_schedule_get_global();
+    if (!schedule) {
+      silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+      return NULL;
+    }
+  }
+
+  stream = silc_calloc(1, sizeof(*stream));
+  if (!stream)
+    return NULL;
+
+  SILC_LOG_DEBUG(("Creating UDP socket stream %p", stream));
+
+  stream->ops = &silc_socket_udp_stream_ops;
+  stream->sock = sock;
+  stream->schedule = schedule;
+  stream->ipv6 = ipv6;
+  stream->connected = connected;
+
+  return (SilcStream)stream;
+}
+
+/* Returns TRUE if the stream is UDP stream */
+
+SilcBool silc_socket_stream_is_udp(SilcStream stream, SilcBool *connected)
+{
+  SilcSocketStream socket_stream = stream;
+
+  if (!SILC_IS_SOCKET_STREAM_UDP(socket_stream))
+    return FALSE;
+
+  if (connected)
+    *connected = socket_stream->connected;
+
+  return TRUE;
+}
+
 /* Returns socket stream information */
 
-bool silc_socket_stream_get_info(SilcStream stream,
-                                int *sock, const char **hostname,
-                                const char **ip, SilcUInt16 *port)
+SilcBool silc_socket_stream_get_info(SilcStream stream,
+                                    SilcSocket *sock, const char **hostname,
+                                    const char **ip, SilcUInt16 *port)
 {
   SilcSocketStream socket_stream = stream;
 
-  if (!SILC_IS_SOCKET_STREAM(socket_stream))
+  if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
+      !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
     return FALSE;
 
   if (sock)
     *sock = socket_stream->sock;
-  if (hostname)
-    *hostname = socket_stream->hostname;
-  if (ip)
-    *ip = socket_stream->ip;
-  if (port)
+  if (port) {
+    if (!socket_stream->port)
+      return FALSE;
     *port = socket_stream->port;
+  }
+  if (ip) {
+    if (!socket_stream->ip)
+      return FALSE;
+    *ip = socket_stream->ip;
+  }
+  if (hostname) {
+    if (!socket_stream->hostname)
+      return FALSE;
+    *hostname = socket_stream->hostname;
+  }
 
   return TRUE;
 }
 
 /* Set socket information */
 
-bool silc_socket_stream_set_info(SilcStream stream,
-                                const char *hostname,
-                                const char *ip, SilcUInt16 port)
+SilcBool silc_socket_stream_set_info(SilcStream stream,
+                                    const char *hostname,
+                                    const char *ip, SilcUInt16 port)
 {
   SilcSocketStream socket_stream = stream;
 
-  if (!SILC_IS_SOCKET_STREAM(socket_stream))
+  if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
+      !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
     return FALSE;
 
   if (hostname) {
     silc_free(socket_stream->hostname);
-    socket_stream->hostname = strdup(hostname);
+    socket_stream->hostname = silc_strdup(hostname);
     if (!socket_stream->hostname)
       return FALSE;
   }
   if (ip) {
     silc_free(socket_stream->ip);
-    socket_stream->ip = strdup(ip);
+    socket_stream->ip = silc_strdup(ip);
     if (!socket_stream->ip)
       return FALSE;
+    if (!socket_stream->hostname) {
+      socket_stream->hostname = silc_strdup(ip);
+      if (!socket_stream->hostname)
+       return FALSE;
+    }
   }
   if (port)
     socket_stream->port = port;
@@ -277,29 +335,18 @@ bool silc_socket_stream_set_info(SilcStream stream,
   return TRUE;
 }
 
-/* Return socket errno */
-
-int silc_socket_stream_get_error(SilcStream stream)
-{
-  SilcSocketStream socket_stream = stream;
-
-  if (!SILC_IS_SOCKET_STREAM(socket_stream))
-    return 0;
-
-  return socket_stream->sock_error;
-}
-
 /* Set QoS for socket stream */
 
-bool silc_socket_stream_set_qos(SilcStream stream,
-                               SilcUInt32 read_rate,
-                               SilcUInt32 read_limit_bytes,
-                               SilcUInt32 limit_sec,
-                               SilcUInt32 limit_usec)
+SilcBool silc_socket_stream_set_qos(SilcStream stream,
+                                   SilcUInt32 read_rate,
+                                   SilcUInt32 read_limit_bytes,
+                                   SilcUInt32 limit_sec,
+                                   SilcUInt32 limit_usec)
 {
   SilcSocketStream socket_stream = stream;
 
-  if (!SILC_IS_SOCKET_STREAM(socket_stream))
+  if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
+      !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
     return FALSE;
 
   SILC_LOG_DEBUG(("Setting QoS for socket stream"));
@@ -335,63 +382,17 @@ bool silc_socket_stream_set_qos(SilcStream stream,
   return TRUE;
 }
 
-/* Closes socket */
-
-bool silc_socket_stream_close(SilcStream stream)
-{
-  SilcSocketStream socket_stream = stream;
-
-  if (!SILC_IS_SOCKET_STREAM(socket_stream))
-    return FALSE;
-
-  silc_schedule_unset_listen_fd(socket_stream->schedule, socket_stream->sock);
-  silc_net_close_connection(socket_stream->sock);
-
-  return TRUE;
-}
-
-/* Destroys the stream */
+/* Return associated scheduler */
 
-void silc_socket_stream_destroy(SilcStream stream)
+SilcSchedule silc_socket_stream_get_schedule(SilcStream stream)
 {
   SilcSocketStream socket_stream = stream;
 
-  if (!SILC_IS_SOCKET_STREAM(socket_stream))
-    return;
-
-  silc_socket_stream_close(socket_stream);
-  silc_free(socket_stream->ip);
-  silc_free(socket_stream->hostname);
-
-  if (socket_stream->qos) {
-    silc_schedule_task_del_by_context(socket_stream->schedule,
-                                     socket_stream->qos);
-    if (socket_stream->qos->buffer) {
-      memset(socket_stream->qos->buffer, 0,
-            socket_stream->qos->read_limit_bytes);
-      silc_free(socket_stream->qos->buffer);
-    }
-    silc_free(socket_stream->qos);
-  }
-
-  silc_free(socket_stream);
-}
-
-/* Sets stream notification callback for the stream */
-
-void silc_socket_stream_notifier(SilcStream stream,
-                                SilcStreamNotifier callback,
-                                void *context)
-{
-  SilcSocketStream socket_stream = stream;
-
-  if (!SILC_IS_SOCKET_STREAM(socket_stream))
-    return;
-
-  SILC_LOG_DEBUG(("Setting stream notifier callback"));
+  if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
+      !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
+    return NULL;
 
-  socket_stream->notifier = callback;
-  socket_stream->notifier_context = context;
+  return socket_stream->schedule;
 }
 
 /* SILC Socket Stream ops.  Functions are implemented under the
@@ -403,4 +404,14 @@ const SilcStreamOps silc_socket_stream_ops =
   silc_socket_stream_close,
   silc_socket_stream_destroy,
   silc_socket_stream_notifier,
+  silc_socket_stream_get_schedule,
+};
+const SilcStreamOps silc_socket_udp_stream_ops =
+{
+  silc_socket_udp_stream_read,
+  silc_socket_udp_stream_write,
+  silc_socket_stream_close,
+  silc_socket_stream_destroy,
+  silc_socket_stream_notifier,
+  silc_socket_stream_get_schedule,
 };