+ if (net_SetMcastOutIPv4 (fd, ipv4) == 0)
+ return 0;
+
+ msg_Err (p_this, "%s: %s", addr, net_strerror (net_errno));
+ }
+ }
+
+ return -1;
+}
+
+
+/**
+ * Old-style any-source multicast join.
+ * In use on Windows XP/2003 and older.
+ */
+static int
+net_IPv4Join (vlc_object_t *obj, int fd,
+ const struct sockaddr_in *src, const struct sockaddr_in *grp)
+{
+#ifdef IP_ADD_MEMBERSHIP
+ union
+ {
+ struct ip_mreq gr4;
+# ifdef IP_ADD_SOURCE_MEMBERSHIP
+ struct ip_mreq_source gsr4;
+# endif
+ } opt;
+ int cmd;
+ struct in_addr id = { .s_addr = INADDR_ANY };
+ socklen_t optlen;
+
+ /* Multicast interface IPv4 address */
+ char *iface = var_CreateGetString (obj, "miface-addr");
+ if (iface != NULL)
+ {
+ if ((*iface)
+ && (inet_pton (AF_INET, iface, &id) <= 0))
+ {
+ msg_Err (obj, "invalid multicast interface address %s", iface);
+ free (iface);
+ goto error;
+ }
+ }
+
+ memset (&opt, 0, sizeof (opt));
+ if (src != NULL)
+ {
+# ifdef IP_ADD_SOURCE_MEMBERSHIP
+ cmd = IP_ADD_SOURCE_MEMBERSHIP;
+ opt.gsr4.imr_multiaddr = grp->sin_addr;
+ opt.gsr4.imr_sourceaddr = src->sin_addr;
+ opt.gsr4.imr_interface = id;
+ optlen = sizeof (opt.gsr4);
+# else
+ errno = ENOSYS;
+ goto error;
+# endif
+ }
+ else
+ {
+ cmd = IP_ADD_MEMBERSHIP;
+ opt.gr4.imr_multiaddr = grp->sin_addr;
+ opt.gr4.imr_interface = id;
+ optlen = sizeof (opt.gr4);
+ }
+
+ msg_Dbg (obj, "IP_ADD_%sMEMBERSHIP multicast request",
+ (src != NULL) ? "SOURCE_" : "");
+
+ if (setsockopt (fd, SOL_IP, cmd, &opt, optlen) == 0)
+ return 0;
+
+error:
+#endif
+
+ msg_Err (obj, "cannot join IPv4 multicast group (%s)",
+ net_strerror (net_errno));
+ return -1;
+}
+
+
+static int
+net_IPv6Join (vlc_object_t *obj, int fd, const struct sockaddr_in6 *src)
+{
+#ifdef IPV6_JOIN_GROUP
+ struct ipv6_mreq gr6;
+ memset (&gr6, 0, sizeof (gr6));
+ gr6.ipv6mr_interface = src->sin6_scope_id;
+ memcpy (&gr6.ipv6mr_multiaddr, &src->sin6_addr, 16);
+
+ msg_Dbg (obj, "IPV6_JOIN_GROUP multicast request");
+
+ if (!setsockopt (fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &gr6, sizeof (gr6)))
+ return 0;
+#else
+ errno = ENOSYS;
+#endif
+
+ msg_Err (obj, "cannot join IPv6 any-source multicast group (%s)",
+ net_strerror (net_errno));
+ return -1;
+}
+
+
+#if defined (WIN32) && !defined (MCAST_JOIN_SOURCE_GROUP)
+/*
+ * I hate manual definitions: Error-prone. Portability hell.
+ * Developers shall use UP-TO-DATE compilers. Full point.
+ * If you remove the warning, you remove the whole ifndef.
+ */
+# warning Your C headers are out-of-date. Please update.
+
+# define MCAST_JOIN_GROUP 41
+struct group_req
+{
+ ULONG gr_interface;
+ struct sockaddr_storage gr_group;
+};
+
+# define MCAST_JOIN_SOURCE_GROUP 45 /* from <ws2ipdef.h> */
+struct group_source_req
+{
+ uint32_t gsr_interface;
+ struct sockaddr_storage gsr_group;
+ struct sockaddr_storage gsr_source;
+};
+#endif
+
+/**
+ * IP-agnostic multicast join,
+ * with fallback to old APIs, and fallback from SSM to ASM.
+ */
+static int
+net_SourceSubscribe (vlc_object_t *obj, int fd,
+ const struct sockaddr *src, socklen_t srclen,
+ const struct sockaddr *grp, socklen_t grplen)
+{
+ int level, iid = 0;
+
+ char *iface = var_CreateGetString (obj, "miface");
+ if (iface != NULL)
+ {
+ if (*iface)
+ {
+ iid = if_nametoindex (iface);
+ if (iid == 0)