[Telepathy-commits] [telepathy-salut/master] Initial implementation of GibberListener

Sjoerd Simons sjoerd.simons at collabora.co.uk
Mon Nov 3 03:57:21 PST 2008


---
 lib/gibber/gibber-listener.c |  255 +++++++++++++++++++++++++++++++++++++++--
 lib/gibber/gibber-listener.h |   15 ++-
 2 files changed, 255 insertions(+), 15 deletions(-)

diff --git a/lib/gibber/gibber-listener.c b/lib/gibber/gibber-listener.c
index 06920b0..98a9ee5 100644
--- a/lib/gibber/gibber-listener.c
+++ b/lib/gibber/gibber-listener.c
@@ -97,16 +97,19 @@ gibber_listener_dispose (GObject *object)
     GIBBER_LISTENER (object);
   GibberListenerPrivate *priv =
     GIBBER_LISTENER_GET_PRIVATE (self);
+  GSList *t;
 
-  if (priv->listeners != NULL)
+  for (t = priv->listeners ; t != NULL ; t = g_slist_delete_link (t, t))
     {
-      /*
-      g_io_channel_unref (priv->listener);
-      priv->listener = NULL;
-      g_source_remove (priv->io_watch_in);
-      */
+      Listener *l = (Listener *)t->data;
+
+      g_io_channel_unref (l->listener);
+      g_source_remove (l->io_watch_in);
+      g_slice_free (Listener, l);
     }
 
+  priv->listeners = NULL;
+
   G_OBJECT_CLASS (gibber_listener_parent_class)->dispose (
       object);
 }
@@ -150,24 +153,250 @@ unimplemented (GError **error)
   return FALSE;
 }
 
