Commit b0df4be5 authored by Lionel Flandrin's avatar Lionel Flandrin

Problem: UDP engine does not support IPv6

Solution: Add IPv6 support
parent 7aba6821
...@@ -925,7 +925,7 @@ unittests_unittest_mtrie_LDADD = $(top_builddir)/src/.libs/libzmq.a \ ...@@ -925,7 +925,7 @@ unittests_unittest_mtrie_LDADD = $(top_builddir)/src/.libs/libzmq.a \
${UNITY_LIBS} \ ${UNITY_LIBS} \
$(CODE_COVERAGE_LDFLAGS) $(CODE_COVERAGE_LDFLAGS)
unittests_unittest_ip_resolver_SOURCES = unittests/unittest_ip_resolver.cpp unittests_unittest_ip_resolver_SOURCES = unittests/unittest_ip_resolver.cpp unittests/unittest_resolver_common.hpp
unittests_unittest_ip_resolver_CPPFLAGS = -I$(top_srcdir)/src ${UNITY_CPPFLAGS} $(CODE_COVERAGE_CPPFLAGS) unittests_unittest_ip_resolver_CPPFLAGS = -I$(top_srcdir)/src ${UNITY_CPPFLAGS} $(CODE_COVERAGE_CPPFLAGS)
unittests_unittest_ip_resolver_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS) unittests_unittest_ip_resolver_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS)
unittests_unittest_ip_resolver_LDADD = $(top_builddir)/src/.libs/libzmq.a \ unittests_unittest_ip_resolver_LDADD = $(top_builddir)/src/.libs/libzmq.a \
...@@ -933,7 +933,7 @@ unittests_unittest_ip_resolver_LDADD = $(top_builddir)/src/.libs/libzmq.a \ ...@@ -933,7 +933,7 @@ unittests_unittest_ip_resolver_LDADD = $(top_builddir)/src/.libs/libzmq.a \
${UNITY_LIBS} \ ${UNITY_LIBS} \
$(CODE_COVERAGE_LDFLAGS) $(CODE_COVERAGE_LDFLAGS)
unittests_unittest_udp_address_SOURCES = unittests/unittest_udp_address.cpp unittests_unittest_udp_address_SOURCES = unittests/unittest_udp_address.cpp unittests/unittest_resolver_common.hpp
unittests_unittest_udp_address_CPPFLAGS = -I$(top_srcdir)/src ${UNITY_CPPFLAGS} $(CODE_COVERAGE_CPPFLAGS) unittests_unittest_udp_address_CPPFLAGS = -I$(top_srcdir)/src ${UNITY_CPPFLAGS} $(CODE_COVERAGE_CPPFLAGS)
unittests_unittest_udp_address_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS) unittests_unittest_udp_address_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS)
unittests_unittest_udp_address_LDADD = $(top_builddir)/src/.libs/libzmq.a \ unittests_unittest_udp_address_LDADD = $(top_builddir)/src/.libs/libzmq.a \
......
...@@ -46,6 +46,20 @@ uint16_t zmq::ip_addr_t::port () const ...@@ -46,6 +46,20 @@ uint16_t zmq::ip_addr_t::port () const
} }
} }
const struct sockaddr *zmq::ip_addr_t::as_sockaddr () const
{
return &generic;
}
size_t zmq::ip_addr_t::sockaddr_len () const
{
if (family () == AF_INET6) {
return sizeof (ipv6);
} else {
return sizeof (ipv4);
}
}
void zmq::ip_addr_t::set_port (uint16_t port) void zmq::ip_addr_t::set_port (uint16_t port)
{ {
if (family () == AF_INET6) { if (family () == AF_INET6) {
......
...@@ -46,6 +46,10 @@ union ip_addr_t ...@@ -46,6 +46,10 @@ union ip_addr_t
int family () const; int family () const;
bool is_multicast () const; bool is_multicast () const;
uint16_t port () const; uint16_t port () const;
const struct sockaddr *as_sockaddr () const;
size_t sockaddr_len () const;
void set_port (uint16_t); void set_port (uint16_t);
static ip_addr_t any (int family); static ip_addr_t any (int family);
......
...@@ -534,7 +534,8 @@ int zmq::socket_base_t::bind (const char *addr_) ...@@ -534,7 +534,8 @@ int zmq::socket_base_t::bind (const char *addr_)
paddr->resolved.udp_addr = new (std::nothrow) udp_address_t (); paddr->resolved.udp_addr = new (std::nothrow) udp_address_t ();
alloc_assert (paddr->resolved.udp_addr); alloc_assert (paddr->resolved.udp_addr);
rc = paddr->resolved.udp_addr->resolve (address.c_str (), true); rc = paddr->resolved.udp_addr->resolve (address.c_str (), true,
options.ipv6);
if (rc != 0) { if (rc != 0) {
LIBZMQ_DELETE (paddr); LIBZMQ_DELETE (paddr);
return -1; return -1;
...@@ -876,7 +877,8 @@ int zmq::socket_base_t::connect (const char *addr_) ...@@ -876,7 +877,8 @@ int zmq::socket_base_t::connect (const char *addr_)
paddr->resolved.udp_addr = new (std::nothrow) udp_address_t (); paddr->resolved.udp_addr = new (std::nothrow) udp_address_t ();
alloc_assert (paddr->resolved.udp_addr); alloc_assert (paddr->resolved.udp_addr);
rc = paddr->resolved.udp_addr->resolve (address.c_str (), false); rc = paddr->resolved.udp_addr->resolve (address.c_str (), false,
options.ipv6);
if (rc != 0) { if (rc != 0) {
LIBZMQ_DELETE (paddr); LIBZMQ_DELETE (paddr);
return -1; return -1;
......
...@@ -41,28 +41,26 @@ ...@@ -41,28 +41,26 @@
#include <sys/types.h> #include <sys/types.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netdb.h> #include <netdb.h>
#include <net/if.h>
#include <ctype.h> #include <ctype.h>
#endif #endif
#include "ip_resolver.hpp" zmq::udp_address_t::udp_address_t () : bind_interface (-1), is_multicast (false)
zmq::udp_address_t::udp_address_t () : is_multicast (false)
{ {
memset (&bind_address, 0, sizeof bind_address); bind_address = ip_addr_t::any (AF_INET);
memset (&dest_address, 0, sizeof dest_address); target_address = ip_addr_t::any (AF_INET);
} }
zmq::udp_address_t::~udp_address_t () zmq::udp_address_t::~udp_address_t ()
{ {
} }
int zmq::udp_address_t::resolve (const char *name_, bool bind_) int zmq::udp_address_t::resolve (const char *name_, bool bind_, bool ipv6_)
{ {
// No IPv6 support yet // No IPv6 support yet
int family = AF_INET;
bool ipv6 = family == AF_INET6;
bool has_interface = false; bool has_interface = false;
ip_addr_t interface_addr;
address = name_;
// If we have a semicolon then we should have an interface specifier in the // If we have a semicolon then we should have an interface specifier in the
// URL // URL
...@@ -79,24 +77,38 @@ int zmq::udp_address_t::resolve (const char *name_, bool bind_) ...@@ -79,24 +77,38 @@ int zmq::udp_address_t::resolve (const char *name_, bool bind_)
// indeterminate socktype. // indeterminate socktype.
.allow_dns (false) .allow_dns (false)
.allow_nic_name (true) .allow_nic_name (true)
.ipv6 (ipv6) .ipv6 (ipv6_)
.expect_port (false); .expect_port (false);
ip_resolver_t src_resolver (src_resolver_opts); ip_resolver_t src_resolver (src_resolver_opts);
const int rc = const int rc = src_resolver.resolve (&bind_address, src_name.c_str ());
src_resolver.resolve (&interface_addr, src_name.c_str ());
if (rc != 0) { if (rc != 0) {
return -1; return -1;
} }
if (interface_addr.is_multicast ()) { if (bind_address.is_multicast ()) {
// It doesn't make sense to have a multicast address as a source // It doesn't make sense to have a multicast address as a source
errno = EINVAL; errno = EINVAL;
return -1; return -1;
} }
// This is a hack because we need the interface index when binding
// multicast IPv6, we can't do it by address. Unfortunately for the
// time being we don't have a generic platform-independent function to
// resolve an interface index from an address, so we only support it
// when an actual interface name is provided.
if (src_name == "*") {
bind_interface = 0;
} else {
bind_interface = if_nametoindex (src_name.c_str ());
if (bind_interface == 0) {
// Error, probably not an interface name.
bind_interface = -1;
}
}
has_interface = true; has_interface = true;
name_ = src_delimiter + 1; name_ = src_delimiter + 1;
} }
...@@ -107,19 +119,17 @@ int zmq::udp_address_t::resolve (const char *name_, bool bind_) ...@@ -107,19 +119,17 @@ int zmq::udp_address_t::resolve (const char *name_, bool bind_)
.allow_dns (!bind_) .allow_dns (!bind_)
.allow_nic_name (bind_) .allow_nic_name (bind_)
.expect_port (true) .expect_port (true)
.ipv6 (ipv6); .ipv6 (ipv6_);
ip_resolver_t resolver (resolver_opts); ip_resolver_t resolver (resolver_opts);
ip_addr_t target_addr; int rc = resolver.resolve (&target_address, name_);
int rc = resolver.resolve (&target_addr, name_);
if (rc != 0) { if (rc != 0) {
return -1; return -1;
} }
is_multicast = target_addr.is_multicast (); is_multicast = target_address.is_multicast ();
uint16_t port = target_addr.port (); uint16_t port = target_address.port ();
if (has_interface) { if (has_interface) {
// If we have an interface specifier then the target address must be a // If we have an interface specifier then the target address must be a
...@@ -129,46 +139,43 @@ int zmq::udp_address_t::resolve (const char *name_, bool bind_) ...@@ -129,46 +139,43 @@ int zmq::udp_address_t::resolve (const char *name_, bool bind_)
return -1; return -1;
} }
interface_addr.set_port (port); bind_address.set_port (port);
dest_address = target_addr.ipv4;
bind_address = interface_addr.ipv4;
} else { } else {
// If we don't have an explicit interface specifier then the URL is // If we don't have an explicit interface specifier then the URL is
// ambiguous: if the target address is multicast then it's the // ambiguous: if the target address is multicast then it's the
// destination address and the bind address is ANY, if it's unicast // destination address and the bind address is ANY, if it's unicast
// then it's the bind address when 'bind_' is true and the destination // then it's the bind address when 'bind_' is true and the destination
// otherwise // otherwise
ip_addr_t any = ip_addr_t::any (family); if (is_multicast || !bind_) {
any.set_port (port); bind_address = ip_addr_t::any (target_address.family ());
bind_address.set_port (port);
if (is_multicast) { bind_interface = 0;
dest_address = target_addr.ipv4;
bind_address = any.ipv4;
} else { } else {
if (bind_) { // If we were asked for a bind socket and the address
dest_address = target_addr.ipv4; // provided was not multicast then it was really meant as
bind_address = target_addr.ipv4; // a bind address and the target_address is useless.
} else { bind_address = target_address;
dest_address = target_addr.ipv4;
bind_address = any.ipv4;
}
} }
} }
if (is_multicast) { if (bind_address.family () != target_address.family ()) {
multicast = dest_address.sin_addr; errno = EINVAL;
return -1;
} }
address = name_; // For IPv6 multicast we *must* have an interface index since we can't
// bind by address.
if (ipv6_ && is_multicast && bind_interface < 0) {
errno = ENODEV;
return -1;
}
return 0; return 0;
} }
int zmq::udp_address_t::to_string (std::string &addr_) int zmq::udp_address_t::family () const
{ {
addr_ = address; return bind_address.family ();
return 0;
} }
bool zmq::udp_address_t::is_mcast () const bool zmq::udp_address_t::is_mcast () const
...@@ -176,41 +183,24 @@ bool zmq::udp_address_t::is_mcast () const ...@@ -176,41 +183,24 @@ bool zmq::udp_address_t::is_mcast () const
return is_multicast; return is_multicast;
} }
const sockaddr *zmq::udp_address_t::bind_addr () const const zmq::ip_addr_t *zmq::udp_address_t::bind_addr () const
{
return (sockaddr *) &bind_address;
}
socklen_t zmq::udp_address_t::bind_addrlen () const
{
return sizeof (sockaddr_in);
}
const sockaddr *zmq::udp_address_t::dest_addr () const
{ {
return (sockaddr *) &dest_address; return &bind_address;
} }
socklen_t zmq::udp_address_t::dest_addrlen () const int zmq::udp_address_t::bind_if () const
{ {
return sizeof (sockaddr_in); return bind_interface;
} }
const in_addr zmq::udp_address_t::multicast_ip () const const zmq::ip_addr_t *zmq::udp_address_t::target_addr () const
{ {
return multicast; return &target_address;
} }
const in_addr zmq::udp_address_t::interface_ip () const int zmq::udp_address_t::to_string (std::string &addr_)
{
return iface;
}
#if defined ZMQ_HAVE_WINDOWS
unsigned short zmq::udp_address_t::family () const
#else
sa_family_t zmq::udp_address_t::family () const
#endif
{ {
return AF_INET; // XXX what do (factor TCP code?)
addr_ = address;
return 0;
} }
...@@ -35,6 +35,8 @@ ...@@ -35,6 +35,8 @@
#include <netinet/in.h> #include <netinet/in.h>
#endif #endif
#include "ip_resolver.hpp"
namespace zmq namespace zmq
{ {
class udp_address_t class udp_address_t
...@@ -43,32 +45,24 @@ class udp_address_t ...@@ -43,32 +45,24 @@ class udp_address_t
udp_address_t (); udp_address_t ();
virtual ~udp_address_t (); virtual ~udp_address_t ();
int resolve (const char *name_, bool receiver_); int resolve (const char *name_, bool receiver_, bool ipv6_);
// The opposite to resolve() // The opposite to resolve()
virtual int to_string (std::string &addr_); virtual int to_string (std::string &addr_);
#if defined ZMQ_HAVE_WINDOWS
unsigned short family () const;
#else
sa_family_t family () const;
#endif
const sockaddr *bind_addr () const;
socklen_t bind_addrlen () const;
const sockaddr *dest_addr () const; int family () const;
socklen_t dest_addrlen () const;
bool is_mcast () const; bool is_mcast () const;
const in_addr multicast_ip () const; const ip_addr_t *bind_addr () const;
const in_addr interface_ip () const; int bind_if () const;
const ip_addr_t *target_addr () const;
private: private:
in_addr multicast; ip_addr_t bind_address;
in_addr iface; int bind_interface;
sockaddr_in bind_address; ip_addr_t target_address;
sockaddr_in dest_address;
bool is_multicast; bool is_multicast;
std::string address; std::string address;
}; };
......
...@@ -46,6 +46,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. ...@@ -46,6 +46,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "err.hpp" #include "err.hpp"
#include "ip.hpp" #include "ip.hpp"
// OSX uses a different name for this socket option
#ifndef IPV6_ADD_MEMBERSHIP
#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
#endif
zmq::udp_engine_t::udp_engine_t (const options_t &options_) : zmq::udp_engine_t::udp_engine_t (const options_t &options_) :
plugged (false), plugged (false),
fd (-1), fd (-1),
...@@ -111,9 +116,11 @@ void zmq::udp_engine_t::plug (io_thread_t *io_thread_, session_base_t *session_) ...@@ -111,9 +116,11 @@ void zmq::udp_engine_t::plug (io_thread_t *io_thread_, session_base_t *session_)
if (send_enabled) { if (send_enabled) {
if (!options.raw_socket) { if (!options.raw_socket) {
out_address = address->resolved.udp_addr->dest_addr (); const ip_addr_t *out = address->resolved.udp_addr->target_addr ();
out_addrlen = address->resolved.udp_addr->dest_addrlen (); out_address = out->as_sockaddr ();
out_addrlen = out->sockaddr_len ();
} else { } else {
/// XXX fixme ?
out_address = (sockaddr *) &raw_address; out_address = (sockaddr *) &raw_address;
out_addrlen = sizeof (sockaddr_in); out_addrlen = sizeof (sockaddr_in);
} }
...@@ -131,12 +138,29 @@ void zmq::udp_engine_t::plug (io_thread_t *io_thread_, session_base_t *session_) ...@@ -131,12 +138,29 @@ void zmq::udp_engine_t::plug (io_thread_t *io_thread_, session_base_t *session_)
errno_assert (rc == 0); errno_assert (rc == 0);
#endif #endif
const ip_addr_t *bind_addr = address->resolved.udp_addr->bind_addr ();
ip_addr_t any = ip_addr_t::any (bind_addr->family ());
const ip_addr_t *real_bind_addr;
bool multicast = address->resolved.udp_addr->is_mcast ();
if (multicast) {
// In multicast we should bind ANY and use the mreq struct to
// specify the interface
any.set_port (bind_addr->port ());
real_bind_addr = &any;
} else {
real_bind_addr = bind_addr;
}
#ifdef ZMQ_HAVE_VXWORKS #ifdef ZMQ_HAVE_VXWORKS
rc = bind (fd, (sockaddr *) address->resolved.udp_addr->bind_addr (), rc = bind (fd, (sockaddr *) real_bind_addr->as_sockaddr (),
address->resolved.udp_addr->bind_addrlen ()); real_bind_addr->sockaddr_len ());
#else #else
rc = bind (fd, address->resolved.udp_addr->bind_addr (), rc = bind (fd, real_bind_addr->as_sockaddr (),
address->resolved.udp_addr->bind_addrlen ()); real_bind_addr->sockaddr_len ());
#endif #endif
#ifdef ZMQ_HAVE_WINDOWS #ifdef ZMQ_HAVE_WINDOWS
wsa_assert (rc != SOCKET_ERROR); wsa_assert (rc != SOCKET_ERROR);
...@@ -144,12 +168,37 @@ void zmq::udp_engine_t::plug (io_thread_t *io_thread_, session_base_t *session_) ...@@ -144,12 +168,37 @@ void zmq::udp_engine_t::plug (io_thread_t *io_thread_, session_base_t *session_)
errno_assert (rc == 0); errno_assert (rc == 0);
#endif #endif
if (address->resolved.udp_addr->is_mcast ()) { if (multicast) {
struct ip_mreq mreq; const ip_addr_t *mcast_addr =
mreq.imr_multiaddr = address->resolved.udp_addr->multicast_ip (); address->resolved.udp_addr->target_addr ();
mreq.imr_interface = address->resolved.udp_addr->interface_ip ();
rc = setsockopt (fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, if (mcast_addr->family () == AF_INET) {
sizeof (mreq)); struct ip_mreq mreq;
mreq.imr_multiaddr = mcast_addr->ipv4.sin_addr;
mreq.imr_interface = bind_addr->ipv4.sin_addr;
rc = setsockopt (fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *) &mreq, sizeof (mreq));
errno_assert (rc == 0);
} else if (mcast_addr->family () == AF_INET6) {
struct ipv6_mreq mreq;
int iface = address->resolved.udp_addr->bind_if ();
zmq_assert (iface >= -1);
mreq.ipv6mr_multiaddr = mcast_addr->ipv6.sin6_addr;
mreq.ipv6mr_interface = iface;
rc = setsockopt (fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
(char *) &mreq, sizeof (mreq));
errno_assert (rc == 0);
} else {
// Shouldn't happen
abort ();
}
#ifdef ZMQ_HAVE_WINDOWS #ifdef ZMQ_HAVE_WINDOWS
wsa_assert (rc != SOCKET_ERROR); wsa_assert (rc != SOCKET_ERROR);
#else #else
......
...@@ -32,6 +32,18 @@ ...@@ -32,6 +32,18 @@
#include <unity.h> #include <unity.h>
// Helper macro to define the v4/v6 function pairs
#define MAKE_TEST_V4V6(_test) \
static void _test##_ipv4 () { _test (false); } \
\
static void _test##_ipv6 () \
{ \
if (!is_ipv6_available ()) { \
TEST_IGNORE_MESSAGE ("ipv6 is not available"); \
} \
_test (true); \
}
void setUp () void setUp ()
{ {
setup_test_context (); setup_test_context ();
...@@ -111,16 +123,19 @@ void test_join_twice_fails () ...@@ -111,16 +123,19 @@ void test_join_twice_fails ()
test_context_socket_close (dish); test_context_socket_close (dish);
} }
void test_radio_dish_tcp_poll () void test_radio_dish_tcp_poll (int ipv6_)
{ {
size_t len = MAX_SOCKET_STRING; size_t len = MAX_SOCKET_STRING;
char my_endpoint[MAX_SOCKET_STRING]; char my_endpoint[MAX_SOCKET_STRING];
void *radio = test_context_socket (ZMQ_RADIO); void *radio = test_context_socket (ZMQ_RADIO);
bind_loopback_ipv4 (radio, my_endpoint, len); bind_loopback (radio, ipv6_, my_endpoint, len);
void *dish = test_context_socket (ZMQ_DISH); void *dish = test_context_socket (ZMQ_DISH);
TEST_ASSERT_SUCCESS_ERRNO (
zmq_setsockopt (dish, ZMQ_IPV6, &ipv6_, sizeof (int)));
// Joining // Joining
TEST_ASSERT_SUCCESS_ERRNO (zmq_join (dish, "Movies")); TEST_ASSERT_SUCCESS_ERRNO (zmq_join (dish, "Movies"));
...@@ -175,22 +190,31 @@ void test_radio_dish_tcp_poll () ...@@ -175,22 +190,31 @@ void test_radio_dish_tcp_poll ()
test_context_socket_close (dish); test_context_socket_close (dish);
test_context_socket_close (radio); test_context_socket_close (radio);
} }
MAKE_TEST_V4V6 (test_radio_dish_tcp_poll)
void test_dish_connect_fails () void test_dish_connect_fails (int ipv6_)
{ {
void *dish = test_context_socket (ZMQ_DISH); void *dish = test_context_socket (ZMQ_DISH);
TEST_ASSERT_SUCCESS_ERRNO (
zmq_setsockopt (dish, ZMQ_IPV6, &ipv6_, sizeof (int)));
const char *url = ipv6_ ? "udp://[::1]:5556" : "udp://127.0.0.1:5556";
// Connecting dish should fail // Connecting dish should fail
TEST_ASSERT_FAILURE_ERRNO (ENOCOMPATPROTO, TEST_ASSERT_FAILURE_ERRNO (ENOCOMPATPROTO, zmq_connect (dish, url));
zmq_connect (dish, "udp://127.0.0.1:5556"));
test_context_socket_close (dish); test_context_socket_close (dish);
} }
MAKE_TEST_V4V6 (test_dish_connect_fails)
void test_radio_bind_fails () void test_radio_bind_fails (int ipv6_)
{ {
void *radio = test_context_socket (ZMQ_RADIO); void *radio = test_context_socket (ZMQ_RADIO);
TEST_ASSERT_SUCCESS_ERRNO (
zmq_setsockopt (radio, ZMQ_IPV6, &ipv6_, sizeof (int)));
// Connecting dish should fail // Connecting dish should fail
// Bind radio should fail // Bind radio should fail
TEST_ASSERT_FAILURE_ERRNO (ENOCOMPATPROTO, TEST_ASSERT_FAILURE_ERRNO (ENOCOMPATPROTO,
...@@ -198,14 +222,22 @@ void test_radio_bind_fails () ...@@ -198,14 +222,22 @@ void test_radio_bind_fails ()
test_context_socket_close (radio); test_context_socket_close (radio);
} }
MAKE_TEST_V4V6 (test_radio_bind_fails)
void test_radio_dish_udp () void test_radio_dish_udp (int ipv6_)
{ {
void *radio = test_context_socket (ZMQ_RADIO); void *radio = test_context_socket (ZMQ_RADIO);
void *dish = test_context_socket (ZMQ_DISH); void *dish = test_context_socket (ZMQ_DISH);
TEST_ASSERT_SUCCESS_ERRNO (
zmq_setsockopt (radio, ZMQ_IPV6, &ipv6_, sizeof (int)));
TEST_ASSERT_SUCCESS_ERRNO (
zmq_setsockopt (dish, ZMQ_IPV6, &ipv6_, sizeof (int)));
const char *radio_url = ipv6_ ? "udp://[::1]:5556" : "udp://127.0.0.1:5556";
TEST_ASSERT_SUCCESS_ERRNO (zmq_bind (dish, "udp://*:5556")); TEST_ASSERT_SUCCESS_ERRNO (zmq_bind (dish, "udp://*:5556"));
TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (radio, "udp://127.0.0.1:5556")); TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (radio, radio_url));
msleep (SETTLE_TIME); msleep (SETTLE_TIME);
...@@ -217,6 +249,7 @@ void test_radio_dish_udp () ...@@ -217,6 +249,7 @@ void test_radio_dish_udp ()
test_context_socket_close (dish); test_context_socket_close (dish);
test_context_socket_close (radio); test_context_socket_close (radio);
} }
MAKE_TEST_V4V6 (test_radio_dish_udp)
int main (void) int main (void)
{ {
...@@ -226,10 +259,14 @@ int main (void) ...@@ -226,10 +259,14 @@ int main (void)
RUN_TEST (test_leave_unjoined_fails); RUN_TEST (test_leave_unjoined_fails);
RUN_TEST (test_join_too_long_fails); RUN_TEST (test_join_too_long_fails);
RUN_TEST (test_join_twice_fails); RUN_TEST (test_join_twice_fails);
RUN_TEST (test_radio_bind_fails); RUN_TEST (test_radio_bind_fails_ipv4);
RUN_TEST (test_dish_connect_fails); RUN_TEST (test_radio_bind_fails_ipv6);
RUN_TEST (test_radio_dish_tcp_poll); RUN_TEST (test_dish_connect_fails_ipv4);
RUN_TEST (test_radio_dish_udp); RUN_TEST (test_dish_connect_fails_ipv6);
RUN_TEST (test_radio_dish_tcp_poll_ipv4);
RUN_TEST (test_radio_dish_tcp_poll_ipv6);
RUN_TEST (test_radio_dish_udp_ipv4);
RUN_TEST (test_radio_dish_udp_ipv6);
return UNITY_END (); return UNITY_END ();
} }
...@@ -26,7 +26,7 @@ include_directories("${CMAKE_SOURCE_DIR}/include" "${CMAKE_SOURCE_DIR}/src" "${C ...@@ -26,7 +26,7 @@ include_directories("${CMAKE_SOURCE_DIR}/include" "${CMAKE_SOURCE_DIR}/src" "${C
foreach(test ${unittests}) foreach(test ${unittests})
# target_sources not supported before CMake 3.1 # target_sources not supported before CMake 3.1
add_executable(${test} ${test}.cpp) add_executable(${test} ${test}.cpp "unittest_resolver_common.hpp")
# per-test directories not generated on OS X / Darwin # per-test directories not generated on OS X / Darwin
if (NOT ${CMAKE_CXX_COMPILER_ID} MATCHES "Clang.*") if (NOT ${CMAKE_CXX_COMPILER_ID} MATCHES "Clang.*")
......
...@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. ...@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <unity.h> #include <unity.h>
#include "../tests/testutil.hpp" #include "../tests/testutil.hpp"
#include "../unittests/unittest_resolver_common.hpp"
#include <ip_resolver.hpp> #include <ip_resolver.hpp>
#include <ip.hpp> #include <ip.hpp>
...@@ -155,41 +156,9 @@ static void test_resolve (zmq::ip_resolver_options_t opts_, ...@@ -155,41 +156,9 @@ static void test_resolve (zmq::ip_resolver_options_t opts_,
TEST_ASSERT_EQUAL (0, rc); TEST_ASSERT_EQUAL (0, rc);
} }
#if defined ZMQ_HAVE_WINDOWS validate_address (family, &addr, expected_addr_,
if (family == AF_INET6 && expected_addr_v4_failover_ != NULL && expected_port_, expected_zone_,
addr.family () == AF_INET) { expected_addr_v4_failover_);
// We've requested an IPv6 but the system gave us an IPv4, use the
// failover address
family = AF_INET;
expected_addr_ = expected_addr_v4_failover_;
}
#else
(void)expected_addr_v4_failover_;
#endif
TEST_ASSERT_EQUAL (family, addr.family ());
if (family == AF_INET6) {
struct in6_addr expected_addr;
const sockaddr_in6 *ip6_addr = &addr.ipv6;
assert (test_inet_pton (AF_INET6, expected_addr_, &expected_addr) == 1);
int neq = memcmp (&ip6_addr->sin6_addr, &expected_addr,
sizeof (expected_addr_));
TEST_ASSERT_EQUAL (0, neq);
TEST_ASSERT_EQUAL (htons (expected_port_), ip6_addr->sin6_port);
TEST_ASSERT_EQUAL (expected_zone_, ip6_addr->sin6_scope_id);
} else {
struct in_addr expected_addr;
const sockaddr_in *ip4_addr = &addr.ipv4;
assert (test_inet_pton (AF_INET, expected_addr_, &expected_addr) == 1);
TEST_ASSERT_EQUAL (expected_addr.s_addr, ip4_addr->sin_addr.s_addr);
TEST_ASSERT_EQUAL (htons (expected_port_), ip4_addr->sin_port);
}
} }
// Helper macro to define the v4/v6 function pairs // Helper macro to define the v4/v6 function pairs
......
/*
Copyright (c) 2018 Contributors as noted in the AUTHORS file
This file is part of 0MQ.
0MQ is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
0MQ is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __UNITTEST_RESOLVER_COMMON_INCLUDED__
#define __UNITTEST_RESOLVER_COMMON_INCLUDED__
#include <ip_resolver.hpp>
// Attempt a resolution and test the results.
//
// On windows we can receive an IPv4 address even when an IPv6 is requested, if
// we're in this situation then we compare to 'expected_addr_v4_failover_'
// instead.
void validate_address(int family, const zmq::ip_addr_t *addr_,
const char *expected_addr_,
uint16_t expected_port_ = 0,
uint16_t expected_zone_ = 0,
const char *expected_addr_v4_failover_ = NULL)
{
#if defined ZMQ_HAVE_WINDOWS
if (family == AF_INET6 && expected_addr_v4_failover_ != NULL &&
addr_->family () == AF_INET) {
// We've requested an IPv6 but the system gave us an IPv4, use the
// failover address
family = AF_INET;
expected_addr_ = expected_addr_v4_failover_;
}
#else
(void)expected_addr_v4_failover_;
#endif
TEST_ASSERT_EQUAL (family, addr_->family ());
if (family == AF_INET6) {
struct in6_addr expected_addr;
const sockaddr_in6 *ip6_addr = &addr_->ipv6;
assert (test_inet_pton (AF_INET6, expected_addr_, &expected_addr) == 1);
int neq = memcmp (&ip6_addr->sin6_addr, &expected_addr,
sizeof (expected_addr_));
TEST_ASSERT_EQUAL (0, neq);
TEST_ASSERT_EQUAL (htons (expected_port_), ip6_addr->sin6_port);
TEST_ASSERT_EQUAL (expected_zone_, ip6_addr->sin6_scope_id);
} else {
struct in_addr expected_addr;
const sockaddr_in *ip4_addr = &addr_->ipv4;
assert (test_inet_pton (AF_INET, expected_addr_, &expected_addr) == 1);
TEST_ASSERT_EQUAL (expected_addr.s_addr, ip4_addr->sin_addr.s_addr);
TEST_ASSERT_EQUAL (htons (expected_port_), ip4_addr->sin_port);
}
}
#endif // __UNITTEST_RESOLVER_COMMON_INCLUDED__
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment