diff options
author | crodrigu <crodrigu@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 2002-06-25 23:25:58 +0000 |
---|---|---|
committer | crodrigu <crodrigu@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 2002-06-25 23:25:58 +0000 |
commit | 116bf25582c26fb11871c8f8c541a06dfa7e21ac (patch) | |
tree | b2b6ccbc35c6b96573750c74b1bdf2ad244c41ea /ace/SOCK_Dgram_Mcast.cpp | |
parent | 84053c2e2800d063f62fdb308cc24cb6839f02b4 (diff) | |
download | ATCD-116bf25582c26fb11871c8f8c541a06dfa7e21ac.tar.gz |
ChangeLogTag: Tue Jun 25 23:22:09 UTC 2002 Craig Rodrigues <crodrigu@bbn.com>
Diffstat (limited to 'ace/SOCK_Dgram_Mcast.cpp')
-rw-r--r-- | ace/SOCK_Dgram_Mcast.cpp | 434 |
1 files changed, 413 insertions, 21 deletions
diff --git a/ace/SOCK_Dgram_Mcast.cpp b/ace/SOCK_Dgram_Mcast.cpp index b812b3c6e8c..5261aa6122b 100644 --- a/ace/SOCK_Dgram_Mcast.cpp +++ b/ace/SOCK_Dgram_Mcast.cpp @@ -1,23 +1,415 @@ -/* $Id$ */ -/* - * The legacy ACE_SOCK_Dgram_Mcast class is a specialization of the newer - * ACE_SOCK_Dgram_Mcast_Ex templated class. - * - * This file is a vestige of the original class, which is only used to satisfy - * automated build rules and to elicit environment-specific template support - * behavior, if necessary. - */ - -// Build the "real" class files. #include "ace/SOCK_Dgram_Mcast.h" +#include "ace/INET_Addr.h" +#include "ace/ACE.h" -// Instantiate SOCK_Dgram_Mcast_Ex internal templates, if necessary. -#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) -template class ACE_SOCK_Dgram_Mcast_Ex<ACE_SYNCH_MUTEX>; -template class ACE_DLList_Iterator<ip_mreq>; -template class ACE_DLList<ip_mreq>; -#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) -#pragma instantiate ACE_SOCK_Dgram_Mcast_Ex<ACE_SYNCH_MUTEX>; -#pragma instantiate ACE_DLList_Iterator<ip_mreq> -#pragma instantiate ACE_DLList<ip_mreq> -#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ +#if defined (ACE_LACKS_INLINE_FUNCTIONS) +#include "ace/SOCK_Dgram_Mcast.i" +#endif /* ACE_LACKS_INLINE_FUNCTIONS */ + +// 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_RCSID (ace, + SOCK_Dgram_Mcast, + "$Id$") + +ACE_ALLOC_HOOK_DEFINE(ACE_SOCK_Dgram_Mcast) + +void +ACE_SOCK_Dgram_Mcast::dump (void) const +{ + ACE_TRACE ("ACE_SOCK_Dgram_Mcast::dump"); +} + +// Dummy default constructor... + +ACE_SOCK_Dgram_Mcast::ACE_SOCK_Dgram_Mcast (void) +{ + ACE_TRACE ("ACE_SOCK_Dgram_Mcast::ACE_SOCK_Dgram_Mcast"); +} + +int +ACE_SOCK_Dgram_Mcast::open (const ACE_Addr &mcast_addr, + int protocol_family, + int protocol, + int reuse_addr) +{ + ACE_TRACE ("ACE_SOCK_Dgram_Mcast::open"); + + // Make a copy of address to use in the <send> methods. Note: Sun + // C++ 4.2 needs the useless const_cast. + this->mcast_addr_.set (ACE_reinterpret_cast (const ACE_INET_Addr &, + ACE_const_cast (ACE_Addr &, + mcast_addr))); + // Only perform the <open> initialization if we haven't been opened + // earlier. + if (this->get_handle () == ACE_INVALID_HANDLE) + { + if (ACE_SOCK::open (SOCK_DGRAM, + protocol_family, + protocol, + reuse_addr) == -1) + return -1; + + int one = 1; + if (reuse_addr + && this->ACE_SOCK::set_option (SOL_SOCKET, + SO_REUSEADDR, + &one, + sizeof one) == -1) + return -1; +#if defined (SO_REUSEPORT) + else if (this->ACE_SOCK::set_option (SOL_SOCKET, + SO_REUSEPORT, + &one, + sizeof one) == -1) + return -1; +#endif /* SO_REUSEPORT */ + + // Create an address to bind the socket to. + ACE_INET_Addr local; + +#if defined (linux) + local = this->mcast_addr_; +#else /* linux */ + if (local.set (this->mcast_addr_.get_port_number ()) == -1) + return -1; +#endif /* linux */ + + if (ACE_SOCK_Dgram::shared_open (local, + protocol_family) == -1) + return -1; + } + + return 0; +} + +int +ACE_SOCK_Dgram_Mcast::subscribe_ifs (const ACE_INET_Addr &mcast_addr, + const ACE_TCHAR *net_if, + int protocol_family, + int protocol, + int reuse_addr) +{ + ACE_TRACE ("ACE_SOCK_Dgram_Mcast::subscribe_ifs"); +#if defined (ACE_WIN32) + // Windows NT's winsock has trouble with multicast subscribes in the + // presence of multiple network interfaces when the IP address is + // given as INADDR_ANY. It will pick the first interface and only + // accept mcast there. So, to work around this, cycle through all + // of the interfaces known and subscribe to all the non-loopback + // ones. + // + // Note that this only needs to be done on NT, but there's no way to + // tell at this point if the code will be running on NT - only if it + // is compiled for NT-only or for NT/95, and that doesn't really + // help us. It doesn't hurt to do this on Win95, it's just a little + // slower than it normally would be. + // + // NOTE - <ACE::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 <ACE::get_ip_interfaces> works with + // MSVC. The reliable way of getting the interface list is + // available only with MSVC 5. + + if (net_if == 0) + { + 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"), + protocol_family, + protocol) == 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_ip_address() == INADDR_LOOPBACK) + continue; + if (this->subscribe (mcast_addr, + reuse_addr, + ACE_TEXT_CHAR_TO_TCHAR + (if_addrs[if_cnt].get_host_addr()), + protocol_family, + protocol) == 0) + ++nr_subscribed; + } + + delete [] if_addrs; + + if (nr_subscribed == 0) + { + errno = ENODEV; + return -1; + } + else + // 1 indicates a "short-circuit" return. This handles the + // rather bizarre semantics of checking all the interfaces on + // NT. + return 1; + } +#else + ACE_UNUSED_ARG (mcast_addr); + ACE_UNUSED_ARG (net_if); + ACE_UNUSED_ARG (protocol_family); + ACE_UNUSED_ARG (protocol); + ACE_UNUSED_ARG (reuse_addr); +#endif /* ACE_WIN32 */ + // Otherwise, do it like everyone else... + + return 0; +} + +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"); + + if (this->open (mcast_addr, + protocol_family, + protocol, + reuse_addr) == -1) + return -1; + + int result = this->subscribe_ifs (mcast_addr, + net_if, + protocol_family, + protocol, + reuse_addr); + // Check for the "short-circuit" return value of 1 (for NT). This + // handles the rather bizarre semantics of checking all the + // interfaces on NT. + if (result != 0) + return result; + + ip_mreq multicast_address; + + // Create multicast request. + result = this->make_multicast_address_i (mcast_addr, + multicast_address, + net_if); + if (result != 0) + return result; + + // Tell network device driver to read datagrams with a + // multicast_address IP interface. + result = this->ACE_SOCK::set_option (IPPROTO_IP, + IP_ADD_MEMBERSHIP, + &multicast_address, + sizeof multicast_address); + if (result != 0) + return result; + + // Set the <mcast_request_if_> field. This is mostly a convenience + // for applications that subscribe to a single multicast group, + // using the internal field allows them to simply call the + // unsubscribe() operation. + // Notice that such applications are using a non-reentrant interface + // of the class, and thus should use proper synchronization for it. + this->mcast_request_if_ = multicast_address; + + return 0; +} + +int +ACE_SOCK_Dgram_Mcast::unsubscribe_ifs (const ACE_INET_Addr &mcast_addr, + const ACE_TCHAR *net_if, + int protocol_family, + int protocol) +{ + ACE_TRACE ("ACE_SOCK_Dgram_Mcast::unsubscribe_ifs"); +#if defined (ACE_WIN32) + // Windows NT's winsock has trouble with multicast subscribes in the + // presence of multiple network interfaces when the IP address is + // given as INADDR_ANY. It will pick the first interface and only + // accept mcast there. So, to work around this, cycle through all + // of the interfaces known and subscribe to all the non-loopback + // ones. + // + // Note that this only needs to be done on NT, but there's no way to + // tell at this point if the code will be running on NT - only if it + // is compiled for NT-only or for NT/95, and that doesn't really + // help us. It doesn't hurt to do this on Win95, it's just a little + // slower than it normally would be. + // + // NOTE - <ACE::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 <ACE::get_ip_interfaces> works with + // MSVC. The reliable way of getting the interface list is + // available only with MSVC 5. + + if (net_if == 0) + { + 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_unsubscribed = 0; + + if (if_cnt < 2) + { + if (this->unsubscribe (mcast_addr, + ACE_LIB_TEXT ("0.0.0.0"), + protocol_family, + protocol) == 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_ip_address() == INADDR_LOOPBACK) + continue; + if (this->unsubscribe (mcast_addr, + ACE_TEXT_CHAR_TO_TCHAR + (if_addrs[if_cnt].get_host_addr()), + protocol_family, + protocol) == 0) + ++nr_unsubscribed; + } + + delete [] if_addrs; + + if (nr_unsubscribed == 0) + { + errno = ENODEV; + return -1; + } + + return 1; + } +#else + ACE_UNUSED_ARG (mcast_addr); + ACE_UNUSED_ARG (net_if); + ACE_UNUSED_ARG (protocol_family); + ACE_UNUSED_ARG (protocol); +#endif /* ACE_WIN32 */ + + return 0; +} + +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"); + int result = this->unsubscribe_ifs (mcast_addr, + net_if, + protocol_family, + protocol); + + // Check for the "short-circuit" return value of 1 (for NT). + if (result != 0) + return result; + + // Otherwise, do it like everyone else. + + ip_mreq multicast_address; + + // Create multicast request. + result = this->make_multicast_address_i (mcast_addr, + multicast_address, + net_if); + if (result != 0) + return result; + + // Tell network device driver to stop reading datagrams with the + // <mcast_addr>. + return ACE_SOCK::set_option (IPPROTO_IP, + IP_DROP_MEMBERSHIP, + &multicast_address, + sizeof multicast_address); +} + +int +ACE_SOCK_Dgram_Mcast::unsubscribe (void) +{ + ACE_TRACE ("ACE_SOCK_Dgram_Mcast::unsubscribe"); + return this->ACE_SOCK::set_option (IPPROTO_IP, + IP_DROP_MEMBERSHIP, + &this->mcast_request_if_, + sizeof this->mcast_request_if_); +} + +int +ACE_SOCK_Dgram_Mcast::make_multicast_address_i (const ACE_INET_Addr &mcast_addr, + ip_mreq &multicast_address , + const ACE_TCHAR *net_if) +{ + if (net_if != 0) + { +#if defined (ACE_WIN32) + // This port number is not necessary, just convenient + ACE_INET_Addr interface_addr; + if (interface_addr.set (mcast_addr.get_port_number (), + net_if) == -1) + return -1; + multicast_address.imr_interface.s_addr = + htonl (interface_addr.get_ip_address ()); +#else + ifreq if_address; + +#if defined (ACE_PSOS) + // Look up the interface by number, not name. + if_address.ifr_ifno = ACE_OS::atoi (net_if); +#else + ACE_OS::strcpy (if_address.ifr_name, net_if); +#endif /* defined (ACE_PSOS) */ + + if (ACE_OS::ioctl (this->get_handle (), + SIOCGIFADDR, + &if_address) == -1) + return -1; + + sockaddr_in *socket_address; + socket_address = ACE_reinterpret_cast(sockaddr_in *, + &if_address.ifr_addr); + multicast_address.imr_interface.s_addr = socket_address->sin_addr.s_addr; +#endif /* ACE_WIN32 */ + } + else + multicast_address.imr_interface.s_addr = INADDR_ANY; + + multicast_address.IMR_MULTIADDR.s_addr = htonl (mcast_addr.get_ip_address ()); + return 0; +} + +int +ACE_SOCK_Dgram_Mcast::make_multicast_address (const ACE_INET_Addr &mcast_addr, + const ACE_TCHAR *net_if) +{ + ACE_TRACE ("ACE_SOCK_Dgram_Mcast::make_multicast_address"); + + return this->make_multicast_address_i (mcast_addr, + this->mcast_request_if_, + net_if ); +} |