diff options
author | Jens Georg <mail@jensge.org> | 2014-02-03 10:42:10 +0100 |
---|---|---|
committer | Jens Georg <mail@jensge.org> | 2014-05-04 13:41:22 +0200 |
commit | 878ab1d340cdbddecaf90646699298ecd3ae96dc (patch) | |
tree | 60a72381f620b5e9e2b3265069cc90e86f85ca6b | |
parent | c12b9f3764a46a4ea52a85a8dc4507585a9b8e21 (diff) | |
download | gssdp-878ab1d340cdbddecaf90646699298ecd3ae96dc.tar.gz |
Use pktinfo on Linux to determine source if
For other systems we use the old approach of comparing networks.
https://bugzilla.gnome.org/show_bug.cgi?id=711320
Signed-off-by: Jens Georg <mail@jensge.org>
-rw-r--r-- | configure.ac | 24 | ||||
-rw-r--r-- | libgssdp/Makefile.am | 5 | ||||
-rw-r--r-- | libgssdp/gssdp-client.c | 120 | ||||
-rw-r--r-- | libgssdp/gssdp-pktinfo-message.c | 244 | ||||
-rw-r--r-- | libgssdp/gssdp-pktinfo-message.h | 77 | ||||
-rw-r--r-- | libgssdp/gssdp-socket-functions.c | 26 | ||||
-rw-r--r-- | libgssdp/gssdp-socket-functions.h | 5 | ||||
-rw-r--r-- | libgssdp/gssdp-socket-source.c | 10 |
8 files changed, 489 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 2410ccc..587e912 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..f35baa8 --- /dev/null +++ b/libgssdp/gssdp-pktinfo-message.c @@ -0,0 +1,244 @@ +/* + * 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; +}; + +enum { + PROP_0, + PROP_PKT_ADDR, + PROP_IFACE_ADDR, + PROP_INDEX +}; + +static gsize +gssdp_pktinfo_message_get_size (GSocketControlMessage *msg) +{ + return sizeof (struct in_pktinfo); +} + +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 void +gssdp_pktinfo_message_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GSSDPPktinfoMessage *self; + + self = GSSDP_PKTINFO_MESSAGE (object); + switch (property_id) + { + case PROP_IFACE_ADDR: + g_value_set_object (value, self->priv->iface_addr); + break; + case PROP_INDEX: + g_value_set_uint (value, self->priv->index); + break; + case PROP_PKT_ADDR: + g_value_set_object (value, self->priv->pkt_addr); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gssdp_pktinfo_message_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GSSDPPktinfoMessage *self; + + self = GSSDP_PKTINFO_MESSAGE (object); + switch (property_id) + { + case PROP_IFACE_ADDR: + self->priv->iface_addr = g_value_get_object (value); + break; + case PROP_INDEX: + self->priv->index = g_value_get_int (value); + break; + case PROP_PKT_ADDR: + self->priv->pkt_addr = g_value_get_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gssdp_pktinfo_dispose (GObject *object) +{ + GSSDPPktinfoMessage *self = GSSDP_PKTINFO_MESSAGE (object); + + g_clear_object (&self->priv->iface_addr); + g_clear_object (&self->priv->pkt_addr); +} + +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); + + GObjectClass *object_class = G_OBJECT_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; + + object_class->get_property = gssdp_pktinfo_message_get_property; + object_class->set_property = gssdp_pktinfo_message_set_property; + object_class->dispose = gssdp_pktinfo_dispose; + + g_object_class_install_property + (object_class, + PROP_IFACE_ADDR, + g_param_spec_object ("iface-address", + "iface-address", + "IP v4 Address of the interface this packet was received on", + G_TYPE_INET_ADDRESS, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (object_class, + PROP_INDEX, + g_param_spec_uint ("index", + "index", + "Network interface index", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (object_class, + PROP_IFACE_ADDR, + g_param_spec_object ("pkt-address", + "pkt-address", + "IP v4 destination Address of the packet", + G_TYPE_INET_ADDRESS, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} + +GSocketControlMessage * +gssdp_pktinfo_message_new (GInetAddress *addr, GInetAddress *dst, gint ifindex) +{ + GSSDPPktinfoMessage *msg; + + msg = GSSDP_PKTINFO_MESSAGE ( + g_object_new (GSSDP_TYPE_PKTINFO_MESSAGE, + "iface-address", dst, + "index", ifindex, + "pkt-address", addr, + NULL)); + + 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..3ebec10 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,28 @@ gssdp_socket_reuse_address (GSocket *socket, #endif return TRUE; } + +gboolean +gssdp_socket_enable_info (GSocket *socket, + gboolean enable, + GError **error) +{ +#ifdef HAVE_PKTINFO + /* Register the type so g_socket_control_message_deserialize() will + * find it */ + g_object_unref (g_object_new (GSSDP_TYPE_PKTINFO_MESSAGE, NULL)); + + 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 +} 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 2057c57..b6e9772 100644 --- a/libgssdp/gssdp-socket-source.c +++ b/libgssdp/gssdp-socket-source.c @@ -221,6 +221,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 */ |