+#if !defined(HAVE_IF_NAMETOINDEX) && defined(G_OS_WIN32)
+static guint
+if_nametoindex (const gchar *iface)
+{
+ PIP_ADAPTER_ADDRESSES addresses = NULL, p;
+ gulong addresses_len = 0;
+ guint idx = 0;
+ DWORD res;
+
+ res = GetAdaptersAddresses (AF_UNSPEC, 0, NULL, NULL, &addresses_len);
+ if (res != NO_ERROR && res != ERROR_BUFFER_OVERFLOW)
+ {
+ if (res == ERROR_NO_DATA)
+ errno = ENXIO;
+ else
+ errno = EINVAL;
+ return 0;
+ }
+
+ addresses = g_malloc (addresses_len);
+ res = GetAdaptersAddresses (AF_UNSPEC, 0, NULL, addresses, &addresses_len);
+
+ if (res != NO_ERROR)
+ {
+ g_free (addresses);
+ if (res == ERROR_NO_DATA)
+ errno = ENXIO;
+ else
+ errno = EINVAL;
+ return 0;
+ }
+
+ p = addresses;
+ while (p)
+ {
+ if (strcmp (p->AdapterName, iface) == 0)
+ {
+ idx = p->IfIndex;
+ break;
+ }
+ p = p->Next;
+ }
+
+ if (p == NULL)
+ errno = ENXIO;
+
+ g_free (addresses);
+
+ return idx;
+}
+
+#define HAVE_IF_NAMETOINDEX 1
+#endif
+
+static gboolean
+g_socket_multicast_group_operation (GSocket *socket,
+ GInetAddress *group,
+ gboolean source_specific,
+ const gchar *iface,
+ gboolean join_group,
+ GError **error)
+{
+ const guint8 *native_addr;
+ gint optname, 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);
+
+ if (!check_socket (socket, error))
+ return FALSE;
+
+ native_addr = g_inet_address_to_bytes (group);
+ if (g_inet_address_get_family (group) == G_SOCKET_FAMILY_IPV4)
+ {
+#ifdef HAVE_IP_MREQN
+ struct ip_mreqn mc_req;
+#else
+ struct ip_mreq mc_req;
+#endif
+
+ memset (&mc_req, 0, sizeof (mc_req));
+ memcpy (&mc_req.imr_multiaddr, native_addr, sizeof (struct in_addr));
+
+#ifdef HAVE_IP_MREQN
+ if (iface)
+ mc_req.imr_ifindex = if_nametoindex (iface);
+ else
+ mc_req.imr_ifindex = 0; /* Pick any. */
+#elif defined(G_OS_WIN32)
+ if (iface)
+ mc_req.imr_interface.s_addr = g_htonl (if_nametoindex (iface));
+ else
+ mc_req.imr_interface.s_addr = g_htonl (INADDR_ANY);
+#else
+ mc_req.imr_interface.s_addr = g_htonl (INADDR_ANY);
+#endif
+
+ if (source_specific)
+ {
+#ifdef IP_ADD_SOURCE_MEMBERSHIP
+ optname = join_group ? IP_ADD_SOURCE_MEMBERSHIP : IP_DROP_SOURCE_MEMBERSHIP;
+#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 source-specific multicast"));
+ return FALSE;
+#endif
+ }
+ else
+ optname = join_group ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
+ result = setsockopt (socket->priv->fd, IPPROTO_IP, optname,
+ &mc_req, sizeof (mc_req));
+ }
+ else if (g_inet_address_get_family (group) == G_SOCKET_FAMILY_IPV6)
+ {
+ struct ipv6_mreq mc_req_ipv6;
+
+ memset (&mc_req_ipv6, 0, sizeof (mc_req_ipv6));
+ memcpy (&mc_req_ipv6.ipv6mr_multiaddr, native_addr, sizeof (struct in6_addr));
+#ifdef HAVE_IF_NAMETOINDEX
+ if (iface)
+ mc_req_ipv6.ipv6mr_interface = if_nametoindex (iface);
+ else
+#endif
+ mc_req_ipv6.ipv6mr_interface = 0;
+
+ optname = join_group ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP;
+ result = setsockopt (socket->priv->fd, IPPROTO_IPV6, optname,
+ &mc_req_ipv6, sizeof (mc_req_ipv6));
+ }
+ else
+ 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:
+ * @socket: a #GSocket.
+ * @group: a #GInetAddress specifying the group address to join.
+ * @iface: (allow-none): Name of the interface to use, or %NULL
+ * @source_specific: %TRUE if source-specific multicast should be used
+ * @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 %TRUE, source-specific multicast as defined
+ * in RFC 4604 is used. Note that on older platforms this may fail
+ * with a %G_IO_ERROR_NOT_SUPPORTED error.
+ *
+ * Returns: %TRUE on success, %FALSE on error.
+ *
+ * Since: 2.32
+ */
+gboolean
+g_socket_join_multicast_group (GSocket *socket,
+ GInetAddress *group,
+ gboolean source_specific,
+ const gchar *iface,
+ GError **error)
+{
+ return g_socket_multicast_group_operation (socket, group, source_specific, iface, TRUE, error);
+}
+
+/**
+ * g_socket_leave_multicast_group:
+ * @socket: a #GSocket.
+ * @group: a #GInetAddress specifying the group address to leave.
+ * @iface: (allow-none): Interface used
+ * @source_specific: %TRUE if source-specific multicast was used
+ * @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.32
+ */
+gboolean
+g_socket_leave_multicast_group (GSocket *socket,
+ GInetAddress *group,
+ gboolean source_specific,
+ const gchar *iface,
+ GError **error)
+{
+ return g_socket_multicast_group_operation (socket, group, source_specific, iface, FALSE, error);
+}
+