diff options
Diffstat (limited to 'ACE/ace/SOCK_Dgram_Mcast.cpp')
-rw-r--r-- | ACE/ace/SOCK_Dgram_Mcast.cpp | 993 |
1 files changed, 993 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..5f325a7e519 --- /dev/null +++ b/ACE/ace/SOCK_Dgram_Mcast.cpp @@ -0,0 +1,993 @@ +// $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_HAS_IPV6) && defined (ACE_WIN32) +#include /**/ <Iphlpapi.h> +#endif + +#if !defined (__ACE_INLINE__) +#include "ace/SOCK_Dgram_Mcast.inl" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID (ace, + SOCK_Dgram_Mcast, + "$Id$") + +#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_LIB_TEXT ("<?>")); + else + { + ACE_TCHAR *pc = ACE_OS::strrchr (ret_string, ACE_LIB_TEXT (':')); + if (clip_portnum && pc) + *pc = ACE_LIB_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_LIB_TEXT ("\nOptions: bindaddr=%s, nulliface=%s\n"), + ACE_BIT_ENABLED (this->opts_, OPT_BINDADDR_YES) ? + ACE_LIB_TEXT ("<Bound>") : ACE_LIB_TEXT ("<Not Bound>"), + ACE_BIT_ENABLED (this->opts_, OPT_NULLIFACE_ALL) ? + ACE_LIB_TEXT ("<All Ifaces>") : ACE_LIB_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_LIB_TEXT ("Send addr=%s iface=%s\n"), + addr_string, + this->send_net_if_ ? this->send_net_if_ + : ACE_LIB_TEXT ("<default>"))); + + // Show list of subscribed addresses. + ACE_DEBUG ((LM_DEBUG, ACE_LIB_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_LIB_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_LIB_TEXT ("<default>")); + + // Dump info. + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_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) + { +#if defined (ACE_HAS_IPV6) + if (mcast_addr.get_type () == AF_INET6) + { + size_t nr_subscribed = 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->join (mcast_addr, reuse_addr, + ACE_TEXT_CHAR_TO_TCHAR(intf[index].if_name)) == 0) + ++nr_subscribed; + + ++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, + NULL, + &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, + NULL, + pAddrs, + &bufLen)) != NO_ERROR) + { + delete[] buf; // clean up + return -1; + } + + while (pAddrs) + { + if (this->join (mcast_addr, reuse_addr, + ACE_TEXT_CHAR_TO_TCHAR(pAddrs->AdapterName)) == 0) + ++nr_subscribed; + + pAddrs = pAddrs->Next; + } + + delete[] buf; // clean up + +# endif /* ACE_WIN32 */ + + if (nr_subscribed == 0) + { + errno = ENODEV; + return -1; + } + + return 1; + } + 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; + + size_t nr_subscribed = 0; + + if (if_cnt < 2) + { + if (this->subscribe (mcast_addr, + reuse_addr, + ACE_LIB_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 () != AF_INET || if_addrs[if_cnt].is_loopback ()) + continue; + if (this->subscribe (mcast_addr, + reuse_addr, + ACE_TEXT_CHAR_TO_TCHAR + (if_addrs[if_cnt].get_host_addr ())) == 0) + ++nr_subscribed; + } + } + + delete [] if_addrs; + + if (nr_subscribed == 0) + { + errno = ENODEV; + return -1; + } + + // 1 indicates a "short-circuit" return. This handles the + // recursive behavior of checking all the interfaces. + return 1; + + } +#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; + + size_t nr_subscribed = 0; + + if (if_cnt < 2) + { + if (this->subscribe (mcast_addr, + reuse_addr, + ACE_LIB_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].is_loopback ()) + continue; + if (this->subscribe (mcast_addr, + reuse_addr, + ACE_TEXT_CHAR_TO_TCHAR + (if_addrs[if_cnt].get_host_addr ())) == 0) + ++nr_subscribed; + } + } + + delete [] if_addrs; + + if (nr_subscribed == 0) + { + errno = ENODEV; + return -1; + } + + // 1 indicates a "short-circuit" return. This handles the + // recursive behavior of checking all the interfaces. + return 1; +#endif /* ACE_HAS_IPV6 */ + } + +#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 + { + // Validate passed multicast addr and iface specifications. + if (this->make_multicast_ifaddr (0, + mcast_addr, + net_if) == -1) + return -1; + } +#else + // Validate passed multicast addr and iface specifications. + if (this->make_multicast_ifaddr (0, + mcast_addr, + net_if) == -1) + return -1; +#endif /* ACE_HAS_IPV6 */ + + return 0; + +} + +// Subscribe and add address/iface to subscription list if successful. +int +ACE_SOCK_Dgram_Mcast::subscribe (const ACE_INET_Addr &mcast_addr, + int reuse_addr, + const ACE_TCHAR *net_if, + int protocol_family, + int protocol) +{ + ACE_TRACE ("ACE_SOCK_Dgram_Mcast::subscribe"); + + ACE_UNUSED_ARG (protocol_family); + ACE_UNUSED_ARG (protocol); + + return this->join (mcast_addr,reuse_addr, net_if); +} + +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_LIB_TEXT ("Subscribed port# (%u) different than bound ") + ACE_LIB_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_LIB_TEXT ("Subscribed address (%s) different than ") + ACE_LIB_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; + } + else + { + // 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; + + } +#else + if (this->make_multicast_ifaddr (&mreq, mcast_addr, net_if) == -1) + return -1; + // Tell IP stack to pass messages sent to this group. + // Note, this is not IPv6 compliant. + else if (this->ACE_SOCK::set_option (IPPROTO_IP, + IP_ADD_MEMBERSHIP, + &mreq, + sizeof mreq) == -1) + return -1; + +#endif /* ACE_HAS_IPV6 */ + + 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, + NULL, + &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, + NULL, + 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_LIB_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; + if (this->leave (mcast_addr, + ACE_TEXT_CHAR_TO_TCHAR + (if_addrs[if_cnt].get_host_addr ())) == 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_LIB_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; + if (this->leave (mcast_addr, + ACE_TEXT_CHAR_TO_TCHAR + (if_addrs[if_cnt].get_host_addr ())) == 0) + ++nr_unsubscribed; + } + } + + delete [] if_addrs; + + if (nr_unsubscribed == 0) + { + errno = ENODEV; + return -1; + } + + return 1; +#endif /* !ACE_HAS_IPV6 */ + } + + return 0; +} + + +// Unsubscribe, and remove address from subscription list. +// Note: If there are duplicate entries, only finds the first in the list (this +// is a defined restriction - most environments don't allow duplicates to be +// created.) +int +ACE_SOCK_Dgram_Mcast::unsubscribe (const ACE_INET_Addr &mcast_addr, + const ACE_TCHAR *net_if, + int protocol_family, + int protocol) +{ + ACE_TRACE ("ACE_SOCK_Dgram_Mcast::unsubscribe"); + + ACE_UNUSED_ARG (protocol_family); + ACE_UNUSED_ARG (protocol); + + return this->leave (mcast_addr, net_if); +} + +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::unsubscribe (void) +{ + ACE_TRACE ("ACE_SOCK_Dgram_Mcast::unsubscribe"); + + // Can't implement this reliably without keeping an expensive list, + // and can't close the socket since the caller may want to continue + // using the socket to send() or join() new groups. Even if we + // wanted to be clever and reopen the socket, we'd need to know what + // options had been set, and reset them--and we have no way of doing + // that either. :-( + // Should this return -1? + ACE_ERROR_RETURN ((LM_INFO, + ACE_LIB_TEXT ("ACE_SOCK_Dgram_Mcast::unsubscribe (void) ") + ACE_LIB_TEXT ("has been deprecated. You must either ") + ACE_LIB_TEXT ("close to socket to unsubscribe to all ") + ACE_LIB_TEXT ("or unsubscribe to each individually.\n")), + 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 |