[gssdp/wip/pktinfo] WIP: Use pktinfo



commit 2ea8b947b23252857d261fafe42e164107748266
Author: Jens Georg <mail jensge org>
Date:   Mon Feb 3 10:42:10 2014 +0100

    WIP: Use pktinfo
    
    https://bugzilla.gnome.org/show_bug.cgi?id=711320
    
    Signed-off-by: Jens Georg <mail jensge org>

 configure.ac                      |   24 +++++++
 libgssdp/Makefile.am              |    5 ++
 libgssdp/gssdp-client.c           |  120 ++++++++++++++++++++++++++------
 libgssdp/gssdp-pktinfo-message.c  |  136 +++++++++++++++++++++++++++++++++++++
 libgssdp/gssdp-pktinfo-message.h  |   77 +++++++++++++++++++++
 libgssdp/gssdp-socket-functions.c |   24 +++++++
 libgssdp/gssdp-socket-functions.h |    5 ++
 libgssdp/gssdp-socket-source.c    |   10 +++
 8 files changed, 379 insertions(+), 22 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 768b5d4..7aec4dc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -131,6 +131,30 @@ esac
 AC_MSG_RESULT([$target_android])
 AM_CONDITIONAL(TARGET_ANDROID, [test $target_android = yes])
 
+dnl Check whether in_pktinfo is available
+AC_CHECK_TYPE(struct in_pktinfo,
+              [
+               HAVE_PKTINFO=yes
+               AC_DEFINE([HAVE_PKTINFO],[1],[Whether we have IP_PKTINFO available])
+              ],
+              [
+               HAVE_PKTINFO=no
+              ], [#include <netinet/ip.h>])
+AM_CONDITIONAL([HAVE_PKTINFO], [test $HAVE_PKTINFO = yes], [])
+
+dnl Check for SIOCGIFINDEX
+AC_MSG_CHECKING([for SIOCGIFINDEX])
+AC_TRY_COMPILE([#include <sys/ioctl.h>],
+               [int i = SIOCGIFINDEX],
+               [
+                HAVE_SIOCGIFINDEX=yes
+                AC_DEFINE([HAVE_SIOCGIFINDEX], [1], [Whether we have SIOCGIFINDEX])
+               ],
+               [
+                HAVE_SIOCGIFINDEX=no
+               ])
+AC_MSG_RESULT([$HAVE_SIOCGIFINDEX])
+
 GTK_DOC_CHECK([1.14], [--flavour no-tmpl])
 
 AC_CONFIG_FILES([
diff --git a/libgssdp/Makefile.am b/libgssdp/Makefile.am
index 470878a..63283a4 100644
--- a/libgssdp/Makefile.am
+++ b/libgssdp/Makefile.am
@@ -48,6 +48,11 @@ libgssdp_1_0_la_SOURCES = $(introspection_sources)   \
                          gssdp-socket-functions.h      \
                          $(BUILT_SOURCES)
 
+if HAVE_PKTINFO
+libgssdp_1_0_la_SOURCES += gssdp-pktinfo-message.c \
+                                                  gssdp-pktinfo-message.h
+endif
+
 libgssdp_1_0_la_LIBADD = $(LIBGSSDP_LIBS)
 
 if OS_WIN32
diff --git a/libgssdp/gssdp-client.c b/libgssdp/gssdp-client.c
index f24e57a..cbb482a 100644
--- a/libgssdp/gssdp-client.c
+++ b/libgssdp/gssdp-client.c
@@ -66,12 +66,19 @@ typedef unsigned long in_addr_t;
 #endif
 #include <libsoup/soup-headers.h>
 
+#ifdef HAVE_SIOCGIFINDEX
+#include <sys/ioctl.h>
+#endif
+
 #include "gssdp-client.h"
 #include "gssdp-client-private.h"
 #include "gssdp-error.h"
 #include "gssdp-socket-source.h"
 #include "gssdp-marshal.h"
 #include "gssdp-protocol.h"
+#ifdef HAVE_PKTINFO
+#include "gssdp-pktinfo-message.h"
+#endif
 
 #ifndef INET6_ADDRSTRLEN
 #define INET6_ADDRSTRLEN 46
@@ -99,8 +106,10 @@ G_DEFINE_TYPE_EXTENDED (GSSDPClient,
 struct _GSSDPNetworkDevice {
         char *iface_name;
         char *host_ip;
+        GInetAddress *host_addr;
         char *network;
         struct sockaddr_in mask;
+        gint index;
 };
 typedef struct _GSSDPNetworkDevice GSSDPNetworkDevice;
 
@@ -1015,18 +1024,25 @@ socket_source_cb (GSSDPSocketSource *socket_source, GSSDPClient *client)
         char *ip_string = NULL;
         guint16 port;
         GError *error = NULL;
-        in_addr_t our_addr;
-        in_addr_t mask;
-        struct sockaddr_in addr;
+        GInputVector vector;
+        GSocketControlMessage **messages;
+        gint num_messages;
+
+        vector.buffer = buf;
+        vector.size = BUF_SIZE;
 
         /* Get Socket */
         socket = gssdp_socket_source_get_socket (socket_source);
-        bytes = g_socket_receive_from (socket,
-                                       &address,
-                                       buf,
-                                       BUF_SIZE - 1,
-                                       NULL,
-                                       &error);
+        bytes = g_socket_receive_message (socket,
+                                          &address,
+                                          &vector,
+                                          1,
+                                          &messages,
+                                          &num_messages,
+                                          NULL,
+                                          NULL,
+                                          &error);
+
         if (bytes == -1) {
                 g_warning ("Failed to receive from socket: %s",
                            error->message);
@@ -1034,28 +1050,53 @@ socket_source_cb (GSSDPSocketSource *socket_source, GSSDPClient *client)
                 goto out;
         }
 
+#ifdef HAVE_PKTINFO
+        {
+                int i;
+                for (i = 0; i < num_messages; i++) {
+                        GSSDPPktinfoMessage *msg;
+                        if (!GSSDP_IS_PKTINFO_MESSAGE (messages[i]))
+                                continue;
+
+                        msg = GSSDP_PKTINFO_MESSAGE (messages[i]);
+                        if (!((gssdp_pktinfo_message_get_ifindex (msg) ==
+                                                        client->priv->device.index) &&
+                                                (g_inet_address_equal (gssdp_pktinfo_message_get_local_addr 
(msg),
+                                                                       client->priv->device.host_addr))))
+                                goto out;
+                        else
+                                break;
+                }
+        }
+#else
         /* We need the following lines to make sure the right client received
          * the packet. We won't need to do this if there was any way to tell
          * Mr. Unix that we are only interested in receiving multicast packets
          * on this socket from a particular interface but AFAIK that is not
          * possible, at least not in a portable way.
          */
+        {
+                struct sockaddr_in addr;
+                in_addr_t mask;
+                in_addr_t our_addr;
+                if (!g_socket_address_to_native (address,
+                                                 &addr,
+                                                 sizeof (struct sockaddr_in),
+                                                 &error)) {
+                        g_warning ("Could not convert address to native: %s",
+                                   error->message);
+
+                        goto out;
+                }
 
-        if (!g_socket_address_to_native (address,
-                                         &addr,
-                                         sizeof (struct sockaddr_in),
-                                         &error)) {
-                g_warning ("Could not convert address to native: %s",
-                           error->message);
-
-                goto out;
-        }
+                mask = client->priv->device.mask.sin_addr.s_addr;
+                our_addr = inet_addr (gssdp_client_get_host_ip (client));
 
-        mask = client->priv->device.mask.sin_addr.s_addr;
-        our_addr = inet_addr (gssdp_client_get_host_ip (client));
+                if ((addr.sin_addr.s_addr & mask) != (our_addr & mask))
+                        goto out;
 
-        if ((addr.sin_addr.s_addr & mask) != (our_addr & mask))
-                goto out;
+        }
+#endif
 
         if (bytes >= BUF_SIZE) {
                 g_warning ("Received packet of %u bytes, but the maximum "
@@ -1207,6 +1248,33 @@ extract_address_and_prefix (PIP_ADAPTER_UNICAST_ADDRESS  adapter,
 }
 #endif
 
+static int
+query_ifindex (const char *iface_name)
+{
+#ifdef HAVE_SIOCGIFINDEX
+        int fd;
+        int result;
+        struct ifreq ifr;
+
+        fd = socket (AF_INET, SOCK_STREAM, 0);
+        if (fd < 0)
+                return -1;
+
+        memset (&ifr, 0, sizeof(struct ifreq));
+        strcpy (ifr.ifr_ifrn.ifrn_name, iface_name);
+
+        result = ioctl (fd, SIOCGIFINDEX, (char *)&ifr);
+        close (fd);
+
+        if (result == 0)
+                return ifr.ifr_ifindex;
+        else
+                return -1;
+#else
+        return -1;
+#endif
+}
+
 /*
  * Get the host IP for the specified interface. If no interface is specified,
  * it gets the IP of the first up & running interface and sets @interface
@@ -1547,6 +1615,7 @@ success:
                 const char *p, *q;
                 struct sockaddr_in *s4, *s4_mask;
                 struct in_addr net_addr;
+                const guint8 *bytes;
 
                 ifa = ifaceptr->data;
 
@@ -1560,12 +1629,19 @@ success:
                                ip,
                                sizeof (ip));
                 device->host_ip = g_strdup (p);
+
+                bytes = (const guint8 *) &s4->sin_addr;
+                device->host_addr = g_inet_address_new_from_bytes
+                                        (bytes, G_SOCKET_FAMILY_IPV4);
+
                 s4_mask = (struct sockaddr_in *) ifa->ifa_netmask;
                 memcpy (&(device->mask), s4_mask, sizeof (struct sockaddr_in));
                 net_addr.s_addr = (in_addr_t) s4->sin_addr.s_addr &
                                   (in_addr_t) s4_mask->sin_addr.s_addr;
                 q = inet_ntop (AF_INET, &net_addr, net, sizeof (net));
 
+                device->index = query_ifindex (ifa->ifa_name);
+
                 if (device->iface_name == NULL)
                         device->iface_name = g_strdup (ifa->ifa_name);
                 if (device->network == NULL)
diff --git a/libgssdp/gssdp-pktinfo-message.c b/libgssdp/gssdp-pktinfo-message.c
new file mode 100644
index 0000000..8f834a5
--- /dev/null
+++ b/libgssdp/gssdp-pktinfo-message.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 Jens Georg.
+ *
+ * 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <netinet/ip.h>
+
+#include "gssdp-pktinfo-message.h"
+
+G_DEFINE_TYPE (GSSDPPktinfoMessage,
+               gssdp_pktinfo_message,
+               G_TYPE_SOCKET_CONTROL_MESSAGE)
+
+struct _GSSDPPktinfoMessagePrivate
+{
+        GInetAddress *pkt_addr;
+        GInetAddress *iface_addr;
+        gint          index;
+};
+
+static gsize
+gssdp_pktinfo_message_get_size (GSocketControlMessage *msg)
+{
+    return 0;
+}
+
+static int
+gssdp_pktinfo_message_get_level (GSocketControlMessage *msg)
+{
+        return IPPROTO_IP;
+}
+
+static int
+gssdp_pktinfo_message_get_msg_type (GSocketControlMessage *msg)
+{
+        return IP_PKTINFO;
+}
+
+static GSocketControlMessage *
+gssdp_pktinfo_message_deserialize (int      level,
+                                   int      type,
+                                   gsize    size,
+                                   gpointer data)
+{
+        GSocketControlMessage *message;
+        GInetAddress *addr, *dst;
+        struct in_pktinfo *info = (struct in_pktinfo *) data;
+        const guint8 *bytes;
+
+        if (level != IPPROTO_IP || type != IP_PKTINFO)
+            return NULL;
+
+        bytes = (const guint8 *)&(info->ipi_addr.s_addr);
+        addr = g_inet_address_new_from_bytes(bytes, G_SOCKET_FAMILY_IPV4);
+
+        bytes = (const guint8 *)&(info->ipi_spec_dst.s_addr);
+        dst = g_inet_address_new_from_bytes (bytes, G_SOCKET_FAMILY_IPV4);
+
+        message = gssdp_pktinfo_message_new (addr, dst, info->ipi_ifindex);
+
+        return message;
+}
+
+static void
+gssdp_pktinfo_message_init (GSSDPPktinfoMessage *self)
+{
+        self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+                                                  GSSDP_TYPE_PKTINFO_MESSAGE,
+                                                  GSSDPPktinfoMessagePrivate);
+}
+
+static void
+gssdp_pktinfo_message_class_init (GSSDPPktinfoMessageClass *klass)
+{
+        GSocketControlMessageClass *scm_class =
+                G_SOCKET_CONTROL_MESSAGE_CLASS (klass);
+
+        g_type_class_add_private (klass, sizeof (GSSDPPktinfoMessagePrivate));
+
+        scm_class->get_size = gssdp_pktinfo_message_get_size;
+        scm_class->get_level = gssdp_pktinfo_message_get_level;
+        scm_class->get_type = gssdp_pktinfo_message_get_msg_type;
+        scm_class->deserialize = gssdp_pktinfo_message_deserialize;
+}
+
+GSocketControlMessage *
+gssdp_pktinfo_message_new (GInetAddress *addr, GInetAddress *dst, gint ifindex)
+{
+        GSSDPPktinfoMessage *msg;
+
+        msg = GSSDP_PKTINFO_MESSAGE (g_object_new (GSSDP_TYPE_PKTINFO_MESSAGE,
+                                                   NULL));
+
+        /* FIXME: Use properties */
+        msg->priv->pkt_addr = addr;
+        msg->priv->iface_addr = dst;
+        msg->priv->index = ifindex;
+
+        return G_SOCKET_CONTROL_MESSAGE (msg);
+}
+
+gint
+gssdp_pktinfo_message_get_ifindex (GSSDPPktinfoMessage *message)
+{
+        g_return_val_if_fail (GSSDP_IS_PKTINFO_MESSAGE (message), -1);
+
+        return message->priv->index;
+}
+
+GInetAddress *
+gssdp_pktinfo_message_get_local_addr (GSSDPPktinfoMessage *message)
+{
+        return message->priv->iface_addr;
+}
+
+GInetAddress *
+gssdp_pktinfo_message_get_pkt_addr (GSSDPPktinfoMessage *message)
+{
+        return message->priv->pkt_addr;
+}
diff --git a/libgssdp/gssdp-pktinfo-message.h b/libgssdp/gssdp-pktinfo-message.h
new file mode 100644
index 0000000..599898a
--- /dev/null
+++ b/libgssdp/gssdp-pktinfo-message.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GSSDP_PKTINFO_MESSAGE_H__
+#define __GSSDP_PKTINFO_MESSAGE_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+G_GNUC_INTERNAL GType
+gssdp_pktinfo_message_get_type (void) G_GNUC_CONST;
+
+#define GSSDP_TYPE_PKTINFO_MESSAGE (gssdp_pktinfo_message_get_type())
+#define GSSDP_PKTINFO_MESSAGE(obj) \
+                            (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+                             GSSDP_TYPE_PKTINFO_MESSAGE, \
+                             GSSDPPktinfoMessage))
+#define GSSDP_PKTINFO_MESAGE_CLASS(klass) \
+                            (G_TYPE_CHECK_CLASS_CAST ((klass), \
+                             GSSDP_TYPE_PKTINFO_MESSAGE, \
+                             GSSDPPktinfoClass))
+#define GSSDP_IS_PKTINFO_MESSAGE(obj) \
+                            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+                             GSSDP_TYPE_PKTINFO_MESSAGE))
+#define GSSDP_IS_PKTINFO_MESSAGE_CLASS(klass) \
+                            (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+                             GSSDP_TYPE_PKTINFO_MESSAGE))
+#define GSSDP_PKTINFO_MESSAGE_GET_CLASS(obj) \
+                            (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+                             GSSDP_TYPE_PKTINFO_MESSAGE, \
+                             GSSDPPktinfoMessageClass))
+
+typedef struct _GSSDPPktinfoMessagePrivate GSSDPPktinfoMessagePrivate;
+typedef struct _GSSDPPktInfoMessage GSSDPPktinfoMessage;
+typedef struct _GSSDPPktinfoMessageClass GSSDPPktinfoMessageClass;
+
+struct _GSSDPPktInfoMessage {
+        GSocketControlMessage parent;
+        GSSDPPktinfoMessagePrivate *priv;
+};
+
+struct _GSSDPPktinfoMessageClass {
+        GSocketControlMessageClass parent_class;
+};
+
+G_GNUC_INTERNAL GSocketControlMessage *
+gssdp_pktinfo_message_new (GInetAddress *addr, GInetAddress *dst, gint ifindex);
+
+G_GNUC_INTERNAL gint
+gssdp_pktinfo_message_get_ifindex (GSSDPPktinfoMessage *message);
+
+G_GNUC_INTERNAL GInetAddress *
+gssdp_pktinfo_message_get_local_addr (GSSDPPktinfoMessage *message);
+
+G_GNUC_INTERNAL GInetAddress *
+gssdp_pktinfo_message_get_pkt_addr (GSSDPPktinfoMessage *message);
+
+#endif /* __GSSDP_PKTINFO_MESSAGE_H__ */
diff --git a/libgssdp/gssdp-socket-functions.c b/libgssdp/gssdp-socket-functions.c
index e1ffaaa..f1f3cd4 100644
--- a/libgssdp/gssdp-socket-functions.c
+++ b/libgssdp/gssdp-socket-functions.c
@@ -37,6 +37,7 @@
 
 #include "gssdp-error.h"
 #include "gssdp-socket-functions.h"
+#include "gssdp-pktinfo-message.h"
 
 static char*
 gssdp_socket_error_message (int error) {
@@ -132,3 +133,26 @@ gssdp_socket_reuse_address (GSocket *socket,
 #endif
         return TRUE;
 }
+
+gboolean
+gssdp_socket_enable_info         (GSocket *socket,
+                                  gboolean enable,
+                                  GError **error)
+{
+        /* Register the type so g_socket_control_message_will() will find it */
+        g_object_unref (g_object_new (GSSDP_TYPE_PKTINFO_MESSAGE, NULL));
+#if 1
+    return gssdp_socket_option_set (socket,
+                                    IPPROTO_IP,
+                                    IP_PKTINFO,
+                                    (char *) &enable,
+                                    sizeof (enable),
+                                    error);
+#else
+    __GSSDP_UNUSED (socket);
+    __GSSDP_UNUSED (enable);
+    __GSSDP_UNUSED (error);
+
+    return TRUE;
+#endif
+}
\ No newline at end of file
diff --git a/libgssdp/gssdp-socket-functions.h b/libgssdp/gssdp-socket-functions.h
index 14f0650..68109a2 100644
--- a/libgssdp/gssdp-socket-functions.h
+++ b/libgssdp/gssdp-socket-functions.h
@@ -32,4 +32,9 @@ G_GNUC_INTERNAL gboolean
 gssdp_socket_reuse_address       (GSocket *socket,
                                   gboolean enable,
                                   GError **error);
+
+G_GNUC_INTERNAL gboolean
+gssdp_socket_enable_info         (GSocket *socket,
+                                  gboolean enable,
+                                  GError **error);
 #endif
diff --git a/libgssdp/gssdp-socket-source.c b/libgssdp/gssdp-socket-source.c
index fe5f3df..ed412fc 100644
--- a/libgssdp/gssdp-socket-source.c
+++ b/libgssdp/gssdp-socket-source.c
@@ -220,6 +220,16 @@ gssdp_socket_source_do_init (GInitable                   *initable,
         /* Enable broadcasting */
         g_socket_set_broadcast (self->priv->socket, TRUE);
 
+        if (!gssdp_socket_enable_info (self->priv->socket,
+                                       TRUE,
+                                       &inner_error)) {
+                g_propagate_prefixed_error (error,
+                                            inner_error,
+                                            "Failed to enable info messages");
+
+                goto error;
+        }
+
         /* TTL */
         if (!self->priv->ttl)
                 /* UDA/1.0 says 4, UDA/1.1 says 2 */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]