[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