Port GSSDP to GIO/GSocket
authorJens Georg <mail@jensge.org>
Sun, 2 May 2010 14:41:14 +0000 (16:41 +0200)
committerZeeshan Ali (Khattak) <zeeshanak@gnome.org>
Sun, 10 Oct 2010 19:23:30 +0000 (22:23 +0300)
configure.ac
libgssdp/Makefile.am
libgssdp/gssdp-client.c
libgssdp/gssdp-socket-functions.c [new file with mode: 0644]
libgssdp/gssdp-socket-functions.h [new file with mode: 0644]
libgssdp/gssdp-socket-source.c
libgssdp/gssdp-socket-source.h

index 2309b68..4691e76 100644 (file)
@@ -12,8 +12,9 @@ AC_STDC_HEADERS
 AC_PROG_LIBTOOL
 AC_CONFIG_MACRO_DIR([m4])
 
-PKG_CHECK_MODULES(LIBGSSDP, glib-2.0 >= 2.18 \
-                            gobject-2.0 >= 2.18 \
+PKG_CHECK_MODULES(LIBGSSDP, glib-2.0 >= 2.22 \
+                            gobject-2.0 >= 2.22 \
+                            gio-2.0 >= 2.22 \
                             libsoup-2.4)
 
 LIBGTK_REQUIRED=2.12.0
index b1d4768..aad8362 100644 (file)
@@ -42,6 +42,8 @@ libgssdp_1_0_la_SOURCES = gssdp-client.c              \
                          gssdp-resource-group.c        \
                          gssdp-socket-source.c         \
                          gssdp-socket-source.h         \
+                         gssdp-socket-functions.c      \
+                         gssdp-socket-functions.h      \
                          $(BUILT_SOURCES)
 
 libgssdp_1_0_la_LIBADD = $(LIBGSSDP_LIBS)
index 3cef6e4..902a97f 100644 (file)
@@ -98,9 +98,13 @@ gssdp_client_set_main_context (GSSDPClient  *client,
 static char *
 make_server_id                (void);
 static gboolean
-request_socket_source_cb      (gpointer      user_data);
+request_socket_source_cb      (GIOChannel   *source,
+                               GIOCondition  condition,
+                               gpointer      user_data);
 static gboolean
-multicast_socket_source_cb    (gpointer      user_data);
+multicast_socket_source_cb    (GIOChannel   *source,
+                               GIOCondition  condition,
+                               gpointer      user_data);
 static gboolean
 init_network_info             (GSSDPClient  *client);
 
@@ -122,6 +126,7 @@ static void
 gssdp_client_constructed (GObject *object)
 {
         GSSDPClient *client = GSSDP_CLIENT (object);
+        GError *error = NULL;
 
         /* Make sure all network info is available to us */
         if (!init_network_info (client))
@@ -130,11 +135,12 @@ gssdp_client_constructed (GObject *object)
         /* Set up sockets (Will set errno if it failed) */
         client->priv->request_socket =
                 gssdp_socket_source_new (GSSDP_SOCKET_SOURCE_TYPE_REQUEST,
-                                         gssdp_client_get_host_ip (client));
+                                         gssdp_client_get_host_ip (client),
+                                         &error);
         if (client->priv->request_socket != NULL) {
                 g_source_set_callback
-                        ((GSource *) client->priv->request_socket,
-                         request_socket_source_cb,
+                        (client->priv->request_socket->source,
+                         (GSourceFunc) request_socket_source_cb,
                          client,
                          NULL);
         } else {
@@ -143,33 +149,31 @@ gssdp_client_constructed (GObject *object)
 
         client->priv->multicast_socket =
                 gssdp_socket_source_new (GSSDP_SOCKET_SOURCE_TYPE_MULTICAST,
-                                         gssdp_client_get_host_ip (client));
+                                         gssdp_client_get_host_ip (client),
+                                         &error);
         if (client->priv->multicast_socket != NULL) {
                 g_source_set_callback
-                        ((GSource *) client->priv->multicast_socket,
-                         multicast_socket_source_cb,
+                        (client->priv->multicast_socket->source,
+                         (GSourceFunc) multicast_socket_source_cb,
                          client,
                          NULL);
         }
 
  errors:
         if (!client->priv->request_socket || !client->priv->multicast_socket) {
-                if (client->priv->error)
-                        g_set_error_literal (client->priv->error,
-                                             GSSDP_ERROR,
-                                             GSSDP_ERROR_FAILED,
-                                             g_strerror (errno));
-
+                if (client->priv->error) {
+                        g_propagate_error (client->priv->error, error);
+                }
                 return;
         }
 
-        g_source_attach ((GSource *) client->priv->request_socket,
+        g_source_attach (client->priv->request_socket->source,
                          client->priv->main_context);
-        g_source_unref ((GSource *) client->priv->request_socket);
+        g_source_unref (client->priv->request_socket->source);
 
-        g_source_attach ((GSource *) client->priv->multicast_socket,
+        g_source_attach (client->priv->multicast_socket->source,
                          client->priv->main_context);
-        g_source_unref ((GSource *) client->priv->multicast_socket);
+        g_source_unref (client->priv->multicast_socket->source);
 }
 
 static void
@@ -254,12 +258,12 @@ gssdp_client_dispose (GObject *object)
 
         /* Destroy the SocketSources */
         if (client->priv->request_socket) {
-                g_source_destroy ((GSource *) client->priv->request_socket);
+                gssdp_socket_source_destroy (client->priv->request_socket);
                 client->priv->request_socket = NULL;
         }
 
         if (client->priv->multicast_socket) {
-                g_source_destroy ((GSource *) client->priv->multicast_socket);
+                gssdp_socket_source_destroy (client->priv->multicast_socket);
                 client->priv->multicast_socket = NULL;
         }
 
@@ -583,8 +587,11 @@ _gssdp_client_send_message (GSSDPClient *client,
                             gushort      dest_port,
                             const char  *message)
 {
-        struct sockaddr_in addr;
-        int socket_fd, res;
+        gssize res;
+        GError *error = NULL;
+        GSocket *socket = NULL;
+        GInetAddress *inet_address = NULL;
+        GSocketAddress *address = NULL;
 
         g_return_if_fail (GSSDP_IS_CLIENT (client));
         g_return_if_fail (message != NULL);
@@ -601,25 +608,24 @@ _gssdp_client_send_message (GSSDPClient *client,
         if (dest_port == 0)
                 dest_port = SSDP_PORT;
 
-        socket_fd = gssdp_socket_source_get_fd (client->priv->request_socket);
-
-        memset (&addr, 0, sizeof (addr));
-
-        addr.sin_family      = AF_INET;
-        addr.sin_port        = htons (dest_port);
-        addr.sin_addr.s_addr = inet_addr (dest_ip);
-
-        res = sendto (socket_fd,
-                      message,
-                      strlen (message),
-                      0,
-                      (struct sockaddr *) &addr,
-                      sizeof (addr));
+        inet_address = g_inet_address_new_from_string (dest_ip);
+        address = g_inet_socket_address_new (inet_address, dest_port);
+        socket = gssdp_socket_source_get_socket (client->priv->request_socket);
+        res = g_socket_send_to (socket,
+                                address,
+                                message,
+                                strlen (message),
+                                NULL,
+                                &error);
 
         if (res == -1) {
-                g_warning ("sendto: Error %d sending message: %s",
-                           errno, strerror (errno));
+                g_warning ("g_socket_sendto: Error sending message %s: %s",
+                           message, error->message);
+                g_error_free (error);
         }
+
+        g_object_unref (address);
+        g_object_unref (inet_address);
 }
 
 /**
@@ -710,34 +716,35 @@ parse_http_response (char                *buf,
  * Called when data can be read from the socket
  **/
 static gboolean
-socket_source_cb (GSSDPSocketSource *socket, GSSDPClient *client)
+socket_source_cb (GSSDPSocketSource *socket_source, GSSDPClient *client)
 {
-        int fd, type, len;
-        ssize_t bytes;
+        int type, len;
         char buf[BUF_SIZE], *end;
-        struct sockaddr_in addr;
-        socklen_t addr_size;
         SoupMessageHeaders *headers;
-        struct in_addr our_addr;
-        in_addr_t our_network;
+        GSocket *socket;
+        GSocketAddress *address = NULL;
+        gssize bytes;
+        GInetAddress *inetaddr;
+        char *ip_string;
+        guint16 port;
+        GError *error = NULL;
         in_addr_t recv_network;
+        in_addr_t our_network;
+        struct in_addr our_addr;
+        struct sockaddr_in addr;
 
-        /* Get FD */
-        fd = gssdp_socket_source_get_fd (socket);
-
-        /* Read data */
-        addr_size = sizeof (addr);
-        
-        bytes = recvfrom (fd,
-                          buf,
-                          BUF_SIZE - 1, /* Leave space for trailing \0 */
-                          MSG_TRUNC,
-                          (struct sockaddr *) &addr,
-                          &addr_size);
+        /* Get Socket */
+        socket = gssdp_socket_source_get_socket (socket_source);
+        bytes = g_socket_receive_from (socket,
+                                       &address,
+                                       buf,
+                                       BUF_SIZE - 1,
+                                       NULL,
+                                       &error);
         if (bytes == -1) {
-                g_warning ("Failed to read from socket: %d (%s)",
-                           errno,
-                           strerror (errno));
+                g_warning ("Failed to receive from socket: %s",
+                           error->message);
+                g_error_free (error);
 
                 return TRUE;
         }
@@ -748,6 +755,18 @@ socket_source_cb (GSSDPSocketSource *socket, GSSDPClient *client)
          * on this socket from a particular interface but AFAIK that is not
          * possible, at least not in a portable way.
          */
+
+        if (!g_socket_address_to_native (address,
+                                         &addr,
+                                         sizeof (struct sockaddr_in),
+                                         &error)) {
+                g_warning ("Could not convert address to native: %s",
+                           error->message);
+                g_error_free (error);
+
+                return TRUE;
+        }
+
         recv_network = inet_netof (addr.sin_addr);
         our_addr.s_addr = inet_addr (gssdp_client_get_host_ip (client));
         our_network = inet_netof (our_addr);
@@ -755,6 +774,7 @@ socket_source_cb (GSSDPSocketSource *socket, GSSDPClient *client)
                 return TRUE;
 
         if (bytes >= BUF_SIZE) {
+                g_object_unref (address);
                 g_warning ("Received packet of %u bytes, but the maximum "
                            "buffer size is %d. Packed dropped.",
                            (unsigned int) bytes, BUF_SIZE);
@@ -768,6 +788,7 @@ socket_source_cb (GSSDPSocketSource *socket, GSSDPClient *client)
         /* Find length */
         end = strstr (buf, "\r\n\r\n");
         if (!end) {
+                g_object_unref (address);
                 g_warning ("Received packet lacks \"\\r\\n\\r\\n\" sequence. "
                            "Packed dropped.");
 
@@ -793,24 +814,36 @@ socket_source_cb (GSSDPSocketSource *socket, GSSDPClient *client)
         }
         
         /* Emit signal if parsing succeeded */
+        inetaddr = g_inet_socket_address_get_address (
+                                        G_INET_SOCKET_ADDRESS (address));
+        ip_string = g_inet_address_to_string (inetaddr);
+        port = g_inet_socket_address_get_port (
+                                        G_INET_SOCKET_ADDRESS (address));
         if (type >= 0) {
                 g_signal_emit (client,
                                signals[MESSAGE_RECEIVED],
                                0,
-                               inet_ntoa (addr.sin_addr),
-                               ntohs (addr.sin_port),
+                               ip_string,
+                               port,
                                type,
                                headers);
         }
 
+        if (ip_string)
+                g_free (ip_string);
+
         if (headers)
                 soup_message_headers_free (headers);
 
+        g_object_unref (address);
+
         return TRUE;
 }
 
 static gboolean
-request_socket_source_cb (gpointer user_data)
+request_socket_source_cb (GIOChannel  *source,
+                          GIOCondition condition,
+                          gpointer     user_data)
 {
         GSSDPClient *client;
 
@@ -820,7 +853,9 @@ request_socket_source_cb (gpointer user_data)
 }
 
 static gboolean
-multicast_socket_source_cb (gpointer user_data)
+multicast_socket_source_cb (GIOChannel  *source,
+                            GIOCondition condition,
+                            gpointer     user_data)
 {
         GSSDPClient *client;
 
diff --git a/libgssdp/gssdp-socket-functions.c b/libgssdp/gssdp-socket-functions.c
new file mode 100644 (file)
index 0000000..0fe78de
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2010 Jens Georg <mail@jensge.org>
+ *
+ * Author: Jens Georg <mail@jensge.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+
+#ifdef G_OS_WIN32
+    #include <winsock2.h>
+    #include <ws2tcpip.h>
+    typedef int socklen_t;
+#else
+    #include <sys/socket.h>
+    #include <sys/types.h>
+    #include <netinet/in.h>
+    #include <arpa/inet.h>
+#endif
+
+#include "gssdp-error.h"
+#include "gssdp-socket-functions.h"
+
+static char*
+gssdp_socket_error_message (int error) {
+#ifdef G_OS_WIN32
+        return g_win32_error_message (error);
+#else
+        return g_strdup (g_strerror (error));
+#endif
+}
+
+static int
+gssdp_socket_errno () {
+#ifdef G_OS_WIN32
+        return WSAGetLastError ();
+#else
+        return errno;
+#endif
+}
+
+
+static gboolean
+gssdp_socket_option_set (GSocket    *socket,
+                         int         level,
+                         int         option,
+                         const void *optval,
+                         socklen_t   optlen,
+                         GError    **error) {
+        int res;
+
+        res = setsockopt (g_socket_get_fd (socket),
+                          level,
+                          option,
+                          optval,
+                          optlen);
+
+        if (res == -1) {
+                char *message;
+                int error_code;
+
+                error_code = gssdp_socket_errno ();
+                message = gssdp_socket_error_message (error_code);
+                g_set_error_literal (error,
+                                     GSSDP_ERROR,
+                                     GSSDP_ERROR_FAILED,
+                                     message);
+                g_free (message);
+        }
+
+        return res != -1;
+}
+
+gboolean
+gssdp_socket_enable_loop (GSocket *socket,
+                          gboolean enable,
+                          GError **error) {
+        return gssdp_socket_option_set (socket,
+                                        IPPROTO_IP,
+                                        IP_MULTICAST_LOOP,
+                                        (char *) &enable,
+                                        sizeof (enable),
+                                        error);
+}
+
+gboolean
+gssdp_socket_set_ttl (GSocket *socket,
+                      int      ttl,
+                      GError **error) {
+        return gssdp_socket_option_set (socket,
+                                        IPPROTO_IP,
+                                        IP_MULTICAST_TTL,
+                                        (char *) &ttl,
+                                        sizeof (ttl),
+                                        error);
+}
+
+gboolean
+gssdp_socket_enable_broadcast (GSocket *socket,
+                               gboolean enable,
+                               GError **error) {
+        return gssdp_socket_option_set (socket,
+                                        SOL_SOCKET,
+                                        SO_BROADCAST,
+                                        (char *) &enable,
+                                        sizeof (enable),
+                                        error);
+}
+
+gboolean
+gssdp_socket_mcast_interface_set (GSocket      *socket,
+                                  GInetAddress *iface_address,
+                                  GError      **error) {
+
+        const guint8 *address;
+        gsize native_size;
+
+        address = g_inet_address_to_bytes (iface_address);
+        native_size = g_inet_address_get_native_size (iface_address);
+
+        return gssdp_socket_option_set (socket,
+                                        IPPROTO_IP,
+                                        IP_MULTICAST_IF,
+                                        (char *) address,
+                                        native_size,
+                                        error);
+}
+
+/*
+ * Iface may be NULL if no special interface is wanted
+ */
+gboolean
+gssdp_socket_mcast_group_join (GSocket       *socket,
+                               GInetAddress  *group,
+                               GInetAddress  *iface,
+                               GError       **error) {
+        struct ip_mreq mreq;
+        GError *inner_error = NULL;
+        gboolean result;
+#ifdef G_OS_WIN32
+        GSocketAddress *local_address;
+#endif
+        if (group == NULL || ! G_IS_INET_ADDRESS (group)) {
+                g_set_error_literal (error,
+                                     GSSDP_ERROR,
+                                     GSSDP_ERROR_NO_IP_ADDRESS,
+                                     "Address is not a valid address");
+
+                return FALSE;
+        }
+
+        if (!g_inet_address_get_is_multicast (group)) {
+                char *address;
+
+                address = g_inet_address_to_string (group);
+                g_set_error (error,
+                             GSSDP_ERROR,
+                             GSSDP_ERROR_FAILED,
+                             "Address '%s' is not a multicast address",
+                             address);
+                g_free (address);
+
+                return FALSE;
+        }
+
+        if (g_inet_address_get_family (group) != G_SOCKET_FAMILY_IPV4) {
+                g_set_error_literal (error,
+                                     GSSDP_ERROR,
+                                     GSSDP_ERROR_FAILED,
+                                     "IPv6 not supported");
+
+                return FALSE;
+        }
+#ifdef G_OS_WIN32
+        /* On Window, it is only possible to join multicast groups on a bound
+         * socket
+         * Note: This test is valid on Windows only. On linux, local_addres
+         * will be the ANY address (0.0.0.0 for IPv4)
+         */
+        local_address = g_socket_get_local_address (socket, &inner_error);
+        if (local_address == NULL) {
+                g_set_error_literal (error,
+                                     GSSDP_ERROR,
+                                     GSSDP_ERROR_FAILED,
+                                     "Cannot join multicast group;"
+                                     "socket is not bound");
+
+                return FALSE;
+        }
+        g_object_unref (local_address);
+#endif
+
+        memset (&mreq, 0, sizeof (struct ip_mreq));
+        memcpy (&(mreq.imr_multiaddr),
+                g_inet_address_to_bytes (group),
+                g_inet_address_get_native_size (group));
+
+        /* if omitted, join will fail if there isn't an explicit multicast
+         * route or a default route
+         */
+        if (iface != NULL)
+                memcpy (&(mreq.imr_interface),
+                        g_inet_address_to_bytes (iface),
+                        g_inet_address_get_native_size (iface));
+
+        result = gssdp_socket_option_set (socket,
+                                          IPPROTO_IP,
+                                          IP_ADD_MEMBERSHIP,
+                                          (char *) &mreq,
+                                          sizeof (mreq),
+                                          &inner_error);
+        if (!result)
+                g_propagate_error (error, inner_error);
+
+        return result;
+}
+
+
diff --git a/libgssdp/gssdp-socket-functions.h b/libgssdp/gssdp-socket-functions.h
new file mode 100644 (file)
index 0000000..e1cda40
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 Jens Georg <mail@jensge.org>
+ *
+ * Author: Jens Georg <mail@jensge.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GSSDP_SOCKET_FUNCTIONS_H
+#define __GSSDP_SOCKET_FUNCTIONS_H
+
+#include <gio/gio.h>
+
+G_GNUC_INTERNAL gboolean
+gssdp_socket_enable_loop         (GSocket       *socket,
+                                  gboolean       enable,
+                                  GError       **error);
+G_GNUC_INTERNAL gboolean
+gssdp_socket_set_ttl             (GSocket       *socket,
+                                  int            ttl,
+                                  GError       **error);
+G_GNUC_INTERNAL gboolean
+gssdp_socket_enable_broadcast    (GSocket       *socket,
+                                  gboolean       enable,
+                                  GError       **error);
+G_GNUC_INTERNAL gboolean
+gssdp_socket_mcast_interface_set (GSocket       *socket,
+                                  GInetAddress  *iface_address,
+                                  GError       **error);
+G_GNUC_INTERNAL gboolean
+gssdp_socket_mcast_group_join    (GSocket       *socket,
+                                  GInetAddress  *group,
+                                  GInetAddress  *iface,
+                                  GError       **error);
+#endif
index 8922dd3..e52fe48 100644 (file)
@@ -1,10 +1,12 @@
 /* 
  * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd.
  * Copyright (C) 2009 Nokia Corporation, all rights reserved.
+ * Copyright (C) 2010 Jens Georg <mail@jensge.org>
  *
  * Author: Jorn Baayen <jorn@openedhand.com>
  *         Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
  *                               <zeeshan.ali@nokia.com>
+ *         Jens Georg <mail@jensge.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
 
 #include <config.h>
 #include <glib.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
 
+#include "gssdp-socket-functions.h"
 #include "gssdp-socket-source.h"
 #include "gssdp-protocol.h"
-
-struct _GSSDPSocketSource {
-        GSource source;
-
-        GPollFD poll_fd;
-};
-
-static gboolean
-gssdp_socket_source_prepare  (GSource    *source,
-                              int        *timeout);
-static gboolean
-gssdp_socket_source_check    (GSource    *source);
-static gboolean
-gssdp_socket_source_dispatch (GSource    *source,
-                              GSourceFunc callback,
-                              gpointer    user_data);
-static void
-gssdp_socket_source_finalize (GSource    *source);
-
-static const GSourceFuncs gssdp_socket_source_funcs = {
-        gssdp_socket_source_prepare,
-        gssdp_socket_source_check,
-        gssdp_socket_source_dispatch,
-        gssdp_socket_source_finalize
-};
+#include "gssdp-error.h"
 
 /**
  * gssdp_socket_source_new
@@ -68,208 +39,185 @@ static const GSourceFuncs gssdp_socket_source_funcs = {
  **/
 GSSDPSocketSource *
 gssdp_socket_source_new (GSSDPSocketSourceType type,
-                         const char           *host_ip)
+                         const char           *host_ip,
+                         GError              **error)
 {
-        GSource *source;
-        GSSDPSocketSource *socket_source;
-        struct sockaddr_in bind_addr;
-        struct in_addr iface_addr;
-        struct ip_mreq mreq;
-        gboolean boolean = TRUE;
-        guchar ttl = 4;
-        int res;
+        GSSDPSocketSource *socket_source = NULL;
+        GInetAddress *iface_address = NULL;
+        GSocketAddress *bind_address = NULL;
+        GInetAddress *group = NULL;
+        GError *inner_error = NULL;
+        GSocketFamily family;
+
+        iface_address = g_inet_address_new_from_string (host_ip);
+        if (iface_address == NULL) {
+                if (error != NULL) {
+                        *error = g_error_new (GSSDP_ERROR,
+                                              GSSDP_ERROR_FAILED,
+                                              "Invalid host ip: %s",
+                                              host_ip);
+                        inner_error = *error;
+                }
 
-        /* Create source */
-        source = g_source_new ((GSourceFuncs*)&gssdp_socket_source_funcs,
-                               sizeof (GSSDPSocketSource));
+                goto error;
+        }
 
-        socket_source = (GSSDPSocketSource *) source;
+        family = g_inet_address_get_family (iface_address);
+
+        if (family == G_SOCKET_FAMILY_IPV4)
+                group = g_inet_address_new_from_string (SSDP_ADDR);
+        else {
+                if (error != NULL) {
+                        *error = g_error_new_literal (GSSDP_ERROR,
+                                                      GSSDP_ERROR_FAILED,
+                                                      "IPv6 address");
+                        inner_error = *error;
+                }
 
-        /* Create socket */
-        socket_source->poll_fd.fd = socket (AF_INET,
-                                            SOCK_DGRAM,
-                                            IPPROTO_UDP);
-        if (socket_source->poll_fd.fd == -1)
                 goto error;
-        
-        socket_source->poll_fd.events = G_IO_IN | G_IO_ERR;
+        }
 
-        g_source_add_poll (source, &socket_source->poll_fd);
 
-        /* Enable broadcasting */
-        res = setsockopt (socket_source->poll_fd.fd, 
-                          SOL_SOCKET,
-                          SO_BROADCAST,
-                          &boolean,
-                          sizeof (boolean));
-        if (res == -1)
-                goto error;
+        /* Create source */
+        socket_source = g_slice_new0 (GSSDPSocketSource);
 
-        /* TTL */
-        res = setsockopt (socket_source->poll_fd.fd,
-                          IPPROTO_IP,
-                          IP_MULTICAST_TTL,
-                          &ttl,
-                          sizeof (ttl));
-        if (res == -1)
-                goto error;
+        /* Create socket */
+        socket_source->socket = g_socket_new (G_SOCKET_FAMILY_IPV4,
+                                              G_SOCKET_TYPE_DATAGRAM,
+                                              G_SOCKET_PROTOCOL_UDP,
+                                              &inner_error);
 
-        memset (&bind_addr, 0, sizeof (bind_addr));
-        bind_addr.sin_family = AF_INET;
+        if (!socket_source->socket) {
+                g_propagate_prefixed_error (error,
+                                            inner_error,
+                                            "Could not create socket");
 
-        res = inet_aton (host_ip, &iface_addr);
-        if (res == 0)
                 goto error;
+        }
+
+        /* Enable broadcasting */
+        if (!gssdp_socket_enable_broadcast (socket_source->socket,
+                                            TRUE,
+                                            &inner_error)) {
+                g_propagate_prefixed_error (error,
+                                            inner_error,
+                                            "Failed to enable broadcast");
+                goto error;
+        }
+
+        /* TTL */
+        if (!gssdp_socket_set_ttl (socket_source->socket,
+                                   4,
+                                   &inner_error)) {
+                g_propagate_prefixed_error (error,
+                                            inner_error,
+                                            "Failed to set TTL");
 
+        }
         /* Set up additional things according to the type of socket desired */
         if (type == GSSDP_SOCKET_SOURCE_TYPE_MULTICAST) {
-                /* Allow multiple sockets to use the same PORT number */
-                res = setsockopt (socket_source->poll_fd.fd,
-                                  SOL_SOCKET,
-#ifdef SO_REUSEPORT 
-                                  SO_REUSEPORT,
-#else
-                                  SO_REUSEADDR,
-#endif
-                                  &boolean,
-                                  sizeof (boolean));
-                if (res == -1)
-                        goto error;
-
                 /* Enable multicast loopback */
-                res = setsockopt (socket_source->poll_fd.fd,
-                                  IPPROTO_IP,
-                                  IP_MULTICAST_LOOP,
-                                  &boolean,
-                                  sizeof (boolean));
-                if (res == -1)
-                       goto error;
-
-                /* Set the interface */
-                res = setsockopt (socket_source->poll_fd.fd,
-                                  IPPROTO_IP,
-                                  IP_MULTICAST_IF,
-                                  &iface_addr,
-                                  sizeof (struct in_addr));
-                if (res == -1)
-                        goto error;
+                if (!gssdp_socket_enable_loop (socket_source->socket,
+                                               TRUE,
+                                               &inner_error)) {
+                        g_propagate_prefixed_error (
+                                        error,
+                                        inner_error,
+                                        "Failed to enable loop-back");
 
-                /* Subscribe to multicast channel */
-                res = inet_aton (SSDP_ADDR, &(mreq.imr_multiaddr));
-                if (res == 0)
                         goto error;
+                }
 
-                memcpy (&(mreq.imr_interface),
-                        &iface_addr,
-                        sizeof (struct in_addr));
+                if (!gssdp_socket_mcast_interface_set (socket_source->socket,
+                                                       iface_address,
+                                                       &inner_error)) {
+                        g_propagate_prefixed_error (
+                                        error,
+                                        inner_error,
+                                        "Failed to set multicast interface");
 
-                res = setsockopt (socket_source->poll_fd.fd,
-                                  IPPROTO_IP,
-                                  IP_ADD_MEMBERSHIP,
-                                  &mreq,
-                                  sizeof (mreq));
-                if (res == -1)
                         goto error;
+                }
 
-                bind_addr.sin_port = htons (SSDP_PORT);
-                res = inet_aton (SSDP_ADDR, &(bind_addr.sin_addr));
-                if (res == 0)
-                        goto error;
+#ifdef G_OS_WIN32
+                bind_address = g_inet_socket_address_new (iface_address,
+                                                          SSDP_PORT);
+#else
+                bind_address = g_inet_socket_address_new (group,
+                                                          SSDP_PORT);
+#endif
         } else {
-                bind_addr.sin_port = 0;
-                memcpy (&(bind_addr.sin_addr),
-                        &iface_addr,
-                        sizeof (struct in_addr));
+                bind_address = g_inet_socket_address_new (iface_address,
+                                                          SSDP_PORT);
         }
 
         /* Bind to requested port and address */
-        res = bind (socket_source->poll_fd.fd,
-                    (struct sockaddr *) &bind_addr,
-                    sizeof (bind_addr));
-        if (res == -1)
-                goto error;
-
-        return socket_source;
-
-error:
-        g_source_destroy (source);
-        
-        return NULL;
-}
-
-static gboolean
-gssdp_socket_source_prepare (GSource *source,
-                             int     *timeout)
-{
-        return FALSE;
-}
-
-static gboolean
-gssdp_socket_source_check (GSource *source)
-{
-        GSSDPSocketSource *socket_source;
+        if (!g_socket_bind (socket_source->socket,
+                            bind_address,
+                            TRUE,
+                            &inner_error)) {
+                g_propagate_prefixed_error (error,
+                                            inner_error,
+                                            "Failed to bind socket");
 
-        socket_source = (GSSDPSocketSource *) source;
-
-        return socket_source->poll_fd.revents & (G_IO_IN | G_IO_ERR);
-}
-
-static gboolean
-gssdp_socket_source_dispatch (GSource    *source,
-                              GSourceFunc callback,
-                              gpointer    user_data)
-{
-        GSSDPSocketSource *socket_source;
+                goto error;
+        }
 
-        socket_source = (GSSDPSocketSource *) source;
+        if (type == GSSDP_SOCKET_SOURCE_TYPE_MULTICAST) {
 
-        if (socket_source->poll_fd.revents & G_IO_IN) {
-                /* Ready to read */
-                if (callback)
-                        callback (user_data);
-        } else if (socket_source->poll_fd.revents & G_IO_ERR) {
-                /* Error */
-                int value;
-                socklen_t size_int;
+                 /* Subscribe to multicast channel */
+                if (!gssdp_socket_mcast_group_join (socket_source->socket,
+                                                    group,
+                                                    iface_address,
+                                                    &inner_error)) {
+                        char *address = g_inet_address_to_string (group);
+                        g_propagate_prefixed_error (error,
+                                                    inner_error,
+                                                    "Failed to join group %s",
+                                                    address);
+                        g_free (address);
 
-                value = EINVAL;
-                size_int = sizeof (int);
-                
-                /* Get errno from socket */
-                getsockopt (socket_source->poll_fd.fd,
-                            SOL_SOCKET,
-                            SO_ERROR,
-                            &value,
-                            &size_int);
+                        goto error;
+                }
+        }
 
-                g_warning ("Socket error %d received: %s",
-                           value,
-                           strerror (value));
+        socket_source->source = g_socket_create_source (socket_source->socket,
+                                                        G_IO_IN | G_IO_ERR,
+                                                        NULL);
+error:
+        if (iface_address != NULL)
+                g_object_unref (iface_address);
+        if (bind_address != NULL)
+                g_object_unref (bind_address);
+        if (group != NULL)
+                g_object_unref (group);
+        if (inner_error != NULL) {
+                if (error == NULL) {
+                        g_warning ("Failed to create socket source: %s",
+                                   inner_error->message);
+                        g_error_free (inner_error);
+                }
+
+                return NULL;
         }
 
-        return TRUE;
+        return socket_source;
 }
 
-static void
-gssdp_socket_source_finalize (GSource *source)
+GSocket *
+gssdp_socket_source_get_socket (GSSDPSocketSource *socket_source)
 {
-        GSSDPSocketSource *socket_source;
+        g_return_val_if_fail (socket_source != NULL, NULL);
 
-        socket_source = (GSSDPSocketSource *) source;
-        
-        /* Close the socket */
-        close (socket_source->poll_fd.fd);
+        return socket_source->socket;
 }
 
-/**
- * gssdp_socket_source_get_fd
- *
- * Return value: The socket's FD.
- **/
-int
-gssdp_socket_source_get_fd (GSSDPSocketSource *socket_source)
+void
+gssdp_socket_source_destroy (GSSDPSocketSource *socket_source)
 {
-        g_return_val_if_fail (socket_source != NULL, -1);
-        
-        return socket_source->poll_fd.fd;
+        g_return_if_fail (socket_source != NULL);
+        g_source_destroy (socket_source->source);
+        g_socket_close (socket_source->socket, NULL);
+        g_object_unref (socket_source->socket);
+        g_slice_free (GSSDPSocketSource, socket_source);
 }
index c8a3594..c5c7369 100644 (file)
@@ -1,7 +1,9 @@
 /* 
  * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd.
+ * Copyright (C) 2010 Jens Georg <mail@jensge.org>
  *
  * Author: Jorn Baayen <jorn@openedhand.com>
+ *         Jens Georg <mail@jensge.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
 #ifndef __GSSDP_SOCKET_SOURCE_H__
 #define __GSSDP_SOCKET_SOURCE_H__
 
+#include <gio/gio.h>
+
 G_BEGIN_DECLS
 
+struct _GSSDPSocketSource {
+        GSource    *source;
+        GSocket    *socket;
+};
+
 typedef struct _GSSDPSocketSource GSSDPSocketSource;
 
 typedef enum {
@@ -32,11 +41,14 @@ typedef enum {
 } GSSDPSocketSourceType;
 
 G_GNUC_INTERNAL GSSDPSocketSource *
-gssdp_socket_source_new    (GSSDPSocketSourceType type,
-                            const char           *host_ip);
-
-G_GNUC_INTERNAL int
-gssdp_socket_source_get_fd (GSSDPSocketSource    *socket_source);
+gssdp_socket_source_new        (GSSDPSocketSourceType  type,
+                                const char            *host_ip,
+                                GError               **error);
+G_GNUC_INTERNAL void
+gssdp_socket_source_destroy    (GSSDPSocketSource     *socket_source);
+
+G_GNUC_INTERNAL GSocket*
+gssdp_socket_source_get_socket (GSSDPSocketSource     *socket_source);
 
 G_END_DECLS