mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-26 05:56:14 +01:00
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
This commit is contained in:
parent
a79ca79a85
commit
ea725a6414
@ -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
|
||||
|
||||
|
16
configure.ac
16
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
|
||||
|
@ -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
|
||||
|
287
gio/gsocket.c
287
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
|
||||
|
@ -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,
|
||||
|
@ -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 = ''
|
||||
|
Loading…
Reference in New Issue
Block a user