mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-24 14:36:13 +01:00
gio: Fix multicast iface selection on macOS
ip_mreqn.imr_ifindex is not used correctly by the XNU kernel, and causes us to bind to the default interface; so fallback to ip_mreq and set the iface source address (not SSM). Fixes https://gitlab.gnome.org/GNOME/glib/-/issues/3489
This commit is contained in:
parent
c94cbf2368
commit
469084c45b
@ -2402,6 +2402,42 @@ g_socket_w32_get_adapter_ipv4_addr (const gchar *name_or_ip)
|
|||||||
|
|
||||||
return ip_result;
|
return ip_result;
|
||||||
}
|
}
|
||||||
|
#elif defined(HAVE_SIOCGIFADDR)
|
||||||
|
static gulong
|
||||||
|
g_socket_get_adapter_ipv4_addr (GSocket *socket,
|
||||||
|
const char *iface,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
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 ULONG_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ULONG_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
iface_addr = (struct sockaddr_in *) &ifr.ifr_addr;
|
||||||
|
return iface_addr->sin_addr.s_addr;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
@ -2418,6 +2454,7 @@ g_socket_multicast_group_operation (GSocket *socket,
|
|||||||
g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
|
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 (socket->priv->type == G_SOCKET_TYPE_DATAGRAM, FALSE);
|
||||||
g_return_val_if_fail (G_IS_INET_ADDRESS (group), FALSE);
|
g_return_val_if_fail (G_IS_INET_ADDRESS (group), FALSE);
|
||||||
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||||
|
|
||||||
if (!check_socket (socket, error))
|
if (!check_socket (socket, error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -2425,7 +2462,7 @@ g_socket_multicast_group_operation (GSocket *socket,
|
|||||||
native_addr = g_inet_address_to_bytes (group);
|
native_addr = g_inet_address_to_bytes (group);
|
||||||
if (g_inet_address_get_family (group) == G_SOCKET_FAMILY_IPV4)
|
if (g_inet_address_get_family (group) == G_SOCKET_FAMILY_IPV4)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_IP_MREQN
|
#if defined(HAVE_IP_MREQN) && !defined(__APPLE__)
|
||||||
struct ip_mreqn mc_req;
|
struct ip_mreqn mc_req;
|
||||||
#else
|
#else
|
||||||
struct ip_mreq mc_req;
|
struct ip_mreq mc_req;
|
||||||
@ -2434,7 +2471,11 @@ g_socket_multicast_group_operation (GSocket *socket,
|
|||||||
memset (&mc_req, 0, sizeof (mc_req));
|
memset (&mc_req, 0, sizeof (mc_req));
|
||||||
memcpy (&mc_req.imr_multiaddr, native_addr, sizeof (struct in_addr));
|
memcpy (&mc_req.imr_multiaddr, native_addr, sizeof (struct in_addr));
|
||||||
|
|
||||||
#ifdef HAVE_IP_MREQN
|
/* mc_req.imr_ifindex is not used correctly by the XNU kernel, and
|
||||||
|
* causes us to bind to the default interface; so fallback to ip_mreq
|
||||||
|
* and set the iface source address (not SSM).
|
||||||
|
* See: https://gitlab.gnome.org/GNOME/glib/-/issues/3489 */
|
||||||
|
#if defined(HAVE_IP_MREQN) && !defined(__APPLE__)
|
||||||
if (iface)
|
if (iface)
|
||||||
mc_req.imr_ifindex = if_nametoindex (iface);
|
mc_req.imr_ifindex = if_nametoindex (iface);
|
||||||
else
|
else
|
||||||
@ -2444,6 +2485,22 @@ g_socket_multicast_group_operation (GSocket *socket,
|
|||||||
mc_req.imr_interface.s_addr = g_socket_w32_get_adapter_ipv4_addr (iface);
|
mc_req.imr_interface.s_addr = g_socket_w32_get_adapter_ipv4_addr (iface);
|
||||||
else
|
else
|
||||||
mc_req.imr_interface.s_addr = g_htonl (INADDR_ANY);
|
mc_req.imr_interface.s_addr = g_htonl (INADDR_ANY);
|
||||||
|
#elif defined(HAVE_SIOCGIFADDR)
|
||||||
|
if (iface)
|
||||||
|
{
|
||||||
|
GError *local_error = NULL;
|
||||||
|
|
||||||
|
mc_req.imr_interface.s_addr = g_socket_get_adapter_ipv4_addr (socket, iface, &local_error);
|
||||||
|
if (local_error != NULL)
|
||||||
|
{
|
||||||
|
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mc_req.imr_interface.s_addr = g_htonl (INADDR_ANY);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
mc_req.imr_interface.s_addr = g_htonl (INADDR_ANY);
|
mc_req.imr_interface.s_addr = g_htonl (INADDR_ANY);
|
||||||
#endif
|
#endif
|
||||||
@ -2642,36 +2699,15 @@ g_socket_multicast_group_operation_ssm (GSocket *socket,
|
|||||||
{
|
{
|
||||||
#if defined(G_OS_WIN32)
|
#if defined(G_OS_WIN32)
|
||||||
S_ADDR_FIELD(mc_req_src) = g_socket_w32_get_adapter_ipv4_addr (iface);
|
S_ADDR_FIELD(mc_req_src) = g_socket_w32_get_adapter_ipv4_addr (iface);
|
||||||
#elif defined (HAVE_SIOCGIFADDR)
|
#elif defined(HAVE_SIOCGIFADDR)
|
||||||
int ret;
|
GError *local_error = NULL;
|
||||||
struct ifreq ifr;
|
|
||||||
struct sockaddr_in *iface_addr;
|
|
||||||
size_t if_name_len = strlen (iface);
|
|
||||||
|
|
||||||
memset (&ifr, 0, sizeof (ifr));
|
S_ADDR_FIELD(mc_req_src) = g_socket_get_adapter_ipv4_addr (socket, iface, &local_error);
|
||||||
|
if (local_error != NULL)
|
||||||
if (if_name_len >= sizeof (ifr.ifr_name))
|
|
||||||
{
|
{
|
||||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FILENAME_TOO_LONG,
|
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||||
_("Interface name too long"));
|
|
||||||
return FALSE;
|
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;
|
|
||||||
S_ADDR_FIELD(mc_req_src) = iface_addr->sin_addr.s_addr;
|
|
||||||
#endif /* defined(G_OS_WIN32) && defined (HAVE_IF_NAMETOINDEX) */
|
#endif /* defined(G_OS_WIN32) && defined (HAVE_IF_NAMETOINDEX) */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user