+
+static gboolean
+listener_io_in_cb (GIOChannel *source,
+                   GIOCondition condition,
+                   gpointer user_data)
+{
+  GibberListener *self = GIBBER_LISTENER (user_data);
+  GibberFdTransport *transport;
+  int fd, nfd;
+  int ret;
+  char host[NI_MAXHOST];
+  char port[NI_MAXSERV];
+  struct sockaddr_storage addr;
+  socklen_t addrlen = sizeof (struct sockaddr_storage);
+
+  fd = g_io_channel_unix_get_fd (source);
+  nfd = accept (fd, (struct sockaddr *) &addr, &addrlen);
+  gibber_normalize_address (&addr);
+
+  transport = g_object_new (GIBBER_TYPE_FD_TRANSPORT, NULL);
+  gibber_fd_transport_set_fd (transport, nfd);
+
+  ret = getnameinfo ((struct sockaddr *) &addr, addrlen,
+      host, NI_MAXHOST, port, NI_MAXSERV,
+      NI_NUMERICHOST | NI_NUMERICSERV);
+
+  if (ret == 0)
+    DEBUG("New connection from %s port %s", host, port);
+  else
+    DEBUG("New connection..");
+
+  g_signal_emit (self, signals[NEW_CONNECTION], 0, transport, &addr, addrlen);
+
+  g_object_unref (transport);
+  return TRUE;
+}
+
+static gboolean
+add_listener (GibberListener *self, int family, int type, int protocol,
+  struct sockaddr *address, socklen_t addrlen, GError **error)
+{
+  #define BACKLOG 5
+  int fd = -1, ret, yes = 1;
+  Listener *l;
+  GibberListenerPrivate *priv = self->priv;
+  char name [NI_MAXHOST], portname[NI_MAXSERV];
+  struct sockaddr_storage baddress;
+  socklen_t baddrlen;
+
+  fd = socket (family, type, protocol);
+  if (fd == -1)
+    {
+      DEBUG ("socket failed: %s", g_strerror (errno));
+      g_set_error (error, GIBBER_LISTENER_ERROR,
+          errno == EAFNOSUPPORT ? GIBBER_LISTENER_ERROR_FAMILY_NOT_SUPPORTED :
+            GIBBER_LISTENER_ERROR_FAILED,
+          "%s", g_strerror (errno));
+      goto error;
+    }
+
+  ret = setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (int));
+  if (ret == -1)
+    {
+      DEBUG ("setsockopt failed: %s", g_strerror (errno));
+      g_set_error (error, GIBBER_LISTENER_ERROR,
+          GIBBER_LISTENER_ERROR_FAILED,
+          "%s", g_strerror (errno));
+      goto error;
+    }
+
+#ifdef IPV6_V6ONLY
+  if (family == AF_INET6)
+    {
+      ret = setsockopt (fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof (int));
+      if (ret == -1)
+        {
+          DEBUG ("setsockopt failed: %s", g_strerror (errno));
+          g_set_error (error, GIBBER_LISTENER_ERROR,
+             GIBBER_LISTENER_ERROR_FAILED,
+             "%s", g_strerror (errno));
+          goto error;
+        }
+    }
+#endif
+
+  ret = bind (fd, address, addrlen);
+  if (ret  < 0)
+    {
+      DEBUG ("bind failed: %s", g_strerror (errno));
+      g_set_error (error, GIBBER_LISTENER_ERROR,
+          errno == EADDRINUSE ?
+          GIBBER_LISTENER_ERROR_ADDRESS_IN_USE :
+          GIBBER_LISTENER_ERROR_FAILED,
+          "%s", g_strerror (errno));
+      goto error;
+    }
+
+  ret = listen (fd, BACKLOG);
+  if (ret == -1)
+    {
+      DEBUG ("listen failed: %s", g_strerror (errno));
+      g_set_error (error, GIBBER_LISTENER_ERROR,
+          errno == EADDRINUSE ?
+          GIBBER_LISTENER_ERROR_ADDRESS_IN_USE :
+          GIBBER_LISTENER_ERROR_FAILED,
+          "%s", g_strerror (errno));
+      goto error;
+    }
+
+  getsockname (fd, (struct sockaddr *)&baddress, &baddrlen);
+  getnameinfo ((struct sockaddr *)&baddress, baddrlen, name, sizeof (name),
+      portname, sizeof (portname), NI_NUMERICHOST | NI_NUMERICSERV);
+
+  DEBUG ( "Listening on %s port %s...", name, portname);
+
+  l = g_slice_new(Listener);
+
+  l->listener = g_io_channel_unix_new (fd);
+  g_io_channel_set_close_on_unref (l->listener, TRUE);
+  l->io_watch_in = g_io_add_watch (l->listener, G_IO_IN,
+      listener_io_in_cb, self);
+
+  priv->listeners = g_slist_append (priv->listeners, l);
+
+  return TRUE;
+
+error:
+  if (fd > 0)
+    close (fd);
+  return FALSE;
+}
+
+static gboolean
+listen_tcp_af (GibberListener *listener, int port,
+  GibberAddressFamily family, gboolean loopback, GError **error)
+{
+  struct addrinfo req, *ans = NULL, *a;
+  GibberListenerPrivate *priv = listener->priv;
+  int ret;
+  gchar sport[6];
+
+  memset (&req, 0, sizeof (req));
+  if (!loopback)
+    req.ai_flags = AI_PASSIVE;
+
+  switch (family)
+    {
+      case GIBBER_AF_IPV4:
+        req.ai_family = AF_INET;
+        break;
+      case GIBBER_AF_IPV6:
+        req.ai_family = AF_INET6;
+        break;
+      case GIBBER_AF_ANY:
+        req.ai_family = AF_UNSPEC;
+        break;
+    }
+  req.ai_socktype = SOCK_STREAM;
+  req.ai_protocol = IPPROTO_TCP;
+
+  g_snprintf (sport, 6, "%d", port);
+
+  ret = getaddrinfo (NULL, sport, &req, &ans);
+  if (ret != 0)
+    {
+      DEBUG ("getaddrinfo failed: %s", gai_strerror (ret));
+      g_set_error (error, GIBBER_LISTENER_ERROR,
+          GIBBER_LISTENER_ERROR_FAILED,
+          "%s", gai_strerror (ret));
+      goto error;
+    }
+
+  for (a = ans ; a != NULL ; a = a->ai_next)
+    {
+      gboolean ret;
+      GError *terror = NULL;
+
+      ret = add_listener (listener, a->ai_family, a->ai_socktype,
+        a->ai_protocol, a->ai_addr, a->ai_addrlen, &terror);
+
+      if (ret == FALSE)
+        {
+          gboolean fatal = !g_error_matches (terror, GIBBER_LISTENER_ERROR,
+              GIBBER_LISTENER_ERROR_FAMILY_NOT_SUPPORTED);
+
+          if (error != NULL)
+            *error = terror;
+          else
+            g_error_free (terror);
+
+          if (fatal)
+              goto error;
+        }
+    }
+
+
+  /* If all listeners failed, report the last error */
+  if (priv->listeners == NULL)
+    goto error;
+
+  if (*error != NULL)
+    {
+      /* There was an error at some point, but not fatal */
+      g_error_free(*error);
+      *error = NULL;
+    }
+
+  freeaddrinfo (ans);
+
+  return TRUE;
+
+error:
+  if (ans != NULL)
+    freeaddrinfo (ans);
+
+  return FALSE;
+}
+
 gboolean
