mirror of
https://github.com/mangosfour/server.git
synced 2025-12-11 16:37:03 +00:00
929 lines
29 KiB
C++
929 lines
29 KiB
C++
// $Id: SOCK_Dgram_Mcast.cpp 96985 2013-04-11 15:50:32Z huangh $
|
|
|
|
#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 (ACE_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_Category.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");
|
|
|
|
ACELIB_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this));
|
|
|
|
# if defined (ACE_SOCK_DGRAM_MCAST_DUMPABLE)
|
|
ACE_TCHAR addr_string[MAXNAMELEN + 1];
|
|
|
|
ACELIB_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);
|
|
ACELIB_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.
|
|
ACELIB_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.
|
|
ACELIB_DEBUG ((LM_DEBUG,
|
|
ACE_TEXT ("\taddr=%s iface=%s\n"),
|
|
addr_string,
|
|
iface_string));
|
|
}
|
|
# endif /* ACE_SOCK_DGRAM_MCAST_DUMPABLE */
|
|
ACELIB_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)
|
|
{
|
|
ACELIB_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);
|
|
ACELIB_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 /* ACE_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(ACE_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
|