summaryrefslogtreecommitdiff
path: root/ACE/ace/SOCK_Dgram_Mcast.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ACE/ace/SOCK_Dgram_Mcast.cpp')
-rw-r--r--ACE/ace/SOCK_Dgram_Mcast.cpp929
1 files changed, 929 insertions, 0 deletions
diff --git a/ACE/ace/SOCK_Dgram_Mcast.cpp b/ACE/ace/SOCK_Dgram_Mcast.cpp
new file mode 100644
index 00000000000..f4687c0a0aa
--- /dev/null
+++ b/ACE/ace/SOCK_Dgram_Mcast.cpp
@@ -0,0 +1,929 @@
+// $Id$
+
+#include "ace/SOCK_Dgram_Mcast.h"
+
+#include "ace/OS_Memory.h"
+#include "ace/OS_NS_string.h"
+#include "ace/OS_NS_errno.h"
+#include "ace/os_include/net/os_if.h"
+#include "ace/os_include/arpa/os_inet.h"
+
+#if defined (__linux__) && defined (ACE_HAS_IPV6)
+#include "ace/OS_NS_sys_socket.h"
+#endif
+
+#if defined (ACE_WIN32)
+#include /**/ <iphlpapi.h>
+#endif
+
+#if defined (ACE_HAS_GETIFADDRS)
+# if defined (ACE_VXWORKS)
+# include /**/ <net/ifaddrs.h>
+# else
+# include /**/ <ifaddrs.h>
+# endif /*ACE_VXWORKS */
+#endif /* ACE_HAS_GETIFADDRS */
+
+#if !defined (__ACE_INLINE__)
+#include "ace/SOCK_Dgram_Mcast.inl"
+#endif /* __ACE_INLINE__ */
+
+#include "ace/Log_Msg.h"
+
+// This is a workaround for platforms with non-standard
+// definitions of the ip_mreq structure
+#if ! defined (IMR_MULTIADDR)
+#define IMR_MULTIADDR imr_multiaddr
+#endif /* ! defined (IMR_MULTIADDR) */
+
+ACE_BEGIN_VERSIONED_NAMESPACE_DECL
+
+// Helper (inline) functions.
+class ACE_SDM_helpers
+{
+public:
+ // Convert ACE_INET_Addr to string, using local formatting rules.
+
+ static void addr_to_string (const ACE_INET_Addr &ip_addr,
+ ACE_TCHAR *ret_string, // results here.
+ size_t len,
+ int clip_portnum) // clip port# info?
+ {
+ if (ip_addr.addr_to_string (ret_string, len, 1) == -1)
+ ACE_OS::strcpy (ret_string, ACE_TEXT ("<?>"));
+ else
+ {
+ ACE_TCHAR *pc = ACE_OS::strrchr (ret_string, ACE_TEXT (':'));
+ if (clip_portnum && pc)
+ *pc = ACE_TEXT ('\0'); // clip port# info.
+ }
+ }
+ // op== for ip_mreq structs.
+ static int is_equal (const ip_mreq &m1, const ip_mreq &m2)
+ {
+ return m1.IMR_MULTIADDR.s_addr == m2.IMR_MULTIADDR.s_addr
+ && m1.imr_interface.s_addr == m2.imr_interface.s_addr;
+ }
+};
+
+ACE_ALLOC_HOOK_DEFINE (ACE_SOCK_Dgram_Mcast)
+
+void
+ACE_SOCK_Dgram_Mcast::dump (void) const
+{
+#if defined (ACE_HAS_DUMP)
+ ACE_TRACE ("ACE_SOCK_Dgram_Mcast::dump");
+
+ ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this));
+
+# if defined (ACE_SOCK_DGRAM_MCAST_DUMPABLE)
+ ACE_TCHAR addr_string[MAXNAMELEN + 1];
+
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_TEXT ("\nOptions: bindaddr=%s, nulliface=%s\n"),
+ ACE_BIT_ENABLED (this->opts_, OPT_BINDADDR_YES) ?
+ ACE_TEXT ("<Bound>") : ACE_TEXT ("<Not Bound>"),
+ ACE_BIT_ENABLED (this->opts_, OPT_NULLIFACE_ALL) ?
+ ACE_TEXT ("<All Ifaces>") : ACE_TEXT ("<Default Iface>")));
+
+ // Show default send addr, port#, and interface.
+ ACE_SDM_helpers::addr_to_string (this->send_addr_, addr_string,
+ sizeof addr_string, 0);
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_TEXT ("Send addr=%s iface=%s\n"),
+ addr_string,
+ this->send_net_if_ ? this->send_net_if_
+ : ACE_TEXT ("<default>")));
+
+ // Show list of subscribed addresses.
+ ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Subscription list:\n")));
+
+ ACE_MT (ACE_GUARD (ACE_SDM_LOCK, guard, this->subscription_list_lock_));
+ subscription_list_iter_t iter (this->subscription_list_);
+ for ( ; !iter.done (); iter.advance ())
+ {
+ ACE_TCHAR iface_string[MAXNAMELEN + 1];
+ ip_mreq *pm = iter.next ();
+
+ // Get subscribed address (w/out port# info - not relevant).
+ ACE_INET_Addr ip_addr (static_cast<u_short> (0),
+ ACE_NTOHL (pm->IMR_MULTIADDR.s_addr));
+ ACE_SDM_helpers::addr_to_string (ip_addr, addr_string,
+ sizeof addr_string, 1);
+
+ // Get interface address/specification.
+ ACE_INET_Addr if_addr (static_cast<u_short> (0),
+ ACE_NTOHL (pm->imr_interface.s_addr));
+ ACE_SDM_helpers::addr_to_string (if_addr, iface_string,
+ sizeof iface_string, 1);
+ if (ACE_OS::strcmp (iface_string, ACE_TEXT ("0.0.0.0")) == 0)
+ // Receives on system default iface. (Note that null_iface_opt_
+ // option processing has already occurred.)
+ ACE_OS::strcpy (iface_string, ACE_TEXT ("<default>"));
+
+ // Dump info.
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_TEXT ("\taddr=%s iface=%s\n"),
+ addr_string,
+ iface_string));
+ }
+# endif /* ACE_SOCK_DGRAM_MCAST_DUMPABLE */
+ ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP));
+#endif /* ACE_HAS_DUMP */
+}
+
+// Constructor.
+ACE_SOCK_Dgram_Mcast::ACE_SOCK_Dgram_Mcast
+ (ACE_SOCK_Dgram_Mcast::options opts)
+ : opts_ (opts),
+ send_net_if_ (0)
+{
+ ACE_TRACE ("ACE_SOCK_Dgram_Mcast::ACE_SOCK_Dgram_Mcast");
+}
+
+// Destructor.
+ACE_SOCK_Dgram_Mcast::~ACE_SOCK_Dgram_Mcast (void)
+{
+ ACE_TRACE ("ACE_SOCK_Dgram_Mcast::~ACE_SOCK_Dgram_Mcast");
+
+ // Free memory and optionally unsubscribe from currently subscribed group(s).
+ delete [] this->send_net_if_;
+ this->clear_subs_list ();
+}
+
+int
+ACE_SOCK_Dgram_Mcast::open (const ACE_INET_Addr &mcast_addr,
+ const ACE_TCHAR *net_if,
+ int reuse_addr)
+{
+ ACE_TRACE ("ACE_SOCK_Dgram_Mcast::open");
+
+ // Only perform the <open> initialization if we haven't been opened
+ // earlier.
+ // No sanity check? We should probably flag an error if the user
+ // makes multiple calls to open().
+ if (this->get_handle () != ACE_INVALID_HANDLE)
+ return 0;
+
+ // Invoke lower-layer ::open.
+ if (ACE_SOCK::open (SOCK_DGRAM,
+ mcast_addr.get_type (),
+ 0, // always use 0
+ reuse_addr) == -1)
+ return -1;
+
+ return this->open_i (mcast_addr, net_if, reuse_addr);
+}
+
+int
+ACE_SOCK_Dgram_Mcast::open_i (const ACE_INET_Addr &mcast_addr,
+ const ACE_TCHAR *net_if,
+ int reuse_addr)
+{
+ ACE_TRACE ("ACE_SOCK_Dgram_Mcast::open_i");
+ // ACE_SOCK::open calls this if reuse_addr is set, so we only need to
+ // process port reuse option.
+ if (reuse_addr)
+ {
+#if defined (SO_REUSEPORT)
+ int one = 1;
+ if (this->ACE_SOCK::set_option (SOL_SOCKET,
+ SO_REUSEPORT,
+ &one,
+ sizeof one) == -1)
+ return -1;
+#endif /* SO_REUSEPORT */
+ }
+
+ // Create an address/port# to bind the socket to. Use mcast_addr to
+ // initialize bind_addy to pick up the correct protocol family. If
+ // OPT_BINDADDR_YES is set, then we're done. Else use mcast_addr's
+ // port number and use the "any" address.
+ ACE_INET_Addr bind_addy (mcast_addr);
+ if (ACE_BIT_DISABLED (this->opts_, OPT_BINDADDR_YES))
+ {
+#if defined (ACE_HAS_IPV6)
+ if (mcast_addr.get_type () == PF_INET6)
+ {
+ if (bind_addy.set (mcast_addr.get_port_number (), "::",
+ 1, AF_INET6) == -1)
+ return -1;
+ }
+ else
+ // Bind to "any" address and explicit port#.
+ if (bind_addy.set (mcast_addr.get_port_number ()) == -1)
+ return -1;
+#else
+ // Bind to "any" address and explicit port#.
+ if (bind_addy.set (mcast_addr.get_port_number ()) == -1)
+ return -1;
+#endif /* ACE_HAS_IPV6 */
+ }
+
+ // Bind to the address (which may be INADDR_ANY) and port# (which may be 0)
+ if (ACE_SOCK_Dgram::shared_open (bind_addy, bind_addy.get_type ()) == -1)
+ return -1;
+
+ // Cache the actual bound address (which may be INADDR_ANY)
+ // and the actual bound port# (which will be a valid, non-zero port#).
+ ACE_INET_Addr bound_addy;
+ if (this->get_local_addr (bound_addy) == -1)
+ {
+ // (Unexpected failure - should be bound to something)
+ if (bound_addy.set (bind_addy) == -1)
+ {
+ // (Shouldn't happen - bind_addy is a valid addy; punt.)
+ return -1;
+ }
+ }
+
+ this->send_addr_ = mcast_addr;
+ this->send_addr_.set_port_number (bound_addy.get_port_number ());
+ if (net_if)
+ {
+ if (this->set_nic (net_if, mcast_addr.get_type ()))
+ return -1;
+
+ this->send_net_if_ = new ACE_TCHAR[ACE_OS::strlen (net_if) + 1];
+ ACE_OS::strcpy (this->send_net_if_, net_if);
+ }
+
+ return 0;
+}
+
+int
+ACE_SOCK_Dgram_Mcast::subscribe_ifs (const ACE_INET_Addr &mcast_addr,
+ const ACE_TCHAR *net_if,
+ int reuse_addr)
+{
+ ACE_TRACE ("ACE_SOCK_Dgram_Mcast::subscribe_ifs");
+
+ if (ACE_BIT_ENABLED (this->opts_, OPT_NULLIFACE_ALL)
+ && net_if == 0)
+ {
+ int family = mcast_addr.get_type ();
+ size_t nr_subscribed = 0;
+
+#if defined (ACE_HAS_GETIFADDRS)
+
+ // Take advantage of the BSD getifaddrs function that simplifies
+ // access to connected interfaces.
+ struct ifaddrs *ifap = 0;
+ struct ifaddrs *p_if = 0;
+
+ if (::getifaddrs (&ifap) != 0)
+ return -1;
+
+ // Not every interface is for IP, and not all are up and multicast.
+ for (p_if = ifap;
+ p_if != 0;
+ p_if = p_if->ifa_next)
+ {
+ // Some OSes can return interfaces with no ifa_addr if the
+ // interface has no assigned address.
+ // If there is an address but it's not the family we want, ignore it.
+ if (p_if->ifa_addr == 0 || p_if->ifa_addr->sa_family != family)
+ continue;
+
+ // Check to see if it's up and supports multicast.
+ unsigned int wanted = IFF_UP | IFF_MULTICAST;
+ if ((p_if->ifa_flags & wanted) != wanted)
+ continue;
+
+ // Sometimes the kernel returns 0.0.0.0 as the interface
+ // address, skip those...
+ if (p_if->ifa_addr->sa_family == PF_INET)
+ {
+ struct sockaddr_in *addr =
+ reinterpret_cast<sockaddr_in *> (p_if->ifa_addr);
+
+ if (addr->sin_addr.s_addr == INADDR_ANY)
+ continue;
+ }
+# if defined (ACE_HAS_IPV6)
+ else if (p_if->ifa_addr->sa_family == AF_INET6)
+ {
+ struct sockaddr_in6 *addr =
+ reinterpret_cast<sockaddr_in6 *> (p_if->ifa_addr);
+
+ // Skip the ANY address
+ if (IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr))
+ continue;
+ }
+# endif /* ACE_HAS_IPV6 */
+
+ // Ok, now join on this interface.
+ if (this->join (mcast_addr,
+ reuse_addr,
+ ACE_TEXT_CHAR_TO_TCHAR(p_if->ifa_name)) == 0)
+ ++nr_subscribed;
+ }
+
+ ::freeifaddrs (ifap);
+
+# elif defined (ACE_WIN32)
+
+ IP_ADAPTER_ADDRESSES tmp_addrs;
+ // Initial call to determine actual memory size needed
+ DWORD dwRetVal;
+ ULONG bufLen = 0;
+ // Note... GetAdaptersAddresses returns different bufLen values depending
+ // on how many multicast joins there are on the system. To avoid this,
+ // specify that we don't want to know about multicast addresses. This
+ // does not avoid multicastable interfaces and makes the size-check
+ // more reliable across varying conditions.
+ DWORD flags = GAA_FLAG_SKIP_MULTICAST;
+ if ((dwRetVal = ::GetAdaptersAddresses (family,
+ flags,
+ 0,
+ &tmp_addrs,
+ &bufLen)) != ERROR_BUFFER_OVERFLOW)
+ {
+ errno = dwRetVal;
+ return -1; // With output bufferlength 0 this can't be right.
+ }
+
+ // Get required output buffer and retrieve info for real.
+ PIP_ADAPTER_ADDRESSES pAddrs;
+ char *buf;
+ ACE_NEW_RETURN (buf,
+ char[bufLen],
+ -1);
+ pAddrs = reinterpret_cast<PIP_ADAPTER_ADDRESSES> (buf);
+ if ((dwRetVal = ::GetAdaptersAddresses (family,
+ flags,
+ 0,
+ pAddrs,
+ &bufLen)) != NO_ERROR)
+ {
+ delete[] buf; // clean up
+ errno = dwRetVal;
+ return -1;
+ }
+
+ for (; pAddrs; pAddrs = pAddrs->Next)
+ {
+ if (pAddrs->OperStatus != IfOperStatusUp)
+ continue;
+
+ // The ACE_SOCK_Dgram::make_multicast_ifaddr (IPv4), called by join(),
+ // can only deal with a dotted-decimal address, not an interface name.
+ if (family == AF_INET)
+ {
+ ACE_INET_Addr intf_addr ((sockaddr_in*)(pAddrs->FirstUnicastAddress->Address.lpSockaddr),
+ pAddrs->FirstUnicastAddress->Address.iSockaddrLength);
+ char intf_addr_str[INET_ADDRSTRLEN];
+ intf_addr.get_host_addr (intf_addr_str, sizeof (intf_addr_str));
+ if (this->join (mcast_addr, reuse_addr,
+ ACE_TEXT_CHAR_TO_TCHAR(intf_addr_str)) == 0)
+ ++nr_subscribed;
+ }
+ else
+ {
+ if (this->join (mcast_addr, reuse_addr,
+ ACE_TEXT_CHAR_TO_TCHAR(pAddrs->AdapterName)) == 0)
+ ++nr_subscribed;
+ }
+ }
+
+ delete[] buf; // clean up
+
+# else
+
+ // Subscribe on all local multicast-capable network interfaces, by
+ // doing recursive calls with specific interfaces.
+
+ ACE_INET_Addr *if_addrs = 0;
+ size_t if_cnt;
+
+ if (ACE::get_ip_interfaces (if_cnt, if_addrs) != 0)
+ return -1;
+
+ if (if_cnt < 2)
+ {
+ if (this->join (mcast_addr,
+ reuse_addr,
+ ACE_TEXT ("0.0.0.0")) == 0)
+ ++nr_subscribed;
+ }
+ else
+ {
+ // Iterate through all the interfaces, figure out which ones
+ // offer multicast service, and subscribe to them.
+ while (if_cnt > 0)
+ {
+ --if_cnt;
+
+ // Convert to 0-based for indexing, next loop check.
+ if (if_addrs[if_cnt].get_type () != family || if_addrs[if_cnt].is_loopback ())
+ continue;
+ char addr_buf[INET6_ADDRSTRLEN];
+ if (this->join (mcast_addr,
+ reuse_addr,
+ ACE_TEXT_CHAR_TO_TCHAR
+ (if_addrs[if_cnt].get_host_addr (addr_buf, INET6_ADDRSTRLEN))) == 0)
+ ++nr_subscribed;
+ }
+ }
+
+ delete [] if_addrs;
+
+# endif /* ACE_WIN32 */
+
+ if (nr_subscribed == 0)
+ {
+ errno = ENODEV;
+ return -1;
+ }
+
+ return 1;
+ }
+
+ // Subscribe on a specific interface, or on the default interface.
+
+#if defined (ACE_HAS_IPV6)
+ if (mcast_addr.get_type () == AF_INET6)
+ {
+ if (this->make_multicast_ifaddr6 (0, mcast_addr, net_if) == -1)
+ return -1;
+ }
+ else
+#endif /* ACE_HAS_IPV6 - Fall into IPv4-only case */
+ {
+ // Validate passed multicast addr and iface specifications.
+ if (this->make_multicast_ifaddr (0,
+ mcast_addr,
+ net_if) == -1)
+ return -1;
+ }
+
+ return 0;
+
+}
+
+int
+ACE_SOCK_Dgram_Mcast::join (const ACE_INET_Addr &mcast_addr,
+ int reuse_addr,
+ const ACE_TCHAR *net_if)
+{
+ ACE_TRACE ("ACE_SOCK_Dgram_Mcast::join");
+ ACE_INET_Addr subscribe_addr = mcast_addr;
+
+ // If port# is 0, insert bound port# if it is set. (To satisfy lower-level
+ // port# validation.)
+ u_short def_port_number = this->send_addr_.get_port_number ();
+ if (subscribe_addr.get_port_number () == 0
+ && def_port_number != 0)
+ {
+ subscribe_addr.set_port_number (def_port_number);
+ }
+
+ // Check for port# different than bound port#.
+ u_short sub_port_number = mcast_addr.get_port_number ();
+ if (sub_port_number != 0
+ && def_port_number != 0
+ && sub_port_number != def_port_number)
+ {
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT ("Subscribed port# (%u) different than bound ")
+ ACE_TEXT ("port# (%u).\n"),
+ (u_int) sub_port_number,
+ (u_int) def_port_number));
+ errno = ENXIO;
+ return -1;
+ }
+
+ // If bind_addr_opt_ is enabled, check for address different than
+ // bound address.
+ ACE_INET_Addr tmp_addr (this->send_addr_);
+ tmp_addr.set_port_number (mcast_addr.get_port_number ()); // force equal port numbers
+ if (ACE_BIT_ENABLED (this->opts_, OPT_BINDADDR_YES)
+ && !this->send_addr_.is_any ()
+ && this->send_addr_ != mcast_addr)
+ {
+ ACE_TCHAR sub_addr_string[MAXNAMELEN + 1];
+ ACE_TCHAR bound_addr_string[MAXNAMELEN + 1];
+ ACE_SDM_helpers::addr_to_string (mcast_addr, sub_addr_string,
+ sizeof sub_addr_string, 1);
+ ACE_SDM_helpers::addr_to_string (this->send_addr_, bound_addr_string,
+ sizeof bound_addr_string, 1);
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT ("Subscribed address (%s) different than ")
+ ACE_TEXT ("bound address (%s).\n"),
+ sub_addr_string,
+ bound_addr_string));
+ errno = ENXIO;
+ return -1;
+ }
+
+ // Attempt subscription.
+ int result = this->subscribe_i (subscribe_addr, reuse_addr, net_if);
+
+#if defined (ACE_SOCK_DGRAM_MCAST_DUMPABLE)
+ if (result == 0)
+ {
+ // Add this addr/iface info to the list of subscriptions.
+ // (Assumes this is unique addr/iface combo - most systems don't allow
+ // re-sub to same addr/iface.)
+ ip_mreq *pmreq = new ip_mreq;
+ // (should not fail)
+ if (this->make_multicast_ifaddr (pmreq, subscribe_addr, net_if) != -1)
+ {
+ ACE_MT (ACE_GUARD_RETURN (ACE_SDM_LOCK, guard,
+ this->subscription_list_lock_, -1));
+ this->subscription_list_.insert_tail (pmreq);
+ return 0;
+ }
+ // this still isn't really right. If ACE_GUARD_RETURN fails, we leak.
+ // Need to add one of Chris' fancy ace auto pointers (bound?).
+ delete pmreq;
+ }
+#endif /* ACE_SOCK_DGRAM_MCAST_DUMPABLE */
+
+ return result >= 0 ? 0 : result;
+}
+
+// Attempt subscribe and return status.
+int
+ACE_SOCK_Dgram_Mcast::subscribe_i (const ACE_INET_Addr &mcast_addr,
+ int reuse_addr,
+ const ACE_TCHAR *net_if)
+{
+ ACE_TRACE ("ACE_SOCK_Dgram_Mcast::subscribe_i");
+ ip_mreq mreq;
+#if defined (ACE_HAS_IPV6)
+ ipv6_mreq mreq6;
+#endif /* __linux__ && ACE_HAS_IPV6 */
+
+ // Open the socket IFF this is the first ::subscribe and ::open
+ // was not explicitly invoked.
+ if (this->open (mcast_addr,
+ net_if,
+ reuse_addr) == -1)
+ return -1;
+
+ // Only do this if net_if == 0, i.e., INADDR_ANY
+ if (net_if == 0)
+ {
+ int result = this->subscribe_ifs (mcast_addr,
+ net_if,
+ reuse_addr);
+ // Check for error or "short-circuit" return.
+ if (result != 0)
+ return result;
+ }
+
+#if defined (ACE_HAS_IPV6)
+ if (mcast_addr.get_type () == AF_INET6)
+ {
+ if (this->make_multicast_ifaddr6 (&mreq6, mcast_addr, net_if) == -1)
+ return -1;
+ // Tell IP stack to pass messages sent to this group.
+ else if (this->ACE_SOCK::set_option (IPPROTO_IPV6,
+ IPV6_JOIN_GROUP,
+ &mreq6,
+ sizeof mreq6) == -1)
+ return -1;
+
+ return 0;
+ }
+ // Fall through for IPv4 case
+#endif /* ACE_HAS_IPV6 */
+
+ // Create multicast addr/if struct.
+ if (this->make_multicast_ifaddr (&mreq, mcast_addr, net_if) == -1)
+ return -1;
+ // Tell IP stack to pass messages sent to this group.
+ else if (this->ACE_SOCK::set_option (IPPROTO_IP,
+ IP_ADD_MEMBERSHIP,
+ &mreq,
+ sizeof mreq) == -1)
+ return -1;
+
+ return 0;
+}
+
+int
+ACE_SOCK_Dgram_Mcast::unsubscribe_ifs (const ACE_INET_Addr &mcast_addr,
+ const ACE_TCHAR *net_if)
+{
+ ACE_TRACE ("ACE_SOCK_Dgram_Mcast::unsubscribe_ifs");
+
+
+ if (ACE_BIT_ENABLED (this->opts_, OPT_NULLIFACE_ALL)
+ && net_if == 0)
+ {
+#if defined (ACE_HAS_IPV6)
+ if (mcast_addr.get_type () == AF_INET6)
+ {
+ size_t nr_unsubscribed = 0;
+# if defined(__linux__)
+
+ struct if_nameindex *intf;
+
+ intf = ACE_OS::if_nameindex ();
+
+ if (intf == 0)
+ return -1;
+
+ int index = 0;
+ while (intf[index].if_index != 0 || intf[index].if_name != 0)
+ {
+ if (this->leave (mcast_addr, ACE_TEXT_CHAR_TO_TCHAR(intf[index].if_name)) == 0)
+ ++nr_unsubscribed;
+
+ ++index;
+ }
+
+ ACE_OS::if_freenameindex (intf);
+
+# elif defined (ACE_WIN32)
+
+ IP_ADAPTER_ADDRESSES tmp_addrs;
+ // Initial call to determine actual memory size needed
+ DWORD dwRetVal;
+ ULONG bufLen = 0;
+ if ((dwRetVal = ::GetAdaptersAddresses (AF_INET6,
+ 0,
+ 0,
+ &tmp_addrs,
+ &bufLen)) != ERROR_BUFFER_OVERFLOW)
+ return -1; // With output bufferlength 0 this can't be right.
+
+ // Get required output buffer and retrieve info for real.
+ PIP_ADAPTER_ADDRESSES pAddrs;
+ char *buf;
+ ACE_NEW_RETURN (buf,
+ char[bufLen],
+ -1);
+ pAddrs = reinterpret_cast<PIP_ADAPTER_ADDRESSES> (buf);
+ if ((dwRetVal = ::GetAdaptersAddresses (AF_INET6,
+ 0,
+ 0,
+ pAddrs,
+ &bufLen)) != NO_ERROR)
+ {
+ delete[] buf; // clean up
+ return -1;
+ }
+
+ while (pAddrs)
+ {
+ if (this->leave (mcast_addr, ACE_TEXT_CHAR_TO_TCHAR(pAddrs->AdapterName)) == 0)
+ ++nr_unsubscribed;
+
+ pAddrs = pAddrs->Next;
+ }
+
+ delete[] buf; // clean up
+
+# endif /* ACE_WIN32 */
+
+ if (nr_unsubscribed == 0)
+ {
+ errno = ENODEV;
+ return -1;
+ }
+
+ return 1;
+
+
+ }
+ else
+ {
+ // Unsubscribe on all local multicast-capable network interfaces, by
+ // doing recursive calls with specific interfaces.
+
+ ACE_INET_Addr *if_addrs = 0;
+ size_t if_cnt;
+
+ // NOTE - <get_ip_interfaces> doesn't always get all of the
+ // interfaces. In particular, it may not get a PPP interface. This
+ // is a limitation of the way <get_ip_interfaces> works with
+ // old versions of MSVC. The reliable way of getting the interface
+ // list is available only with MSVC 5 and newer.
+ if (ACE::get_ip_interfaces (if_cnt, if_addrs) != 0)
+ return -1;
+
+ size_t nr_unsubscribed = 0;
+
+ if (if_cnt < 2)
+ {
+ if (this->leave (mcast_addr,
+ ACE_TEXT ("0.0.0.0")) == 0)
+ ++nr_unsubscribed;
+ }
+ else
+ {
+ while (if_cnt > 0)
+ {
+ --if_cnt;
+ // Convert to 0-based for indexing, next loop check
+ if (if_addrs[if_cnt].get_type () != AF_INET || if_addrs[if_cnt].is_loopback ())
+ continue;
+ char addr_buf[INET6_ADDRSTRLEN];
+ if (this->leave (mcast_addr,
+ ACE_TEXT_CHAR_TO_TCHAR
+ (if_addrs[if_cnt].get_host_addr (addr_buf, INET6_ADDRSTRLEN))) == 0)
+ ++nr_unsubscribed;
+ }
+ }
+
+ delete [] if_addrs;
+
+ if (nr_unsubscribed == 0)
+ {
+ errno = ENODEV;
+ return -1;
+ }
+
+ return 1;
+
+ }
+#else /* ACE_HAS_IPV6 */
+ // Unsubscribe on all local multicast-capable network interfaces, by
+ // doing recursive calls with specific interfaces.
+
+ ACE_INET_Addr *if_addrs = 0;
+ size_t if_cnt;
+
+ // NOTE - <get_ip_interfaces> doesn't always get all of the
+ // interfaces. In particular, it may not get a PPP interface. This
+ // is a limitation of the way <get_ip_interfaces> works with
+ // old versions of MSVC. The reliable way of getting the interface list
+ // is available only with MSVC 5 and newer.
+ if (ACE::get_ip_interfaces (if_cnt, if_addrs) != 0)
+ return -1;
+
+ size_t nr_unsubscribed = 0;
+
+ if (if_cnt < 2)
+ {
+ if (this->leave (mcast_addr,
+ ACE_TEXT ("0.0.0.0")) == 0)
+ ++nr_unsubscribed;
+ }
+ else
+ {
+ while (if_cnt > 0)
+ {
+ --if_cnt;
+ // Convert to 0-based for indexing, next loop check
+ if (if_addrs[if_cnt].is_loopback ())
+ continue;
+ char addr_buf[INET6_ADDRSTRLEN];
+ if (this->leave (mcast_addr,
+ ACE_TEXT_CHAR_TO_TCHAR
+ (if_addrs[if_cnt].get_host_addr (addr_buf, INET6_ADDRSTRLEN))) == 0)
+ ++nr_unsubscribed;
+ }
+ }
+
+ delete [] if_addrs;
+
+ if (nr_unsubscribed == 0)
+ {
+ errno = ENODEV;
+ return -1;
+ }
+
+ return 1;
+#endif /* !ACE_HAS_IPV6 */
+ }
+
+ return 0;
+}
+
+
+int
+ACE_SOCK_Dgram_Mcast::leave (const ACE_INET_Addr &mcast_addr,
+ const ACE_TCHAR *net_if)
+{
+ ACE_TRACE ("ACE_SOCK_Dgram_Mcast::leave");
+ // Unsubscribe.
+ int result = this->unsubscribe_i (mcast_addr,
+ net_if);
+
+#if defined (ACE_SOCK_DGRAM_MCAST_DUMPABLE)
+ // (Unconditionally) Remove this addr/if from subscription list.
+ // (Addr/if is removed even if unsubscribe failed)
+ ip_mreq tgt_mreq;
+ if (this->make_multicast_ifaddr (&tgt_mreq,
+ mcast_addr,
+ net_if) != -1)
+ {
+ ACE_MT (ACE_GUARD_RETURN (ACE_SDM_LOCK, guard,
+ this->subscription_list_lock_, -1));
+ subscription_list_iter_t iter (this->subscription_list_);
+ for (; !iter.done (); iter.advance ())
+ {
+ ip_mreq *pm = iter.next ();
+ if (ACE_SDM_helpers::is_equal (*pm, tgt_mreq))
+ {
+ iter.remove ();
+ delete pm;
+ break;
+ }
+ }
+ }
+#endif /* ACE_SOCK_DGRAM_MCAST_DUMPABLE */
+
+ return result >= 0 ? 0 : result;
+}
+
+// Attempt unsubscribe and return status.
+int
+ACE_SOCK_Dgram_Mcast::unsubscribe_i (const ACE_INET_Addr &mcast_addr,
+ const ACE_TCHAR *net_if)
+{
+ ACE_TRACE ("ACE_SOCK_Dgram_Mcast::unsubscribe_i");
+
+ int result = this->unsubscribe_ifs (mcast_addr,
+ net_if);
+
+ // Check for error or "short-circuit" return.
+ if (result != 0)
+ return result;
+
+#if defined (ACE_HAS_IPV6)
+ if (mcast_addr.get_type () == AF_INET6)
+ {
+ // Validate addr/if specifications and create addr/if struct.
+ ipv6_mreq mreq;
+ if (this->make_multicast_ifaddr6 (&mreq, mcast_addr, net_if) == -1)
+ {
+ return -1;
+ }
+ // Tell network device driver to stop reading datagrams with the
+ // <mcast_addr>.
+ else if (ACE_SOCK::set_option (IPPROTO_IPV6,
+ IPV6_LEAVE_GROUP,
+ &mreq,
+ sizeof mreq) == -1)
+ {
+ return -1;
+ }
+
+ }
+ else // IPv4
+ {
+ // Validate addr/if specifications and create addr/if struct.
+ ip_mreq mreq;
+ if (this->make_multicast_ifaddr (&mreq, mcast_addr, net_if) == -1)
+ {
+ return -1;
+ }
+ // Tell network device driver to stop reading datagrams with the
+ // <mcast_addr>.
+ else if (ACE_SOCK::set_option (IPPROTO_IP,
+ IP_DROP_MEMBERSHIP,
+ &mreq,
+ sizeof mreq) == -1)
+ {
+ return -1;
+ }
+
+ }
+#else
+ // Validate addr/if specifications and create addr/if struct.
+ ip_mreq mreq;
+ if (this->make_multicast_ifaddr (&mreq, mcast_addr, net_if) == -1)
+ {
+ return -1;
+ }
+ // Tell network device driver to stop reading datagrams with the
+ // <mcast_addr>.
+ // Note, this is not IPv6 friendly...
+ else if (ACE_SOCK::set_option (IPPROTO_IP,
+ IP_DROP_MEMBERSHIP,
+ &mreq,
+ sizeof mreq) == -1)
+ {
+ return -1;
+ }
+#endif /* ACE_HAS_IPV6 */
+
+ return 0;
+}
+
+int
+ACE_SOCK_Dgram_Mcast::clear_subs_list (void)
+{
+ ACE_TRACE ("ACE_SOCK_Dgram_Mcast::clear_subs_list");
+ int result = 0;
+
+#if defined (ACE_SOCK_DGRAM_MCAST_DUMPABLE)
+ ACE_MT (ACE_GUARD_RETURN (ACE_SDM_LOCK, guard,
+ this->subscription_list_lock_, -1));
+ subscription_list_iter_t iter (this->subscription_list_);
+ for (; !iter.done (); /*Hack: Do _not_ ::advance after remove*/)
+ {
+ ip_mreq *pm = iter.next ();
+ iter.remove ();
+ delete pm;
+ }
+#endif /* ACE_SOCK_DGRAM_MCAST_DUMPABLE */
+ return result;
+}
+
+ACE_END_VERSIONED_NAMESPACE_DECL