-gibber_listener_listen_tcp (GibberListener *listener, int port,
-    GError **error) {
-  return gibber_listener_listen_tcp_af (listener, port, AF_UNSPEC, error);
+gibber_listener_listen_tcp (GibberListener *listener, int port, GError **error)
+{
+  return gibber_listener_listen_tcp_af (listener, port, GIBBER_AF_ANY, error);
 }
 
 gboolean
 gibber_listener_listen_tcp_af (GibberListener *listener, int port,
-  int adress_family, GError **error) {
+  GibberAddressFamily family, GError **error)
+{
+  return listen_tcp_af (listener, port, family, FALSE, error);
+}
 
-  return unimplemented (error);
+gboolean
+gibber_listener_listen_tcp_loopback (GibberListener *listener,
+  int port, GError **error)
+{
+  return gibber_listener_listen_tcp_loopback_af (listener, port,
+    GIBBER_AF_ANY, error);
 }
 
 gboolean
 gibber_listener_listen_tcp_loopback_af (GibberListener *listener,
-  int port, int address_family, GError **error)
+  int port, GibberAddressFamily family, GError **error)
 {
-  return unimplemented (error);
+  return listen_tcp_af (listener, port, family, TRUE, error);
 }
 
 gboolean
diff --git a/lib/gibber/gibber-listener.h b/lib/gibber/gibber-listener.h
index 148adf7..712b409 100644
--- a/lib/gibber/gibber-listener.h
+++ b/lib/gibber/gibber-listener.h
@@ -32,9 +32,17 @@ typedef enum
 {
   GIBBER_LISTENER_ERROR_ALREADY_LISTENING,
   GIBBER_LISTENER_ERROR_ADDRESS_IN_USE,
+  GIBBER_LISTENER_ERROR_FAMILY_NOT_SUPPORTED,
   GIBBER_LISTENER_ERROR_FAILED,
 } GibberListenerError;
 
+typedef enum
+{
+  GIBBER_AF_IPV4,
+  GIBBER_AF_IPV6,
+  GIBBER_AF_ANY
+} GibberAddressFamily;
+
 typedef struct _GibberListener GibberListener;
 typedef struct _GibberListenerClass GibberListenerClass;
 
@@ -73,10 +81,13 @@ gboolean gibber_listener_listen_tcp (GibberListener *listener,
   int port, GError **error);
 
 gboolean gibber_listener_listen_tcp_af (GibberListener *listener,
-  int port, int adress_family, GError **error);
+  int port, GibberAddressFamily family, GError **error);
+
+gboolean gibber_listener_listen_tcp_loopback (GibberListener *listener,
+  int port, GError **error);
 
 gboolean gibber_listener_listen_tcp_loopback_af (GibberListener *listener,
-  int port, int address_family, GError **error);
+  int port, GibberAddressFamily family, GError **error);
 
 gboolean gibber_listener_listen_socket (GibberListener *listener,
   gchar *path, gboolean abstract, GError **error);
-- 
1.5.6.5




More information about the Telepathy-commits mailing list