[glib] gio: add g_socket_join_multicast_group_ssm (IGMPv3 SSM)
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] gio: add g_socket_join_multicast_group_ssm (IGMPv3 SSM)
- Date: Mon, 16 Oct 2017 10:15:13 +0000 (UTC)
commit ea725a64147b6af10166d56da1950a9a86d62610
Author: Julien Isorce <julien isorce gmail com>
Date: Thu Nov 27 00:04:25 2014 +0000
gio: add g_socket_join_multicast_group_ssm (IGMPv3 SSM)
It adds support for source-specific multicast IGMPv3.
Allow receiving data only from a specified source when joining
a multicast group.
g_socket_join_multicast_group_ssm can be called multiple times
to allow receiving data from more than one source.
Support IPv4 and IPv6.
Fixes https://bugzilla.gnome.org/show_bug.cgi?id=740791
config.h.meson | 3 +
configure.ac | 16 ++
docs/reference/gio/gio-sections.txt | 2 +
gio/gsocket.c | 287 +++++++++++++++++++++++++++++++++++
gio/gsocket.h | 12 ++
gio/meson.build | 12 ++
6 files changed, 332 insertions(+), 0 deletions(-)
---
diff --git a/config.h.meson b/config.h.meson
index 6f0b7e4..c67e179 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -364,6 +364,9 @@
/* Define if you have the 'sig_atomic_t' type. */
#mesondefine HAVE_SIG_ATOMIC_T
+/* Define if there is support for the ioctl request SIOCGIFADDR. */
+#mesondefine HAVE_SIOCGIFADDR
+
/* Define to 1 if you have the `snprintf' function. */
#mesondefine HAVE_SNPRINTF
diff --git a/configure.ac b/configure.ac
index c0825d7..9cd1510 100644
--- a/configure.ac
+++ b/configure.ac
@@ -951,6 +951,22 @@ fi
AC_CHECK_FUNCS(endservent if_nametoindex if_indextoname sendmmsg recvmmsg)
+AC_MSG_CHECKING([for SIOCGIFADDR])
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+ [[
+ #include <sys/ioctl.h>
+ #include <net/if.h>
+ ]],
+ [[
+ struct ifreq ifr;
+ ioctl(0, SIOCGIFADDR, &ifr);
+ ]])], [
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_SIOCGIFADDR, 1, [SIOCGIFADDR is available])
+ ], [
+ AC_MSG_RESULT(no)
+])
+
AS_IF([test $glib_native_win32 = yes], [
# <wspiapi.h> in the Windows SDK and in mingw-w64 has wrappers for
# inline workarounds for getaddrinfo, getnameinfo and freeaddrinfo if
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index c21d71e..484ef62 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -2095,6 +2095,8 @@ g_socket_get_credentials
<SUBSECTION>
g_socket_join_multicast_group
g_socket_leave_multicast_group
+g_socket_join_multicast_group_ssm
+g_socket_leave_multicast_group_ssm
g_socket_get_multicast_loopback
g_socket_set_multicast_loopback
g_socket_get_multicast_ttl
diff --git a/gio/gsocket.c b/gio/gsocket.c
index 2d4669b..c4147fb 100644
--- a/gio/gsocket.c
+++ b/gio/gsocket.c
@@ -44,6 +44,10 @@
# include <sys/ioctl.h>
#endif
+#ifdef HAVE_SIOCGIFADDR
+#include <net/if.h>
+#endif
+
#ifdef HAVE_SYS_FILIO_H
# include <sys/filio.h>
#endif
@@ -59,6 +63,7 @@
#include "gdatagrambased.h"
#include "gioenumtypes.h"
#include "ginetaddress.h"
+#include "ginetsocketaddress.h"
#include "ginitable.h"
#include "gioerror.h"
#include "gioenums.h"
@@ -2272,6 +2277,9 @@ g_socket_multicast_group_operation (GSocket *socket,
* in RFC 4604 is used. Note that on older platforms this may fail
* with a %G_IO_ERROR_NOT_SUPPORTED error.
*
+ * To bind to a given source-specific multicast address, use
+ * g_socket_join_multicast_group_ssm() instead.
+ *
* Returns: %TRUE on success, %FALSE on error.
*
* Since: 2.32
@@ -2301,6 +2309,9 @@ g_socket_join_multicast_group (GSocket *socket,
* @socket remains bound to its address and port, and can still receive
* unicast messages after calling this.
*
+ * To unbind to a given source-specific multicast address, use
+ * g_socket_leave_multicast_group_ssm() instead.
+ *
* Returns: %TRUE on success, %FALSE on error.
*
* Since: 2.32
@@ -2315,6 +2326,282 @@ g_socket_leave_multicast_group (GSocket *socket,
return g_socket_multicast_group_operation (socket, group, source_specific, iface, FALSE, error);
}
+static gboolean
+g_socket_multicast_group_operation_ssm (GSocket *socket,
+ GInetAddress *group,
+ GInetAddress *source_specific,
+ const gchar *iface,
+ gboolean join_group,
+ GError **error)
+{
+ gint result;
+
+ g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
+ g_return_val_if_fail (socket->priv->type == G_SOCKET_TYPE_DATAGRAM, FALSE);
+ g_return_val_if_fail (G_IS_INET_ADDRESS (group), FALSE);
+ g_return_val_if_fail (iface == NULL || *iface != '\0', FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ if (!source_specific)
+ {
+ return g_socket_multicast_group_operation (socket, group, FALSE, iface,
+ join_group, error);
+ }
+
+ if (!check_socket (socket, error))
+ return FALSE;
+
+ switch (g_inet_address_get_family (group))
+ {
+ case G_SOCKET_FAMILY_INVALID:
+ case G_SOCKET_FAMILY_UNIX:
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ join_group ?
+ _("Error joining multicast group: %s") :
+ _("Error leaving multicast group: %s"),
+ _("Unsupported socket family"));
+ return FALSE;
+ }
+ break;
+
+ case G_SOCKET_FAMILY_IPV4:
+ {
+#ifdef IP_ADD_SOURCE_MEMBERSHIP
+ gint optname;
+ struct ip_mreq_source mc_req_src;
+
+ if (g_inet_address_get_family (source_specific) !=
+ G_SOCKET_FAMILY_IPV4)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ join_group ?
+ _("Error joining multicast group: %s") :
+ _("Error leaving multicast group: %s"),
+ _("source-specific not an IPv4 address"));
+ return FALSE;
+ }
+
+ memset (&mc_req_src, 0, sizeof (mc_req_src));
+
+ /* By default use the default IPv4 multicast interface. */
+ mc_req_src.imr_interface.s_addr = g_htonl (INADDR_ANY);
+
+ if (iface)
+ {
+#if defined(G_OS_WIN32) && defined (HAVE_IF_NAMETOINDEX)
+ guint iface_index = if_nametoindex (iface);
+ if (iface_index == 0)
+ {
+ int errsv = errno;
+
+ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
+ _("Interface not found: %s"), g_strerror (errsv));
+ return FALSE;
+ }
+ /* (0.0.0.iface_index) only works on Windows. */
+ mc_req_src.imr_interface.s_addr = g_htonl (iface_index);
+#elif defined (HAVE_SIOCGIFADDR)
+ int ret;
+ struct ifreq ifr;
+ struct sockaddr_in *iface_addr;
+ size_t if_name_len = strlen (iface);
+
+ memset (&ifr, 0, sizeof (ifr));
+
+ if (if_name_len >= sizeof (ifr.ifr_name))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FILENAME_TOO_LONG,
+ _("Interface name too long"));
+ return FALSE;
+ }
+
+ memcpy (ifr.ifr_name, iface, if_name_len);
+
+ /* Get the IPv4 address of the given network interface name. */
+ ret = ioctl (socket->priv->fd, SIOCGIFADDR, &ifr);
+ if (ret < 0)
+ {
+ int errsv = errno;
+
+ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
+ _("Interface not found: %s"), g_strerror (errsv));
+ return FALSE;
+ }
+
+ iface_addr = (struct sockaddr_in *) &ifr.ifr_addr;
+ mc_req_src.imr_interface.s_addr = iface_addr->sin_addr.s_addr;
+#endif /* defined(G_OS_WIN32) && defined (HAVE_IF_NAMETOINDEX) */
+ }
+ memcpy (&mc_req_src.imr_multiaddr, g_inet_address_to_bytes (group),
+ g_inet_address_get_native_size (group));
+ memcpy (&mc_req_src.imr_sourceaddr,
+ g_inet_address_to_bytes (source_specific),
+ g_inet_address_get_native_size (source_specific));
+
+ optname =
+ join_group ? IP_ADD_SOURCE_MEMBERSHIP : IP_DROP_SOURCE_MEMBERSHIP;
+ result = setsockopt (socket->priv->fd, IPPROTO_IP, optname,
+ &mc_req_src, sizeof (mc_req_src));
+#else
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ join_group ?
+ _("Error joining multicast group: %s") :
+ _("Error leaving multicast group: %s"),
+ _("No support for IPv4 source-specific multicast"));
+ return FALSE;
+#endif /* IP_ADD_SOURCE_MEMBERSHIP */
+ }
+ break;
+
+ case G_SOCKET_FAMILY_IPV6:
+ {
+#ifdef MCAST_JOIN_SOURCE_GROUP
+ gboolean res;
+ gint optname;
+ struct group_source_req mc_req_src;
+ GSocketAddress *saddr_group, *saddr_source_specific;
+ guint iface_index = 0;
+
+#if defined (HAVE_IF_NAMETOINDEX)
+ if (iface)
+ {
+ iface_index = if_nametoindex (iface);
+ if (iface_index == 0)
+ {
+ int errsv = errno;
+
+ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
+ _("Interface not found: %s"), g_strerror (errsv));
+ return FALSE;
+ }
+ }
+#endif /* defined (HAVE_IF_NAMETOINDEX) */
+ mc_req_src.gsr_interface = iface_index;
+
+ saddr_group = g_inet_socket_address_new (group, 0);
+ res = g_socket_address_to_native (saddr_group, &mc_req_src.gsr_group,
+ sizeof (mc_req_src.gsr_group),
+ error);
+ g_object_unref (saddr_group);
+ if (!res)
+ return FALSE;
+
+ saddr_source_specific = g_inet_socket_address_new (source_specific, 0);
+ res = g_socket_address_to_native (saddr_source_specific,
+ &mc_req_src.gsr_source,
+ sizeof (mc_req_src.gsr_source),
+ error);
+ g_object_unref (saddr_source_specific);
+
+ if (!res)
+ return FALSE;
+
+ optname =
+ join_group ? MCAST_JOIN_SOURCE_GROUP : MCAST_LEAVE_SOURCE_GROUP;
+ result = setsockopt (socket->priv->fd, IPPROTO_IPV6, optname,
+ &mc_req_src, sizeof (mc_req_src));
+#else
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ join_group ?
+ _("Error joining multicast group: %s") :
+ _("Error leaving multicast group: %s"),
+ _("No support for IPv6 source-specific multicast"));
+ return FALSE;
+#endif /* MCAST_JOIN_SOURCE_GROUP */
+ }
+ break;
+
+ default:
+ g_return_val_if_reached (FALSE);
+ }
+
+ if (result < 0)
+ {
+ int errsv = get_socket_errno ();
+
+ g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
+ join_group ?
+ _("Error joining multicast group: %s") :
+ _("Error leaving multicast group: %s"),
+ socket_strerror (errsv));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * g_socket_join_multicast_group_ssm:
+ * @socket: a #GSocket.
+ * @group: a #GInetAddress specifying the group address to join.
+ * @source_specific: (nullable): a #GInetAddress specifying the
+ * source-specific multicast address or %NULL to ignore.
+ * @iface: (nullable): Name of the interface to use, or %NULL
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Registers @socket to receive multicast messages sent to @group.
+ * @socket must be a %G_SOCKET_TYPE_DATAGRAM socket, and must have
+ * been bound to an appropriate interface and port with
+ * g_socket_bind().
+ *
+ * If @iface is %NULL, the system will automatically pick an interface
+ * to bind to based on @group.
+ *
+ * If @source_specific is not %NULL, use source-specific multicast as
+ * defined in RFC 4604. Note that on older platforms this may fail
+ * with a %G_IO_ERROR_NOT_SUPPORTED error.
+ *
+ * Note that this function can be called multiple times for the same
+ * @group with different @source_specific in order to receive multicast
+ * packets from more than one source.
+ *
+ * Returns: %TRUE on success, %FALSE on error.
+ *
+ * Since: 2.56
+ */
+gboolean
+g_socket_join_multicast_group_ssm (GSocket *socket,
+ GInetAddress *group,
+ GInetAddress *source_specific,
+ const gchar *iface,
+ GError **error)
+{
+ return g_socket_multicast_group_operation_ssm (socket, group,
+ source_specific, iface, TRUE, error);
+}
+
+/**
+ * g_socket_leave_multicast_group_ssm:
+ * @socket: a #GSocket.
+ * @group: a #GInetAddress specifying the group address to leave.
+ * @source_specific: (nullable): a #GInetAddress specifying the
+ * source-specific multicast address or %NULL to ignore.
+ * @iface: (nullable): Name of the interface to use, or %NULL
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Removes @socket from the multicast group defined by @group, @iface,
+ * and @source_specific (which must all have the same values they had
+ * when you joined the group).
+ *
+ * @socket remains bound to its address and port, and can still receive
+ * unicast messages after calling this.
+ *
+ * Returns: %TRUE on success, %FALSE on error.
+ *
+ * Since: 2.56
+ */
+gboolean
+g_socket_leave_multicast_group_ssm (GSocket *socket,
+ GInetAddress *group,
+ GInetAddress *source_specific,
+ const gchar *iface,
+ GError **error)
+{
+ return g_socket_multicast_group_operation_ssm (socket, group,
+ source_specific, iface, FALSE, error);
+}
+
/**
* g_socket_speaks_ipv4:
* @socket: a #GSocket
diff --git a/gio/gsocket.h b/gio/gsocket.h
index 613c8dd..a65cbc2 100644
--- a/gio/gsocket.h
+++ b/gio/gsocket.h
@@ -157,6 +157,18 @@ gboolean g_socket_leave_multicast_group (GSocket
gboolean source_specific,
const gchar *iface,
GError **error);
+GLIB_AVAILABLE_IN_2_56
+gboolean g_socket_join_multicast_group_ssm (GSocket *socket,
+ GInetAddress *group,
+ GInetAddress *source_specific,
+ const gchar *iface,
+ GError **error);
+GLIB_AVAILABLE_IN_2_56
+gboolean g_socket_leave_multicast_group_ssm (GSocket *socket,
+ GInetAddress *group,
+ GInetAddress *source_specific,
+ const gchar *iface,
+ GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_socket_connect (GSocket *socket,
GSocketAddress *address,
diff --git a/gio/meson.build b/gio/meson.build
index 87f0861..4a10d49 100644
--- a/gio/meson.build
+++ b/gio/meson.build
@@ -93,6 +93,18 @@ if host_system != 'windows'
name : 'struct ip_mreqn')
glib_conf.set('HAVE_IP_MREQN', '/**/')
endif
+
+ if cc.compiles('''#include <sys/ioctl.h>
+ #include <net/if.h>
+ int main (int argc, char ** argv) {
+ struct ifreq ifr;
+ ioctl(0, SIOCGIFADDR, &ifr);
+ return 0;
+ }''',
+ name : 'ioctl with request SIOCGIFADDR')
+ glib_conf.set('HAVE_SIOCGIFADDR', '/**/')
+ endif
+
endif
network_args_string = ''
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]