diff --git a/.gitignore b/.gitignore index 74e9997a0eec6..6a9e1ff6743d2 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ *S_T.inl *.bmak *.recipe +.DS_Store .depend.* GNUmakefile* diff --git a/ACE/ace/INET_Addr.cpp b/ACE/ace/INET_Addr.cpp index 25a1fb5d74fb7..0f54fc27902f1 100644 --- a/ACE/ace/INET_Addr.cpp +++ b/ACE/ace/INET_Addr.cpp @@ -313,6 +313,10 @@ ACE_INET_Addr::ACE_INET_Addr (const ACE_INET_Addr &sa) { ACE_TRACE ("ACE_INET_Addr::ACE_INET_Addr"); this->set (sa); + if (sa.if_name_) + { + this->set_interface_name (sa.if_name_->c_str ()); + } } // Initializes a ACE_INET_Addr from a PORT_NUMBER and a 32 bit Internet @@ -1002,6 +1006,25 @@ ACE_INET_Addr::set_interface (const char *intf_name) } #endif /* ACE_LINUX && ACE_HAS_IPV6 */ +std::shared_ptr +ACE_INET_Addr::get_interface_name () const +{ + return this->if_name_; +} + +void +ACE_INET_Addr::set_interface_name (const char * if_name) +{ + if (if_name == nullptr) + { + this->if_name_.reset (); + } + else + { + this->if_name_ = std::make_shared (if_name); + } +} + const char * ACE_INET_Addr::get_host_addr (char *dst, int size) const { diff --git a/ACE/ace/INET_Addr.h b/ACE/ace/INET_Addr.h index 9172a86eb51fb..9ec1785899686 100644 --- a/ACE/ace/INET_Addr.h +++ b/ACE/ace/INET_Addr.h @@ -19,6 +19,9 @@ #endif /* ACE_LACKS_PRAGMA_ONCE */ #include "ace/Addr.h" + +#include +#include #include ACE_BEGIN_VERSIONED_NAMESPACE_DECL @@ -283,6 +286,23 @@ class ACE_Export ACE_INET_Addr : public ACE_Addr int set_interface (const char *intf_name); #endif /* (ACE_LINUX || ACE_WIN32) && ACE_HAS_IPV6 */ + /** + * For informational purposes only, get the name of the network interface to + * which this IP address is assigned (such as en0, eth2, utun4, etc.). This + * name is not used automatically by any parts of INET_Addr, nor is it stored + * in the internal IP address scope ID (see set_interface for that). It is + * applicable only in certain, limited scopes, notably calls to + * ACE::get_ip_interfaces(). + */ + std::shared_ptr get_interface_name () const; + + /** + * For informational purposes only, set the name of the network interface to + * which this IP address is assigned. See get_interface_name for more + * information. + */ + void set_interface_name (const char * if_name); + /// Return the port number, converting it into host byte-order. u_short get_port_number () const; @@ -417,6 +437,8 @@ class ACE_Export ACE_INET_Addr : public ACE_Addr // holds all of them; one is always copied to inet_addr_. std::vector inet_addrs_; std::vector::iterator inet_addrs_iter_; + + std::shared_ptr if_name_ = nullptr; }; ACE_END_VERSIONED_NAMESPACE_DECL diff --git a/ACE/ace/SOCK_Dgram.cpp b/ACE/ace/SOCK_Dgram.cpp index 8b8e1a6f0b95b..127e9cb317486 100644 --- a/ACE/ace/SOCK_Dgram.cpp +++ b/ACE/ace/SOCK_Dgram.cpp @@ -649,10 +649,16 @@ ACE_SOCK_Dgram::make_multicast_ifaddr (ip_mreq *ret_mreq, ip_mreq lmreq; // Scratch copy. if (net_if != 0) { + const char * net_if_char = ACE_TEXT_ALWAYS_CHAR (net_if); + if (ACE_OS::strstr (net_if_char, "if=") != 0) + { + net_if_char = net_if_char + 3; + } + #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) + if (interface_addr.set (mcast_addr.get_port_number (), net_if_char) == -1) { IP_ADAPTER_ADDRESSES tmp_addrs; // Initial call to determine actual memory size needed @@ -677,8 +683,8 @@ ACE_SOCK_Dgram::make_multicast_ifaddr (ip_mreq *ret_mreq, int set_result = -1; while (pAddrs && set_result == -1) { - if (ACE_OS::strcmp (ACE_TEXT_ALWAYS_CHAR (net_if), pAddrs->AdapterName) == 0 || - ACE_OS::strcmp (ACE_TEXT_ALWAYS_WCHAR (net_if), pAddrs->FriendlyName) == 0) + if (ACE_OS::strcmp (net_if_char, pAddrs->AdapterName) == 0 || + ACE_OS::strcmp (ACE_Ascii_To_Wide (net_if_char).wchar_rep (), pAddrs->FriendlyName) == 0) { PIP_ADAPTER_UNICAST_ADDRESS pUnicast = pAddrs->FirstUnicastAddress; LPSOCKADDR sa = pUnicast->Address.lpSockaddr; @@ -702,19 +708,19 @@ ACE_SOCK_Dgram::make_multicast_ifaddr (ip_mreq *ret_mreq, ACE_HTONL (interface_addr.get_ip_address ()); #else ifreq if_address; - ACE_OS::strsncpy (if_address.ifr_name, ACE_TEXT_ALWAYS_CHAR (net_if), (sizeof if_address.ifr_name)); + ACE_OS::strsncpy (if_address.ifr_name, net_if_char, (sizeof if_address.ifr_name)); if (ACE_OS::ioctl (this->get_handle (), SIOCGIFADDR, &if_address) == -1) { - // The net_if name failed to be found. It seems that older linux + // The net_if_char name failed to be found. It seems that older linux // kernals only support the actual interface name (eg. "eth0"), // not the IP address string of the interface (eg. "192.168.0.1"), // which newer kernals seem to automatically translate. // So assume that we have been given an IP Address and translate // that instead, similar to the above for windows. ACE_INET_Addr interface_addr; - if (interface_addr.set (mcast_addr.get_port_number (), net_if) == -1) + if (interface_addr.set (mcast_addr.get_port_number (), net_if_char) == -1) return -1; // Still doesn't work, unknown device specified. lmreq.imr_interface.s_addr = ACE_HTONL (interface_addr.get_ip_address ()); @@ -755,11 +761,24 @@ ACE_SOCK_Dgram::make_multicast_ifaddr6 (ipv6_mreq *ret_mreq, #if defined (ACE_WIN32) || !defined (ACE_LACKS_IF_NAMETOINDEX) if (net_if != 0) { + const char * net_if_char = ACE_TEXT_ALWAYS_CHAR (net_if); + + struct in6_addr net_if_in6_addr; + bool net_if_is_ip_address ( false ); + if (ACE_OS::strstr (net_if_char, "if=") != 0) + { + net_if_char = net_if_char + 3; + } + else + { + net_if_is_ip_address = inet_pton (AF_INET6, net_if_char, &net_if_in6_addr) == 1; + } + #if defined (ACE_WIN32) int if_ix = 0; bool const num_if = - ACE_OS::ace_isdigit (net_if[0]) && - (if_ix = ACE_OS::atoi (net_if)) > 0; + ACE_OS::ace_isdigit (net_if_char[0]) && + (if_ix = ACE_OS::atoi (net_if_char)) > 0; ULONG bufLen = 15000; // Initial size as per Microsoft char *buf = nullptr; @@ -793,10 +812,30 @@ ACE_SOCK_Dgram::make_multicast_ifaddr6 (ipv6_mreq *ret_mreq, while (pAddrs) { - if ((num_if && pAddrs->Ipv6IfIndex == static_cast(if_ix)) - || (!num_if && - (ACE_OS::strcmp (ACE_TEXT_ALWAYS_CHAR (net_if), pAddrs->AdapterName) == 0 - || ACE_OS::strcmp (ACE_TEXT_ALWAYS_WCHAR (net_if), pAddrs->FriendlyName) == 0))) + if (net_if_is_ip_address) + { + IP_ADAPTER_UNICAST_ADDRESS *uni = 0; + for (uni = pAddrs->FirstUnicastAddress; uni != 0; uni = uni->Next) + { + struct sockaddr_in6 *sin = reinterpret_cast (uni->Address.lpSockaddr); + if (std::memcmp ( + reinterpret_cast (&net_if_in6_addr), + reinterpret_cast (&sin->sin6_addr), + sizeof (in6_addr)) == 0) + { + lmreq.ipv6mr_interface = pAddrs->Ipv6IfIndex; + break; + } + } + if (lmreq.ipv6mr_interface != 0) + { + break; + } + } + else if ((num_if && pAddrs->Ipv6IfIndex == static_cast(if_ix)) + || (!num_if && + (ACE_OS::strcmp (net_if_char, pAddrs->AdapterName) == 0 + || ACE_OS::strcmp (ACE_Ascii_To_Wide (net_if_char).wchar_rep (), pAddrs->FriendlyName) == 0))) { lmreq.ipv6mr_interface = pAddrs->Ipv6IfIndex; break; @@ -809,13 +848,53 @@ ACE_SOCK_Dgram::make_multicast_ifaddr6 (ipv6_mreq *ret_mreq, #else /* ACE_WIN32 */ #ifndef ACE_LACKS_IF_NAMETOINDEX - lmreq.ipv6mr_interface = ACE_OS::if_nametoindex (ACE_TEXT_ALWAYS_CHAR (net_if)); + lmreq.ipv6mr_interface = ACE_OS::if_nametoindex (net_if_char); #endif /* ACE_LACKS_IF_NAMETOINDEX */ #endif /* ACE_WIN32 */ if (lmreq.ipv6mr_interface == 0) { - errno = EINVAL; - return -1; +#ifndef ACE_LACKS_IF_NAMETOINDEX + if (net_if_is_ip_address) + { + // net_if is an IP(v6) address, so find the interface name and *then* convert it to an interface index + ACE_INET_Addr *if_addrs = 0; + size_t if_cnt; + if (ACE::get_ip_interfaces (if_cnt, if_addrs) == 0) + { + struct sockaddr_in6 net_if_sockaddr_in6; + net_if_sockaddr_in6.sin6_family = AF_INET6; + net_if_sockaddr_in6.sin6_addr = net_if_in6_addr; + net_if_sockaddr_in6.sin6_port = 0; + net_if_sockaddr_in6.sin6_flowinfo = 0; + ACE_INET_Addr net_if_ace_inet_addr ( + reinterpret_cast (&net_if_sockaddr_in6), + sizeof(sockaddr_in6)); + + while (if_cnt > 0) + { + // Convert to 0-based for indexing, next loop check + --if_cnt; + + if (net_if_ace_inet_addr.is_ip_equal (if_addrs[if_cnt])) + { + auto if_name = if_addrs[if_cnt].get_interface_name (); + if (if_name) + { + lmreq.ipv6mr_interface = ACE_OS::if_nametoindex (if_name->c_str ()); + } + break; + } + } + } + delete [] if_addrs; + } + + if (lmreq.ipv6mr_interface == 0) +#endif /* ACE_LACKS_IF_NAMETOINDEX */ + { + errno = EINVAL; + return -1; + } } } #else /* ACE_WIN32 || !ACE_LACKS_IF_NAMETOINDEX */ diff --git a/ACE/ace/Sock_Connect.cpp b/ACE/ace/Sock_Connect.cpp index ae3ce1ace0f32..2bc31e912fdb3 100644 --- a/ACE/ace/Sock_Connect.cpp +++ b/ACE/ace/Sock_Connect.cpp @@ -607,6 +607,7 @@ get_ip_interfaces_getifaddrs (size_t &count, addrs[count].set ((u_short) 0, addr->sin_addr.s_addr, 0); + addrs[count].set_interface_name (p_if->ifa_name); ++count; } } @@ -619,8 +620,9 @@ get_ip_interfaces_getifaddrs (size_t &count, // Skip the ANY address if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr)) { - addrs[count].set(reinterpret_cast (addr), - sizeof(sockaddr_in6)); + addrs[count].set (reinterpret_cast (addr), + sizeof(sockaddr_in6)); + addrs[count].set_interface_name (p_if->ifa_name); ++count; } } diff --git a/TAO/bin/tao_other_tests.lst b/TAO/bin/tao_other_tests.lst index 8f185925c99b8..b8a78aa15f4af 100644 --- a/TAO/bin/tao_other_tests.lst +++ b/TAO/bin/tao_other_tests.lst @@ -239,6 +239,7 @@ TAO/orbsvcs/tests/Miop/McastPreferredInterfaces/run_test.pl: !MINIMUM !CORBA_E_C TAO/orbsvcs/tests/Miop/McastPreferredInterfaces/run_test_ipv6.pl: !MINIMUM !CORBA_E_COMPACT !CORBA_E_MICRO !STATIC !NO_MCAST IPV6 !NO_LOOPBACK_MCAST TAO/orbsvcs/tests/Miop/McastFragmentation/run_test.pl: !MINIMUM !CORBA_E_COMPACT !CORBA_E_MICRO !STATIC !NO_MCAST TAO/orbsvcs/tests/Miop/McastFragmentation/run_test_ipv6.pl: IPV6 !MINIMUM !CORBA_E_COMPACT !CORBA_E_MICRO !STATIC !NO_MCAST +TAO/orbsvcs/tests/Miop/McastListenerInterfaces/run_test.pl: IPV6 !MINIMUM !CORBA_E_COMPACT !CORBA_E_MICRO !STATIC !NO_MCAST !NO_LOOPBACK_MCAST # The following 2 tests use dynamic loading to change the default reactor on Windows !VxWorks !VxWorks_RTP !LabVIEW_RT TAO/orbsvcs/tests/LoadBalancing/GenericFactory/Application_Controlled/run_test.pl: !MINIMUM !CORBA_E_COMPACT !CORBA_E_MICRO !DISABLE_INTERCEPTORS !STATIC !ACE_FOR_TAO !LynxOS !ST TAO/orbsvcs/tests/LoadBalancing/GenericFactory/Infrastructure_Controlled/run_test.pl: !MINIMUM !CORBA_E_COMPACT !CORBA_E_MICRO !DISABLE_INTERCEPTORS !STATIC !ACE_FOR_TAO !LynxOS diff --git a/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/.gitignore b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/.gitignore new file mode 100644 index 0000000000000..315535d9239d9 --- /dev/null +++ b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/.gitignore @@ -0,0 +1,8 @@ +TestC.cpp +TestC.h +TestC.inl +TestS.cpp +TestS.h +client +server +if_addrs_helper diff --git a/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/McastListenerInterfaces.mpc b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/McastListenerInterfaces.mpc new file mode 100644 index 0000000000000..0366722f9a363 --- /dev/null +++ b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/McastListenerInterfaces.mpc @@ -0,0 +1,43 @@ +// -*- MPC -*- +project(*IDL): taoidldefaults { + IDL_Files { + Test.idl + } + custom_only = 1 +} + +project(*Server): orbsvcsexe, portablegroup { + exename = server + after += *IDL + + Source_Files { + Test_impl.cpp + server.cpp + TestC.cpp + TestS.cpp + } + IDL_Files { + } +} + +project(*Client): taoexe { + exename = client + after += *IDL + + Source_Files { + client.cpp + TestC.cpp + } + IDL_Files { + } +} + +project(*Helper) { + exename = if_addrs_helper + + Header_Files { + } + Source_Files { + if_addrs_helper.cpp + } +} diff --git a/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/README b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/README new file mode 100644 index 0000000000000..3a3759182e77d --- /dev/null +++ b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/README @@ -0,0 +1,15 @@ +This test verifies the proper behavior of the -ORBListenerInterfaces option on IPv4 and IPv6 with values *=ip_addr, +*=interface_name, and CopyPreferredInterfaces. The latter requires also using the -ORBPreferredInterface option. +It does so by doing the following: + +1. Calling the local helper utility if_addrs_helper to determine the system name of the loopback interface and + ethernet-like interface(s) with routable IPv4 and IPv6 addresses, as well as the correct IP addresses for those + interfaces. + +2. Creating a matrix of tests across these interfaces, IP addresses, and the *=ip_addr, *=interface_name, and + CopyPreferredInterfaces option values. + +3. Executing the matrix of tests and asserting a specific failure (but no other failures) for loopback interfaces and + no failures for non-loopback interfaces. Specifically, for loopback interfaces, the server process should never + receive the instruction to shut down from the client, and so the test should time out waiting on the server process + and elect to kill it. diff --git a/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/Test.idl b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/Test.idl new file mode 100644 index 0000000000000..acb2b6a1aea48 --- /dev/null +++ b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/Test.idl @@ -0,0 +1,12 @@ +#ifndef _TEST_IDL_ +#define _TEST_IDL_ + +module Test +{ + interface Server + { + oneway void shutdown (); + }; +}; + +#endif // _TEST_IDL_ diff --git a/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/Test_impl.cpp b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/Test_impl.cpp new file mode 100644 index 0000000000000..d59755cc7cebd --- /dev/null +++ b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/Test_impl.cpp @@ -0,0 +1,12 @@ +#include "Test_impl.h" + + +Server_impl::Server_impl (CORBA::ORB_ptr orb) + : orb_ (CORBA::ORB::_duplicate (orb)) +{ +} + +void Server_impl::shutdown () +{ + this->orb_->shutdown (false); +} diff --git a/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/Test_impl.h b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/Test_impl.h new file mode 100644 index 0000000000000..1fe5ec980c9f8 --- /dev/null +++ b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/Test_impl.h @@ -0,0 +1,17 @@ +#ifndef TEST_IMPL_HPP +#define TEST_IMPL_HPP + +#include "TestS.h" + +class Server_impl : public virtual POA_Test::Server +{ +public: + Server_impl (CORBA::ORB_ptr orb); + + virtual void shutdown (); + +private: + CORBA::ORB_var orb_; +}; + +#endif /* TEST_IMPL_HPP */ diff --git a/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/client.cpp b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/client.cpp new file mode 100644 index 0000000000000..08eddcccd171b --- /dev/null +++ b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/client.cpp @@ -0,0 +1,62 @@ +#include "ace/OS_NS_stdio.h" +#include "ace/Get_Opt.h" +#include "TestC.h" + + +const ACE_TCHAR *ior_server = 0; + +int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opts (argc, argv, ACE_TEXT ("k:")); + int c; + + while ((c = get_opts ()) != -1) + switch (c) + { + case 'k': + ior_server = get_opts.opt_arg (); + break; + case '?': + default: + ACE_ERROR_RETURN ((LM_ERROR, + "usage: %s " + "-k file://" + "\n", + argv [0]), + 1); + } + // Indicates successful parsing of the command line + return 0; +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + try + { + CORBA::ORB_var orb = CORBA::ORB_init (argc, argv); + + if (parse_args (argc, argv) != 0) + ACE_ERROR_RETURN ((LM_ERROR, "Wrong arguments\n"), 2); + + CORBA::Object_var tmp = orb->string_to_object (ior_server); + + Test::Server_var server = + Test::Server::_unchecked_narrow (tmp.in ()); + + if (CORBA::is_nil (server.in ())) + ACE_ERROR_RETURN ((LM_ERROR, "Nil reference\n"), 3); + + server->shutdown (); + + orb->destroy (); + } + catch (const ::CORBA::Exception &ex) + { + ex._tao_print_exception ("Exception in client.cpp:\n"); + return 99; + } + + return 0; +} diff --git a/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/if_addrs_helper.cpp b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/if_addrs_helper.cpp new file mode 100644 index 0000000000000..b141e0ff0bbeb --- /dev/null +++ b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/if_addrs_helper.cpp @@ -0,0 +1,434 @@ +#include +#include + +#define IPV4_MIN_LENGTH 7 +#define IPV6_MIN_LENGTH 3 + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) + +#include +#include +#include +#include +#include +#include +#include + +#include "ace/OS_main.h" +#include "ace/ace_wchar.h" + +#pragma comment(lib, "IPHLPAPI.lib") +#pragma comment(lib, "ws2_32.lib") + +#define WORKING_BUFFER_SIZE 15000 +#define MAX_TRIES 3 + +#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) +#define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) + +class WinsockHelper + { + public: + WinsockHelper () : status (-1) + { + WORD wVersionRequested = MAKEWORD (2, 2); + WSADATA wsaData; + + int err = WSAStartup (wVersionRequested, &wsaData); + if (err != 0) + { + ::std::cout << "WSAStartup failed with error: " << err << ::std::endl; + this->status = 1; + return; + } + + this->status = 0; + } + + ~WinsockHelper () + { + WSACleanup (); + } + + int status; + }; + +class AddressesHelper + { + public: + AddressesHelper () : pAddresses (nullptr), status (-2) + { + DWORD dwRetVal = 0; + ULONG iterations = 0; + + // Allocate a 15 KB buffer to start with. + ULONG outBufLen = WORKING_BUFFER_SIZE; + + do + { + this->pAddresses = reinterpret_cast (MALLOC (outBufLen)); + if (this->pAddresses == nullptr) + { + ::std::cout << "Memory allocation failed for IP_ADAPTER_ADDRESSES struct" << ::std::endl; + this->status = 2; + return; + } + + dwRetVal = ::GetAdaptersAddresses (AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nullptr, this->pAddresses, &outBufLen); + + if (dwRetVal == ERROR_BUFFER_OVERFLOW) + { + FREE (this->pAddresses); + this->pAddresses = nullptr; + } + else + { + break; + } + + iterations++; + } while (dwRetVal == ERROR_BUFFER_OVERFLOW && iterations < MAX_TRIES); + + if (dwRetVal != NO_ERROR) + { + ::std::cout << "Call to GetAdaptersAddresses failed with error: " << dwRetVal << ::std::endl; + this->status = 2; + return; + } + + this->status = 0; + } + + ~AddressesHelper () + { + FREE (this->pAddresses); + } + + PIP_ADAPTER_ADDRESSES pAddresses; + int status; + }; + +bool +is_loopback (const struct in6_addr * a) + { + // ::1 + return (a->s6_words[0] == 0 && + a->s6_words[1] == 0 && + a->s6_words[2] == 0 && + a->s6_words[3] == 0 && + a->s6_words[4] == 0 && + a->s6_words[5] == 0 && + a->s6_words[6] == 0 && + a->s6_bytes[14] == 0 && + a->s6_bytes[15] == 0x01); + } + +bool +is_link_local (const struct in6_addr * a) + { + // fe80::/64 + return (a->s6_bytes[0] == 0xfe && + a->s6_bytes[1] == 0x80 && + a->s6_words[1] == 0 && + a->s6_words[2] == 0 && + a->s6_words[3] == 0); + } + +bool +is_site_local (const struct in6_addr * a) + { + // fec0::/16 + return (a->s6_bytes[0] == 0xfe && + a->s6_bytes[1] == 0xc0); + } + +bool +is_v4_mapped (const struct in6_addr * a) + { + // ::ffff:0:0/96 + return (a->s6_words[0] == 0 && + a->s6_words[1] == 0 && + a->s6_words[2] == 0 && + a->s6_words[3] == 0 && + a->s6_words[4] == 0 && + a->s6_words[5] == 0xffff); + } + +bool +is_v4_translated (const struct in6_addr * a) + { + // ::ffff:0:0:0/96 + return (a->s6_words[0] == 0 && + a->s6_words[1] == 0 && + a->s6_words[2] == 0 && + a->s6_words[3] == 0 && + a->s6_words[4] == 0xffff && + a->s6_words[5] == 0); + } + +int +address_to_string (LPSOCKADDR sa, ::std::string & host) + { + static const DWORD ipv4_struct_length (sizeof (struct sockaddr_in)); + static const DWORD ipv6_struct_length (sizeof (struct sockaddr_in6)); + static const DWORD host_length = 100; + + ::std::string::size_type min_length (IPV4_MIN_LENGTH); + DWORD struct_length (ipv4_struct_length); + if (sa->sa_family == AF_INET6) + { + min_length = IPV6_MIN_LENGTH; + struct_length = ipv6_struct_length; + } + + DWORD length (host_length); + wchar_t host_wchars [host_length]; + + int err = ::WSAAddressToStringW (sa, struct_length, nullptr, host_wchars, &length); + if (err != 0) + { + ::std::cout << "WARNING: WSAAddressToString() failed: WSAGetLastError() = " << + ::WSAGetLastError () << ::std::endl; + return 1; + } + ::std::wstring whost (host_wchars); + // IP addresses never need > 1 byte/character + host = ::std::wstring_convert<::std::codecvt_utf8, wchar_t> ().to_bytes (whost); + if (host.length () < min_length) + { + ::std::cout << "WARNING: WSAAddressToString() returned a too-short value: " << host << ::std::endl; + return 1; + } + + return 0; + } + +int +ACE_TMAIN (int, ACE_TCHAR * []) + { + WinsockHelper winsock; + if (winsock.status != 0) + { + return winsock.status; + } + + AddressesHelper addresses; + if (addresses.status != 0) + { + return addresses.status; + } + + bool hasLOv4 (false), hasETHv4 (false), hasLOv6 (false), hasETHv6 (false); + + while (addresses.pAddresses) + { + if (addresses.pAddresses->OperStatus != IfOperStatusUp) + { + continue; + } + + IP_ADAPTER_UNICAST_ADDRESS *uni = 0; + for (uni = addresses.pAddresses->FirstUnicastAddress; uni != 0; uni = uni->Next) + { + LPSOCKADDR sa = uni->Address.lpSockaddr; + if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6) + { + continue; + } + + ::std::string host; + if (address_to_string (sa, host) != 0) + { + continue; + } + + if (sa->sa_family == AF_INET) + { + if (host.rfind ("127.", 0) == 0) + { + if (!hasLOv4) + { + hasLOv4 = true; + ::std::cout << "LOv4: " << addresses.pAddresses->AdapterName << " " << host << ::std::endl; + } + } + else if (host.rfind ("0.", 0) != 0 && + host.rfind ("169.254.", 0) != 0) + { + if (!hasETHv4) + { + hasETHv4 = true; + ::std::cout << "ETHv4: " << addresses.pAddresses->AdapterName << " " << host << ::std::endl; + } + } + } + else + { + struct sockaddr_in6 *sin = reinterpret_cast (sa); + if (is_loopback (&sin->sin6_addr)) + { + if (!hasLOv6) + { + hasLOv6 = true; + ::std::cout << "LOv6: " << addresses.pAddresses->AdapterName << " " << host << ::std::endl; + } + } + else if (!is_link_local (&sin->sin6_addr) && + !is_site_local (&sin->sin6_addr) && + !is_v4_mapped (&sin->sin6_addr) && + !is_v4_translated (&sin->sin6_addr)) + { + if (!hasETHv6) + { + hasETHv6 = true; + ::std::cout << "ETHv6: " << addresses.pAddresses->AdapterName << " " << host << ::std::endl; + } + } + } + + if (hasLOv4 && hasLOv6 && hasETHv4 && hasETHv6) + { + break; + } + } + + addresses.pAddresses = addresses.pAddresses->Next; + } + + return 0; + } + +#else + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ace/OS_main.h" +#include "ace/ace_wchar.h" + +int +address_to_string (struct sockaddr *ifa_addr, ::std::string & host) + { + static const size_t ipv4_struct_length (sizeof (struct sockaddr_in)); + static const size_t ipv6_struct_length (sizeof (struct sockaddr_in6)); + static const size_t host_length = 100; + + ::std::string::size_type min_length (IPV4_MIN_LENGTH); + size_t struct_length (ipv4_struct_length); + if (ifa_addr->sa_family == AF_INET6) + { + min_length = IPV6_MIN_LENGTH; + struct_length = ipv6_struct_length; + } + + char host_chars [host_length]; + + int err = ::getnameinfo (ifa_addr, struct_length, host_chars, host_length, 0, 0, NI_NUMERICHOST); + if (err != 0) + { + ::std::cout << "WARNING: getnameinfo() failed: " << ::gai_strerror (err) << ::std::endl; + return 1; + } + + host = ::std::string (host_chars); + if (host.length () < min_length) + { + ::std::cout << "WARNING: getnameinfo() returned a too-short value: " << host << ::std::endl; + return 1; + } + + return 0; + } + +int +ACE_TMAIN (int, ACE_TCHAR * []) + { + struct ifaddrs *ifaddr; + if (::getifaddrs (&ifaddr) == -1) + { + ::perror ("getifaddrs"); + return 1; + } + + bool hasLOv4 (false), hasETHv4 (false), hasLOv6 (false), hasETHv6 (false); + + for (struct ifaddrs *ifa = ifaddr; ifa != 0; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr == 0 || (ifa->ifa_flags & IFF_UP) != IFF_UP || ( + ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6)) + { + continue; + } + + ::std::string host; + if (address_to_string (ifa->ifa_addr, host) != 0) + { + continue; + } + + if (ifa->ifa_addr->sa_family == AF_INET) + { + if (host.rfind ("127.", 0) == 0) + { + if (!hasLOv4) + { + hasLOv4 = true; + ::std::cout << "LOv4: " << ifa->ifa_name << " " << host << ::std::endl; + } + } + else if (host.rfind ("0.", 0) != 0 && + host.rfind ("169.254.", 0) != 0) + { + if (!hasETHv4) + { + hasETHv4 = true; + ::std::cout << "ETHv4: " << ifa->ifa_name << " " << host << ::std::endl; + } + } + } + + else + { + struct sockaddr_in6 *addr = reinterpret_cast (ifa->ifa_addr); + if (IN6_IS_ADDR_LOOPBACK (&addr->sin6_addr) && (ifa->ifa_flags & IFF_LOOPBACK) == IFF_LOOPBACK) + { + if (!hasLOv6) + { + hasLOv6 = true; + ::std::cout << "LOv6: " << ifa->ifa_name << " " << host << ::std::endl; + } + } + else if (!IN6_IS_ADDR_UNSPECIFIED (&addr->sin6_addr) && + !IN6_IS_ADDR_LINKLOCAL (&addr->sin6_addr) && + !IN6_IS_ADDR_SITELOCAL (&addr->sin6_addr) && + !IN6_IS_ADDR_V4MAPPED (&addr->sin6_addr) && + !IN6_IS_ADDR_V4COMPAT (&addr->sin6_addr) && + !IN6_IS_ADDR_MC_NODELOCAL (&addr->sin6_addr)) + { + if (!hasETHv6) + { + hasETHv6 = true; + ::std::cout << "ETHv6: " << ifa->ifa_name << " " << host << ::std::endl; + } + } + } + + if (hasLOv4 && hasLOv6 && hasETHv4 && hasETHv6) + { + break; + } + } + + ::freeifaddrs (ifaddr); + + return hasETHv4 || hasETHv6 ? 0 : 1; + } + +#endif diff --git a/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/run_test.pl b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/run_test.pl new file mode 100644 index 0000000000000..786b826bf8178 --- /dev/null +++ b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/run_test.pl @@ -0,0 +1,288 @@ +eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' + & eval 'exec perl -S $0 $argv:q' + if 0; + +# -*- perl -*- + +use lib "$ENV{'ACE_ROOT'}/bin"; +use PerlACE::TestTarget; +use Env; + +my $IOR_BASE = "server.ior"; +my $SVC_CONF_BASE = "miop_svc.conf"; +my $ATTACHING_DEBUGGER = $ENV{ATTACHING_DEBUGGER} eq "true" ? 1 : 0; +my $COPY_PREFERRED_INTERFACES = "CopyPreferredInterfaces"; + +my $LOOPBACK = 2; +my $PUBLIC = 3; +my $IPv4 = 4; +my $IPv6 = 6; + +## FF00::/16, FF01::/16, FF02::/16, and FF0F::/16 are prohibited/reserved multicast scopes which TAO cannot use on +## many platforms. +my %MULTICAST_ADDRESSES = ( + $IPv4 => "225.1.1.8", + $IPv6 => "[FF03::25:334]", +); + +# { "version" => $IPv4|$IPv6, "type" => $LOOPBACK|$PUBLIC, "interface" => [if_name], "address" => [ip_addr], "via" => [if_name|ip_addr]} +my @TESTS = (); + +my $helper_output = `if_addrs_helper`; +my $helper_rc = $?; +if ($helper_rc == 0) { + my @lines = split (/\n/, $helper_output); + for my $line (@lines) { + $line =~ s/^\s+|\s+$//g; + my ($type, $result) = split(/:/, $line, 2); + $result =~ s/^\s+|\s+$//g; + if ($type eq "LOv4" || $type eq "LOv6" || $type eq "ETHv4" || $type eq "ETHv6") { + ($if_name, $addr) = split(/ /, $result, 2); + push @TESTS, { + "version" => $type eq "LOv6" || $type eq "ETHv6" ? $IPv6 : $IPv4, + "type" => $type eq "LOv4" || $type eq "LOv6" ? $LOOPBACK : $PUBLIC, + "interface" => $if_name, + "address" => $addr, + "via" => "*=$addr", + $COPY_PREFERRED_INTERFACES => 0, + }; + push @TESTS, { + "version" => $type eq "LOv6" || $type eq "ETHv6" ? $IPv6 : $IPv4, + "type" => $type eq "LOv4" || $type eq "LOv6" ? $LOOPBACK : $PUBLIC, + "interface" => $if_name, + "address" => $addr, + "via" => "*=$if_name", + $COPY_PREFERRED_INTERFACES => 0, + }; + if ($type eq "ETHv4" || $type eq "ETHv6") { + push @TESTS, { + "version" => $type eq "LOv6" || $type eq "ETHv6" ? $IPv6 : $IPv4, + "type" => $type eq "LOv4" || $type eq "LOv6" ? $LOOPBACK : $PUBLIC, + "interface" => $if_name, + "address" => $addr, + "via" => "*=$addr", + $COPY_PREFERRED_INTERFACES => 1, + }; + push @TESTS, { + "version" => $type eq "LOv6" || $type eq "ETHv6" ? $IPv6 : $IPv4, + "type" => $type eq "LOv4" || $type eq "LOv6" ? $LOOPBACK : $PUBLIC, + "interface" => $if_name, + "address" => $addr, + "via" => "*=$if_name", + $COPY_PREFERRED_INTERFACES => 1, + }; + } + } else { + print "$line\n"; + } + } + + if (!@TESTS) { + print "ERROR: Cannot run this test because if_addrs_helper did not return any useful results."; + if ($helper_output) { + print " Output from if_addrs_helper:\n$helper_output"; + } else { + print "\n"; + } + exit 1; + } +} else { + print "ERROR: Cannot run this test because if_addrs_helper failed."; + if ($helper_output) { + print " Output from if_addrs_helper:\n$helper_output"; + } else { + print "\n"; + } + exit 1; +} + +sub testToString ($) { + my $test = shift; + + return "" . + ($test->{"version"} == $IPv4 ? "IPv4" : "IPv6") . ", " . + ($test->{"type"} == $LOOPBACK ? "loopback interface" : "public interface") . ", interface '" . + $test->{"interface"} . "', address '" . $test->{"address"} . "', multicast address '" . + $MULTICAST_ADDRESSES{$test->{"version"}} . "', via '" . $test->{"via"} ."', $COPY_PREFERRED_INTERFACES = " . + ($test->{$COPY_PREFERRED_INTERFACES} ? "true" : "false"); + ; +} + +sub timedWaitWrapper ($$) { + # For some reason, ->TimedWait does not change the RUNNING flag on processes that have already stopped, making + # future commands against those processes think they are still running and unable to determine otherwise. This + # wrapper implementes the behavior that this test needs, but that might break other tests (hence not changing + # TimedWait itself). + my $process = shift; + my $timeout = shift; + + my $rc = $process->TimedWait ($timeout); + if ($rc != -1) { + $process->{RUNNING} = 0; + } + return $rc; +} + +sub startWaitIntervalWrapper($$) { + my $process = shift; + my $timeout_override = shift; + + if ($timeout_override > 0) { + #print "DEBUG: Using timeout override $timeout_override\n"; + return $timeout_override; + } + my $timeout = $process->ProcessStartWaitInterval(); + #print "DEBUG: Using default timeout $timeout\n"; + return $timeout; +} + +print "NOTE: Will run the following tests: \n"; +for my $test (@TESTS) { + print " - " . testToString ($test) . "\n"; +} +print "\n"; + +my $status = 0; +my $i = 1; + +for my $test (@TESTS) { + if ($i > 1) { + print "--------------------------------------------------------------------------------\n\n"; + } + print "////// RUNNING TEST #$i //////\nDetails: " . testToString ($test) . "\n"; + $i++; + + my $timeout_override = 0; + if ($ATTACHING_DEBUGGER) { + print "Enter a timeout override when the debugger is ready (or just press ENTER to use default timeout): "; + my $timeout_str = ; + if ($timeout_str) { + chomp $timeout_str; + if ($timeout_str) { + $timeout_override = 0 + $timeout_str; + } + } + } + + my $unicast_address = $test->{"address"}; + my $multicast_address = $MULTICAST_ADDRESSES{$test->{"version"}}; + my $via = $test->{$COPY_PREFERRED_INTERFACES} ? $COPY_PREFERRED_INTERFACES : $test->{"via"}; + + my $svc_conf = " +dynamic UIPMC_Factory Service_Object * TAO_PortableGroup:_make_TAO_UIPMC_Protocol_Factory() \"-ORBListenerInterfaces $via\" +static Resource_Factory \"-ORBProtocolFactory IIOP_Factory -ORBProtocolFactory UIPMC_Factory\" +dynamic PortableGroup_Loader Service_Object * TAO_PortableGroup:_make_TAO_PortableGroup_Loader() \"\" +"; + if ($ATTACHING_DEBUGGER) { + print "DEBUG: $svc_conf"; + } + + my $server_box = PerlACE::TestTarget::create_target (1) || die "Creating server target failed\n"; + my $client_box = PerlACE::TestTarget::create_target (2) || die "Creating client target failed\n"; + my $server_ior_file = $server_box->LocalFile ($IOR_BASE); + my $client_ior_file = $client_box->LocalFile ($IOR_BASE); + my $server_svc_conf_file = $server_box->LocalFile ($SVC_CONF_BASE); + my $client_svc_conf_file = $client_box->LocalFile ($SVC_CONF_BASE); + + $server_box->DeleteFile ($IOR_BASE); + $client_box->DeleteFile ($IOR_BASE); + $server_box->DeleteFile ($SVC_CONF_BASE); + $client_box->DeleteFile ($SVC_CONF_BASE); + + open(FH, '>', $SVC_CONF_BASE) or die "Could not open file $SVC_CONF_BASE for writing: $!"; + print FH $svc_conf; + close(FH); + + $server_box->PutFile ($SVC_CONF_BASE); + $client_box->PutFile ($SVC_CONF_BASE); + + my $port = $server_box->RandomPort (); + + my $server = $server_box->CreateProcess ( + "server", + "-o $server_ior_file -u corbaloc:miop:1.0\@1.0-foo-1/$multicast_address:$port -ORBSvcConf $server_svc_conf_file" + ); + my $client = undef; + if ($test->{$COPY_PREFERRED_INTERFACES} ) { + $via = $test->{"via"}; + print "Testing with '-ORBPreferredInterfaces $via' as well\n"; + $client = $client_box->CreateProcess ( + "client", + "-ORBPreferredInterfaces $via -ORBEnforcePreferredInterfaces=1 -ORBSvcConf $client_svc_conf_file -ORBIPMulticastLoop 1 -k corbaloc:miop:1.0\@1.0-foo-1/$multicast_address:$port" + ); + } else { + $client = $client_box->CreateProcess ( + "client", + "-ORBSvcConf $client_svc_conf_file -ORBIPMulticastLoop 1 -k corbaloc:miop:1.0\@1.0-foo-1/$multicast_address:$port" + ); + } + + print "Starting server...\n"; + my $server_status = $server->Spawn (); + if ($server_status != 0) { + print STDERR "ERROR: server returned $server_status\n"; + next; + } + print "Server started. Waiting for IOR file...\n"; + if ($server_box->WaitForFileTimed ($IOR_BASE, startWaitIntervalWrapper ($server_box, $timeout_override)) == -1) { + $server_status = timedWaitWrapper ($server, 0.1); + if ($server_status != -1) { + print STDERR "ERROR: server died with rc $server_status before IOR file was created.\n"; + $server->Kill (); + timedWaitWrapper ($server, 1); + $status = 1; + next; + } + print STDERR "ERROR: cannot find file <$server_ior_file>\n"; + $server->Kill (); + timedWaitWrapper ($server, 1); + $status = 1; + next; + } + if ($server_box->GetFile ($IOR_BASE) == -1) { + print STDERR "ERROR: cannot retrieve file <$server_ior_file>\n"; + $server->Kill (); + timedWaitWrapper ($server, 1); + $status = 1; + next; + } + if ($client_box->PutFile ($IOR_BASE) == -1) { + print STDERR "ERROR: cannot set file <$client_ior_file>\n"; + $server->Kill (); + timedWaitWrapper ($server, 1); + $status = 1; + next; + } + + print "Copied IOR file from server to client. Starting and waiting on client...\n"; + my $client_status = $client->SpawnWaitKill (startWaitIntervalWrapper ($client_box, $timeout_override)); + print $client_status == -1 ? "Client status: timed out and killed.\n" : "Client exit code: $client_status\n"; + + $server_status = $server->WaitKill ($server_box->ProcessStopWaitInterval()); + print $server_status == -1 ? "Server status: timed out and killed.\n" : "Server exit code: $server_status\n"; + + if ($client_status != 0) { + print "TEST RESULT: FAILED (client should never be rc != 0)\n"; + $status = 1; + } elsif ($server_status != 0) { + if ($test->{"type"} == $LOOPBACK && $server_status == -1) { + print "TEST RESULT: SUCCESS (server should time out with loopback as multicast listener interface)\n"; + } else { + print "TEST RESULT: FAILED\n"; + $status = 1; + } + } else { + if ($test->{"type"} == $LOOPBACK) { + print "TEST RESULT: FAILED (server should time out with loopback as multicast listener interface)\n"; + $status = 1; + } else { + print "TEST RESULT: SUCCESS\n"; + } + } + + unlink $SVC_CONF_BASE; +} + +print "\n"; + +exit $status; diff --git a/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/server.cpp b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/server.cpp new file mode 100644 index 0000000000000..6001d27c5f9f2 --- /dev/null +++ b/TAO/orbsvcs/tests/Miop/McastListenerInterfaces/server.cpp @@ -0,0 +1,129 @@ +#include "Test_impl.h" +#include "ace/OS_NS_stdio.h" +#include "ace/Get_Opt.h" +#include "orbsvcs/PortableGroup/GOA.h" + + +const ACE_TCHAR *uipmc_url = 0; +const ACE_TCHAR *ior_output_file = 0; + +int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opts (argc, argv, ACE_TEXT ("o:u:")); + int c; + + while ((c = get_opts ()) != -1) + switch (c) + { + case 'u': + uipmc_url = get_opts.opt_arg (); + break; + case 'o': + ior_output_file = get_opts.opt_arg (); + break; + case '?': + default: + ACE_ERROR_RETURN ((LM_ERROR, + "usage: %s " + "-o " + "-u " + "\n", + argv [0]), + 1); + } + // Indicates successful parsing of the command line + return 0; +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + try + { + CORBA::ORB_var orb = CORBA::ORB_init (argc, argv); + + if (parse_args (argc, argv) != 0) + ACE_ERROR_RETURN ((LM_ERROR, "Wrong arguments\n"), 2); + + CORBA::Object_var poa_object = + orb->resolve_initial_references("RootPOA"); + + Server_impl* server_obj = 0; + ACE_NEW_RETURN (server_obj, + Server_impl(orb.in()), + 3); + PortableServer::ServantBase_var owner (server_obj); + + CORBA::String_var ior; + if (uipmc_url) + { + PortableGroup::GOA_var root_poa = + PortableGroup::GOA::_narrow (poa_object.in ()); + + if (CORBA::is_nil (root_poa.in ())) + ACE_ERROR_RETURN ((LM_ERROR, + " (%P|%t) Panic: nil RootPOA\n"), + 4); + + PortableServer::POAManager_var poa_manager = + root_poa->the_POAManager (); + + // create UIPMC reference + CORBA::Object_var ref = + orb->string_to_object (uipmc_url); + + // create id + PortableServer::ObjectId_var id = + root_poa->create_id_for_reference (ref.in ()); + + // activate object + root_poa->activate_object_with_id (id.in (), + server_obj); + + ior = + orb->object_to_string (ref.in ()); + + poa_manager->activate (); + } + else + { + PortableServer::POA_var root_poa = + PortableServer::POA::_narrow (poa_object.in ()); + + if (CORBA::is_nil (root_poa.in ())) + ACE_ERROR_RETURN ((LM_ERROR, + " (%P|%t) Panic: nil RootPOA\n"), + 5); + + PortableServer::POAManager_var poa_manager = + root_poa->the_POAManager (); + + CORBA::Object_var ref = + server_obj->_this (); + + ior = + orb->object_to_string (ref.in ()); + + poa_manager->activate (); + } + + if (ior_output_file != 0) + { + FILE *output_file = ACE_OS::fopen (ior_output_file, "w"); + ACE_OS::fprintf (output_file, "%s", ior.in ()); + ACE_OS::fclose (output_file); + } + + orb->run (); + + orb->destroy (); + } + catch (const ::CORBA::Exception &ex) + { + ex._tao_print_exception ("Exception in server.cpp:\n"); + return 99; + } + + return 0; +}