Added silc_likely and silc_unlikely GCC branch prediction macros.
[silc.git] / lib / silcutil / silcsocketstream.c
index 815bbdb1796fc9c43e9d798b127e9cc75c2c66cb..0e40d4141ddd64ab799c7a59b4c68c427281b9ed 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2005 Pekka Riikonen
+  Copyright (C) 2005 - 2006 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"
+
+/************************** Types and definitions ***************************/
 
 #define SILC_IS_SOCKET_STREAM(s) (s->ops == &silc_socket_stream_ops)
+#define SILC_IS_SOCKET_STREAM_UDP(s) (s->ops == &silc_socket_udp_stream_ops)
 
 const SilcStreamOps silc_socket_stream_ops;
+const SilcStreamOps silc_socket_udp_stream_ops;
 
 /* Platform specific functions */
 int silc_socket_stream_read(SilcStream stream, unsigned char *buf,
@@ -30,6 +34,10 @@ int silc_socket_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);
+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);
 
 /* Internal async host lookup context. */
 typedef struct {
@@ -42,6 +50,9 @@ typedef struct {
   unsigned int aborted      : 1;
 } *SilcSocketHostLookup;
 
+
+/************************ Static utility functions **************************/
+
 /* The IO process callback that calls the notifier callback to upper
    layer. */
 
@@ -49,7 +60,7 @@ SILC_TASK_CALLBACK(silc_socket_stream_io)
 {
   SilcSocketStream stream = context;
 
-  if (!stream->notifier)
+  if (silc_unlikely(!stream->notifier))
     return;
 
   switch (type) {
@@ -94,16 +105,6 @@ SILC_TASK_CALLBACK(silc_socket_host_lookup_finish)
     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);
@@ -146,7 +147,7 @@ static void *silc_socket_host_lookup_start(void *context)
 
  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,13 +164,17 @@ 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, SilcBool lookup, SilcBool require_fqdn,
-                         SilcSchedule schedule,
-                         SilcSocketStreamCallback callback,
-                         void *context)
+silc_socket_tcp_stream_create(int sock, SilcBool lookup,
+                             SilcBool require_fqdn,
+                             SilcSchedule schedule,
+                             SilcSocketStreamCallback callback,
+                             void *context)
 {
   SilcSocketStream stream;
   SilcSocketHostLookup l;
@@ -181,7 +186,7 @@ silc_socket_stream_create(int sock, SilcBool lookup, SilcBool require_fqdn,
     return NULL;
   }
 
-  SILC_LOG_DEBUG(("Creating new socket stream %p", stream));
+  SILC_LOG_DEBUG(("Creating TCP socket stream %p", stream));
 
   stream->ops = &silc_socket_stream_ops;
   stream->sock = sock;
@@ -225,25 +230,73 @@ silc_socket_stream_create(int sock, SilcBool lookup, SilcBool require_fqdn,
   }
 }
 
+/* Creates UDP socket stream */
+
+SilcStream silc_socket_udp_stream_create(int sock, SilcBool ipv6,
+                                        SilcBool connected,
+                                        SilcSchedule schedule)
+{
+  SilcSocketStream stream;
+
+  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 */
 
 SilcBool silc_socket_stream_get_info(SilcStream stream,
-                                int *sock, const char **hostname,
-                                const char **ip, SilcUInt16 *port)
+                                    int *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)
+  if (hostname) {
+    if (!socket_stream->hostname)
+      return FALSE;
     *hostname = socket_stream->hostname;
-  if (ip)
+  }
+  if (ip) {
+    if (!socket_stream->ip)
+      return FALSE;
     *ip = socket_stream->ip;
-  if (port)
+  }
+  if (port) {
+    if (!socket_stream->port)
+      return FALSE;
     *port = socket_stream->port;
+  }
 
   return TRUE;
 }
@@ -251,12 +304,13 @@ SilcBool silc_socket_stream_get_info(SilcStream stream,
 /* Set socket information */
 
 SilcBool silc_socket_stream_set_info(SilcStream stream,
-                                const char *hostname,
-                                const char *ip, SilcUInt16 port)
+                                    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) {
@@ -270,6 +324,11 @@ SilcBool silc_socket_stream_set_info(SilcStream stream,
     socket_stream->ip = strdup(ip);
     if (!socket_stream->ip)
       return FALSE;
+    if (!socket_stream->hostname) {
+      socket_stream->hostname = strdup(ip);
+      if (!socket_stream->hostname)
+       return FALSE;
+    }
   }
   if (port)
     socket_stream->port = port;
@@ -283,7 +342,8 @@ int silc_socket_stream_get_error(SilcStream stream)
 {
   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 0;
 
   return socket_stream->sock_error;
@@ -292,14 +352,15 @@ int silc_socket_stream_get_error(SilcStream stream)
 /* Set QoS for socket stream */
 
 SilcBool silc_socket_stream_set_qos(SilcStream stream,
-                               SilcUInt32 read_rate,
-                               SilcUInt32 read_limit_bytes,
-                               SilcUInt32 limit_sec,
-                               SilcUInt32 limit_usec)
+                                   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"));
@@ -341,7 +402,8 @@ SilcBool silc_socket_stream_close(SilcStream stream)
 {
   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_schedule_unset_listen_fd(socket_stream->schedule, socket_stream->sock);
@@ -356,12 +418,14 @@ void silc_socket_stream_destroy(SilcStream stream)
 {
   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;
 
   silc_socket_stream_close(socket_stream);
   silc_free(socket_stream->ip);
   silc_free(socket_stream->hostname);
+  silc_schedule_task_del_by_fd(socket_stream->schedule, socket_stream->sock);
 
   if (socket_stream->qos) {
     silc_schedule_task_del_by_context(socket_stream->schedule,
@@ -374,24 +438,60 @@ void silc_socket_stream_destroy(SilcStream stream)
     silc_free(socket_stream->qos);
   }
 
+  silc_schedule_wakeup(socket_stream->schedule);
+
   silc_free(socket_stream);
 }
 
 /* Sets stream notification callback for the stream */
 
 void silc_socket_stream_notifier(SilcStream stream,
+                                SilcSchedule schedule,
                                 SilcStreamNotifier callback,
                                 void *context)
 {
   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;
 
   SILC_LOG_DEBUG(("Setting stream notifier callback"));
 
   socket_stream->notifier = callback;
   socket_stream->notifier_context = context;
+  socket_stream->schedule = schedule;
+
+  if (socket_stream->notifier) {
+    /* Add the socket to scheduler.  Safe to call if already added. */
+    silc_schedule_task_add_fd(socket_stream->schedule, socket_stream->sock,
+                             silc_socket_stream_io, socket_stream);
+
+    /* Initially set socket for reading */
+    silc_schedule_set_listen_fd(socket_stream->schedule, socket_stream->sock,
+                               SILC_TASK_READ, FALSE);
+    silc_schedule_wakeup(socket_stream->schedule);
+  } else {
+    /* Unschedule the socket */
+    silc_schedule_unset_listen_fd(socket_stream->schedule,
+                                 socket_stream->sock);
+    silc_schedule_task_del_by_fd(socket_stream->schedule,
+                                socket_stream->sock);
+    silc_schedule_wakeup(socket_stream->schedule);
+  }
+}
+
+/* Return associated scheduler */
+
+SilcSchedule silc_socket_stream_get_schedule(SilcStream stream)
+{
+  SilcSocketStream socket_stream = stream;
+
+  if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
+      !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
+    return NULL;
+
+  return socket_stream->schedule;
 }
 
 /* SILC Socket Stream ops.  Functions are implemented under the
@@ -403,4 +503,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,
 };