diff --git a/Makefile.am b/Makefile.am index a167979470..3036d81ae1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -898,7 +898,8 @@ test_apps += \ unittests/unittest_poller \ unittests/unittest_ypipe \ unittests/unittest_mtrie \ - unittests/unittest_ip_resolver + unittests/unittest_ip_resolver \ + unittests/unittest_udp_address unittests_unittest_poller_SOURCES = unittests/unittest_poller.cpp unittests_unittest_poller_CPPFLAGS = -I$(top_srcdir)/src ${UNITY_CPPFLAGS} $(CODE_COVERAGE_CPPFLAGS) @@ -931,6 +932,14 @@ unittests_unittest_ip_resolver_LDADD = $(top_builddir)/src/.libs/libzmq.a \ ${src_libzmq_la_LIBADD} \ ${UNITY_LIBS} \ $(CODE_COVERAGE_LDFLAGS) + +unittests_unittest_udp_address_SOURCES = unittests/unittest_udp_address.cpp +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_LDADD = $(top_builddir)/src/.libs/libzmq.a \ + ${src_libzmq_la_LIBADD} \ + ${UNITY_LIBS} \ + $(CODE_COVERAGE_LDFLAGS) endif check_PROGRAMS = ${test_apps} diff --git a/src/udp_address.cpp b/src/udp_address.cpp index 99887eb616..f21cf23746 100644 --- a/src/udp_address.cpp +++ b/src/udp_address.cpp @@ -44,6 +44,8 @@ #include #endif +#include "ip_resolver.hpp" + zmq::udp_address_t::udp_address_t () : is_multicast (false) { memset (&bind_address, 0, sizeof bind_address); @@ -56,38 +58,29 @@ zmq::udp_address_t::~udp_address_t () int zmq::udp_address_t::resolve (const char *name_, bool bind_) { - // Find the ':' at end that separates address from the port number. - const char *delimiter = strrchr (name_, ':'); - if (!delimiter) { - errno = EINVAL; - return -1; - } + ip_resolver_options_t resolver_opts; - // Separate the address/port. - std::string addr_str (name_, delimiter - name_); - std::string port_str (delimiter + 1); + resolver_opts.bindable (bind_) + .allow_dns (!bind_) + .allow_nic_name (bind_) + .expect_port (true) + .ipv6 (false); - // Parse the port number (0 is not a valid port). - uint16_t port = (uint16_t) atoi (port_str.c_str ()); - if (port == 0) { - errno = EINVAL; + ip_resolver_t resolver (resolver_opts); + ip_addr_t addr; + + int rc = resolver.resolve (&addr, name_); + if (rc != 0) { return -1; } - dest_address.sin_family = AF_INET; - dest_address.sin_port = htons (port); - - // Only when the udp should bind we allow * as the address - if (addr_str == "*" && bind_) - dest_address.sin_addr.s_addr = htonl (INADDR_ANY); - else - dest_address.sin_addr.s_addr = inet_addr (addr_str.c_str ()); - - if (dest_address.sin_addr.s_addr == INADDR_NONE) { - errno = EINVAL; + if (addr.generic.sa_family != AF_INET) { + // Shouldn't happen return -1; } + dest_address = addr.ipv4; + // we will check only first byte of IP // and if it from 224 to 239, then it can // represent multicast IP. @@ -110,7 +103,7 @@ int zmq::udp_address_t::resolve (const char *name_, bool bind_) bind_address = dest_address; else { bind_address.sin_family = AF_INET; - bind_address.sin_port = htons (port); + bind_address.sin_port = dest_address.sin_port; bind_address.sin_addr.s_addr = htonl (INADDR_ANY); } diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 1b83281068..9dd561db0f 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -6,6 +6,7 @@ set(unittests unittest_poller unittest_mtrie unittest_ip_resolver + unittest_udp_address ) #IF (ENABLE_DRAFTS) diff --git a/unittests/unittest_udp_address.cpp b/unittests/unittest_udp_address.cpp new file mode 100644 index 0000000000..22b2278f2d --- /dev/null +++ b/unittests/unittest_udp_address.cpp @@ -0,0 +1,163 @@ +/* +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 . +*/ + +#include +#include "../tests/testutil.hpp" + +#include +#include + +void setUp () +{ +} + +void tearDown () +{ +} + +// Test an UDP address resolution. If 'bind_addr_' is not NULL +// request a bind address. If 'dest_addr_' is NULL assume the +// resolution is supposed to fail. +static void test_resolve (const char *name_, const char *dest_addr_, + uint16_t expected_port_ = 0, + const char *bind_addr_ = NULL, + bool multicast_ = false) +{ + zmq::udp_address_t addr; + bool bound = bind_addr_ != NULL; + + int rc = addr.resolve (name_, bound); + + if (dest_addr_ == NULL) { + TEST_ASSERT_EQUAL (-1, rc); + TEST_ASSERT_EQUAL (EINVAL, errno); + return; + } else { + TEST_ASSERT_EQUAL (0, rc); + } + + TEST_ASSERT_EQUAL (multicast_, addr.is_mcast ()); + + struct sockaddr_in *dest = (struct sockaddr_in *)addr.dest_addr (); + struct in_addr expected_dest; + assert (test_inet_pton (AF_INET, dest_addr_, &expected_dest) == 1); + + TEST_ASSERT_EQUAL (AF_INET, dest->sin_family); + TEST_ASSERT_EQUAL (expected_dest.s_addr, dest->sin_addr.s_addr); + TEST_ASSERT_EQUAL (htons (expected_port_), dest->sin_port); + + struct sockaddr_in *bind = (struct sockaddr_in *)addr.bind_addr (); + struct in_addr expected_bind; + + if (bind_addr_ == NULL) { + // Bind ANY + bind_addr_ = "0.0.0.0"; + } + + assert (test_inet_pton (AF_INET, bind_addr_, &expected_bind) == 1); + + TEST_ASSERT_EQUAL (AF_INET, bind->sin_family); + TEST_ASSERT_EQUAL (expected_bind.s_addr, bind->sin_addr.s_addr); + TEST_ASSERT_EQUAL (htons (expected_port_), bind->sin_port); +} + +static void test_resolve_ipv4_simple () +{ + test_resolve ("127.0.0.1:5555", "127.0.0.1", 5555); +} + +static void test_resolve_ipv4_bind () +{ + test_resolve ("127.0.0.1:5555", "127.0.0.1", 5555, "127.0.0.1"); +} + +static void test_resolve_ipv4_bind_any () +{ + test_resolve ("*:*", "0.0.0.0", 0, "0.0.0.0"); +} + +static void test_resolve_ipv4_bind_anyport () +{ + test_resolve ("127.0.0.1:*", "127.0.0.1", 0, "127.0.0.1"); +} + +static void test_resolve_ipv4_bind_any_port () +{ + test_resolve ("*:5555", "0.0.0.0", 5555, "0.0.0.0"); +} + +static void test_resolve_ipv4_connect_any () +{ + // Cannot use wildcard for connection + test_resolve ("*:5555", NULL); +} + +static void test_resolve_ipv4_connect_anyport () +{ + test_resolve ("127.0.0.1:*", NULL); +} + +static void test_resolve_ipv4_connect_port0 () +{ + test_resolve ("127.0.0.1:0", "127.0.0.1", 0); +} + +static void test_resolve_ipv4_bind_mcast () +{ + test_resolve ("239.0.0.1:1234", "239.0.0.1", 1234, "0.0.0.0", true); +} + +static void test_resolve_ipv4_connect_mcast () +{ + test_resolve ("239.0.0.1:2222", "239.0.0.1", 2222, NULL, true); +} + +static void test_resolve_ipv6_simple () +{ + if (!is_ipv6_available ()) { + TEST_IGNORE_MESSAGE ("ipv6 is not available"); + } + + // IPv6 not yet supported + test_resolve ("::1", NULL); +} + +int main (void) +{ + zmq::initialize_network (); + setup_test_environment (); + + UNITY_BEGIN (); + + RUN_TEST (test_resolve_ipv4_simple); + RUN_TEST (test_resolve_ipv4_bind); + RUN_TEST (test_resolve_ipv4_bind_any); + RUN_TEST (test_resolve_ipv4_bind_anyport); + RUN_TEST (test_resolve_ipv4_bind_any_port); + RUN_TEST (test_resolve_ipv4_connect_any); + RUN_TEST (test_resolve_ipv4_connect_anyport); + RUN_TEST (test_resolve_ipv4_connect_port0); + RUN_TEST (test_resolve_ipv4_bind_mcast); + RUN_TEST (test_resolve_ipv4_connect_mcast); + RUN_TEST (test_resolve_ipv6_simple); + + zmq::shutdown_network (); + + return UNITY_END (); +}