/*
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/>.
*/

#include <unity.h>
#include "../tests/testutil.hpp"
#include "../tests/testutil_unity.hpp"
#include "../unittests/unittest_resolver_common.hpp"

#include <ip_resolver.hpp>
#include <ip.hpp>

#ifndef _WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#endif

void setUp ()
{
}

void tearDown ()
{
}

class test_ip_resolver_t : public zmq::ip_resolver_t
{
  public:
    test_ip_resolver_t (zmq::ip_resolver_options_t opts_) :
        ip_resolver_t (opts_)
    {
    }

  protected:
    struct dns_lut_t
    {
        const char *hostname;
        const char *ipv4;
        const char *ipv6;
    };

    virtual int do_getaddrinfo (const char *node_,
                                const char *service_,
                                const struct addrinfo *hints_,
                                struct addrinfo **res_)
    {
        static const struct dns_lut_t dns_lut[] = {
          {"ip.zeromq.org", "10.100.0.1", "fdf5:d058:d656::1"},
          {"ipv4only.zeromq.org", "10.100.0.2", "::ffff:10.100.0.2"},
          {"ipv6only.zeromq.org", NULL, "fdf5:d058:d656::2"},
        };
        unsigned lut_len = sizeof (dns_lut) / sizeof (dns_lut[0]);
        struct addrinfo ai;

        TEST_ASSERT_NULL (service_);

        bool ipv6 = (hints_->ai_family == AF_INET6);
        bool no_dns = (hints_->ai_flags & AI_NUMERICHOST) != 0;
        const char *ip = NULL;

        if (!no_dns) {
            for (unsigned i = 0; i < lut_len; i++) {
                if (strcmp (dns_lut[i].hostname, node_) == 0) {
                    if (ipv6) {
                        ip = dns_lut[i].ipv6;
                    } else {
                        ip = dns_lut[i].ipv4;

                        if (ip == NULL) {
                            //  No address associated with NAME
                            return EAI_NODATA;
                        }
                    }
                }
            }
        }

        if (ip == NULL) {
            //  No entry for 'node_' found in the LUT (or DNS is
            //  forbidden), assume that it's a numeric IP address
            ip = node_;
        }

        //  Call the real getaddrinfo implementation, making sure that it won't
        //  attempt to resolve using DNS
        ai = *hints_;
        ai.ai_flags |= AI_NUMERICHOST;

        return zmq::ip_resolver_t::do_getaddrinfo (ip, NULL, &ai, res_);
    }

    virtual unsigned int do_if_nametoindex (const char *ifname_)
    {
        static const char *dummy_interfaces[] = {
          "lo0",
          "eth0",
          "eth1",
        };
        unsigned lut_len =
          sizeof (dummy_interfaces) / sizeof (dummy_interfaces[0]);

        for (unsigned i = 0; i < lut_len; i++) {
            if (strcmp (dummy_interfaces[i], ifname_) == 0) {
                //  The dummy index will be the position in the array + 1 (0 is
                //  invalid)
                return i + 1;
            }
        }

        //  Not found
        return 0;
    }
};

//  Attempt a resolution and test the results. If 'expected_addr_' is NULL
//  assume that the resolution is meant to fail.
//
//  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.
static void test_resolve (zmq::ip_resolver_options_t opts_,
                          const char *name_,
                          const char *expected_addr_,
                          uint16_t expected_port_ = 0,
                          uint16_t expected_zone_ = 0,
                          const char *expected_addr_v4_failover_ = NULL)
{
    zmq::ip_addr_t addr;
    int family = opts_.ipv6 () ? AF_INET6 : AF_INET;

    if (family == AF_INET6 && !is_ipv6_available ()) {
        TEST_IGNORE_MESSAGE ("ipv6 is not available");
    }

    //  Generate an invalid but well-defined 'ip_addr_t'. Avoids testing
    //  uninitialized values if the code is buggy.
    memset (&addr, 0xba, sizeof (addr));

    test_ip_resolver_t resolver (opts_);

    int rc = resolver.resolve (&addr, name_);

    if (expected_addr_ == NULL) {
        // TODO also check the expected errno
        TEST_ASSERT_EQUAL (-1, rc);
        return;
    } else {
        TEST_ASSERT_SUCCESS_ERRNO (rc);
    }

    validate_address (family, &addr, expected_addr_, expected_port_,
                      expected_zone_, expected_addr_v4_failover_);
}

// Helper macro to define the v4/v6 function pairs
#define MAKE_TEST_V4V6(_test)                                                  \
    static void _test##_ipv4 () { _test (false); }                             \
                                                                               \
    static void _test##_ipv6 () { _test (true); }

static void test_bind_any (bool ipv6_)
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.bindable (true).expect_port (true).ipv6 (ipv6_);

    const char *expected = ipv6_ ? "::" : "0.0.0.0";
    test_resolve (resolver_opts, "*:*", expected, 0);
}
MAKE_TEST_V4V6 (test_bind_any)

static void test_bind_any_port0 (bool ipv6_)
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.bindable (true).expect_port (true).ipv6 (ipv6_);

    //  Should be equivalent to "*:*"
    const char *expected = ipv6_ ? "::" : "0.0.0.0";
    test_resolve (resolver_opts, "*:0", expected, 0);
}
MAKE_TEST_V4V6 (test_bind_any_port0)

static void test_nobind_any (bool ipv6_)
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.expect_port (true).ipv6 (ipv6_);

    //  Wildcard should be rejected if we're not looking for a
    //  bindable address
    test_resolve (resolver_opts, "*:*", NULL);
}
MAKE_TEST_V4V6 (test_nobind_any)

static void test_nobind_any_port (bool ipv6_)
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.expect_port (true).ipv6 (ipv6_);

    //  Wildcard should be rejected if we're not looking for a
    //  bindable address
    test_resolve (resolver_opts, "*:1234", NULL);
}
MAKE_TEST_V4V6 (test_nobind_any_port)

static void test_nobind_addr_anyport (bool ipv6_)
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.expect_port (true).ipv6 (ipv6_);

    //  Wildcard port should be rejected for non-bindable addresses
    test_resolve (resolver_opts, "127.0.0.1:*", NULL);
}
MAKE_TEST_V4V6 (test_nobind_addr_anyport)

static void test_nobind_addr_port0 (bool ipv6_)
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.expect_port (true).ipv6 (ipv6_);

    //  Connecting to port 0 is allowed, although it might not be massively
    //  useful
    const char *expected = ipv6_ ? "::ffff:127.0.0.1" : "127.0.0.1";
    const char *fallback = ipv6_ ? "127.0.0.1" : NULL;
    test_resolve (resolver_opts, "127.0.0.1:0", expected, 0, 0, fallback);
}
MAKE_TEST_V4V6 (test_nobind_addr_port0)

static void test_parse_ipv4_simple ()
{
    zmq::ip_resolver_options_t resolver_opts;

    test_resolve (resolver_opts, "1.2.128.129", "1.2.128.129");
}

static void test_parse_ipv4_zero ()
{
    zmq::ip_resolver_options_t resolver_opts;

    test_resolve (resolver_opts, "0.0.0.0", "0.0.0.0");
}

static void test_parse_ipv4_max ()
{
    zmq::ip_resolver_options_t resolver_opts;

    test_resolve (resolver_opts, "255.255.255.255", "255.255.255.255");
}

static void test_parse_ipv4_brackets ()
{
    zmq::ip_resolver_options_t resolver_opts;

    //  Not particularly useful, but valid
    test_resolve (resolver_opts, "[1.2.128.129]", "1.2.128.129");
}

static void test_parse_ipv4_brackets_missingl ()
{
    zmq::ip_resolver_options_t resolver_opts;

    test_resolve (resolver_opts, "1.2.128.129]", NULL);
}

static void test_parse_ipv4_brackets_missingr ()
{
    zmq::ip_resolver_options_t resolver_opts;

    test_resolve (resolver_opts, "[1.2.128.129", NULL);
}

static void test_parse_ipv4_brackets_bad ()
{
    zmq::ip_resolver_options_t resolver_opts;

    test_resolve (resolver_opts, "[1.2.128].129", NULL);
}

static void test_parse_ipv4_reject_port ()
{
    zmq::ip_resolver_options_t resolver_opts;

    //  No port expected, should be rejected
    test_resolve (resolver_opts, "1.2.128.129:123", NULL);
}

static void test_parse_ipv4_reject_any ()
{
    zmq::ip_resolver_options_t resolver_opts;

    //  No port expected, should be rejected
    test_resolve (resolver_opts, "1.2.128.129:*", NULL);
}

static void test_parse_ipv4_reject_ipv6 ()
{
    zmq::ip_resolver_options_t resolver_opts;

    //  No port expected, should be rejected
    test_resolve (resolver_opts, "::1", NULL);
}

static void test_parse_ipv4_port ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.expect_port (true);

    test_resolve (resolver_opts, "1.2.128.129:123", "1.2.128.129", 123);
}

static void test_parse_ipv4_port0 ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.expect_port (true);

    //  Port 0 is accepted and is equivalent to *
    test_resolve (resolver_opts, "1.2.128.129:0", "1.2.128.129", 0);
}

static void test_parse_ipv4_port_garbage ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.expect_port (true);

    //  The code doesn't validate that the port doesn't contain garbage
    test_resolve (resolver_opts, "1.2.3.4:567bad", "1.2.3.4", 567);
}

static void test_parse_ipv4_port_missing ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.expect_port (true);

    test_resolve (resolver_opts, "1.2.3.4", NULL);
}

static void test_parse_ipv4_port_bad ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.expect_port (true);

    test_resolve (resolver_opts, "1.2.3.4:bad", NULL);
}

static void test_parse_ipv4_port_brackets ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.expect_port (true);

    test_resolve (resolver_opts, "[192.168.1.1]:5555", "192.168.1.1", 5555);
}

static void test_parse_ipv4_port_brackets_bad ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.expect_port (true);

    test_resolve (resolver_opts, "[192.168.1.1:]5555", NULL);
}

static void test_parse_ipv4_port_brackets_bad2 ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.expect_port (true);

    test_resolve (resolver_opts, "[192.168.1.1:5555]", NULL);
}

static void test_parse_ipv4_wild_brackets_bad ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.expect_port (true);

    test_resolve (resolver_opts, "[192.168.1.1:*]", NULL);
}

static void test_parse_ipv4_port_ipv6_reject ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.expect_port (true);

    test_resolve (resolver_opts, "[::1]:1234", NULL);
}

static void test_parse_ipv6_simple ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true);

    test_resolve (resolver_opts, "::1", "::1");
}

static void test_parse_ipv6_simple2 ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true);

    test_resolve (resolver_opts, "abcd:1234::1:0:234", "abcd:1234::1:0:234");
}

static void test_parse_ipv6_zero ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true);

    test_resolve (resolver_opts, "::", "::");
}

static void test_parse_ipv6_max ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true);

    test_resolve (resolver_opts, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
                  "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
}

static void test_parse_ipv6_brackets ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true);

    test_resolve (resolver_opts, "[::1]", "::1");
}

static void test_parse_ipv6_brackets_missingl ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true);

    test_resolve (resolver_opts, "::1]", NULL);
}

static void test_parse_ipv6_brackets_missingr ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true);

    test_resolve (resolver_opts, "[::1", NULL);
}

static void test_parse_ipv6_brackets_bad ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true);

    test_resolve (resolver_opts, "[abcd:1234::1:]0:234", NULL);
}

static void test_parse_ipv6_port ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true).expect_port (true);

    test_resolve (resolver_opts, "[1234::1]:80", "1234::1", 80);
}

static void test_parse_ipv6_port_any ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true).expect_port (true).bindable (true);

    test_resolve (resolver_opts, "[1234::1]:*", "1234::1", 0);
}

static void test_parse_ipv6_port_nobrackets ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true).expect_port (true);

    //  Should this be allowed? Seems error-prone but so far ZMQ accepts it.
    test_resolve (resolver_opts, "abcd:1234::1:0:234:123", "abcd:1234::1:0:234",
                  123);
}

static void test_parse_ipv4_in_ipv6 ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true);

    //  Parsing IPv4 should also work if an IPv6 is requested, it returns an
    //  IPv6 with the IPv4 address embedded (except sometimes on Windows where
    //  we end up with an IPv4 anyway)
    test_resolve (resolver_opts, "11.22.33.44", "::ffff:11.22.33.44", 0, 0,
                  "11.22.33.44");
}

static void test_parse_ipv4_in_ipv6_port ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true).expect_port (true);

    test_resolve (resolver_opts, "11.22.33.44:55", "::ffff:11.22.33.44", 55, 0,
                  "11.22.33.44");
}

static void test_parse_ipv6_scope_int ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true);

    test_resolve (resolver_opts, "3000:4:5::1:234%2", "3000:4:5::1:234", 0, 2);
}

static void test_parse_ipv6_scope_zero ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true);

    test_resolve (resolver_opts, "3000:4:5::1:234%0", NULL);
}

static void test_parse_ipv6_scope_int_port ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.expect_port (true).ipv6 (true);

    test_resolve (resolver_opts, "3000:4:5::1:234%2:1111", "3000:4:5::1:234",
                  1111, 2);
}

static void test_parse_ipv6_scope_if ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true);

    test_resolve (resolver_opts, "3000:4:5::1:234%eth1", "3000:4:5::1:234", 0,
                  3);
}

static void test_parse_ipv6_scope_if_port ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.expect_port (true).ipv6 (true);

    test_resolve (resolver_opts, "3000:4:5::1:234%eth0:8080", "3000:4:5::1:234",
                  8080, 2);
}

static void test_parse_ipv6_scope_if_port_brackets ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.expect_port (true).ipv6 (true);

    test_resolve (resolver_opts, "[3000:4:5::1:234%eth0]:8080",
                  "3000:4:5::1:234", 8080, 2);
}

static void test_parse_ipv6_scope_badif ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true);

    test_resolve (resolver_opts, "3000:4:5::1:234%bad0", NULL);
}

static void test_dns_ipv4_simple ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.allow_dns (true);

    test_resolve (resolver_opts, "ip.zeromq.org", "10.100.0.1");
}

static void test_dns_ipv4_only ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.allow_dns (true);

    test_resolve (resolver_opts, "ipv4only.zeromq.org", "10.100.0.2");
}

static void test_dns_ipv4_invalid ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.allow_dns (true);

    test_resolve (resolver_opts, "invalid.zeromq.org", NULL);
}

static void test_dns_ipv4_ipv6 ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.allow_dns (true);

    test_resolve (resolver_opts, "ipv6only.zeromq.org", NULL);
}

static void test_dns_ipv4_numeric ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.allow_dns (true);

    //  Numeric IPs should still work
    test_resolve (resolver_opts, "5.4.3.2", "5.4.3.2");
}

static void test_dns_ipv4_port ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.expect_port (true).allow_dns (true);

    test_resolve (resolver_opts, "ip.zeromq.org:1234", "10.100.0.1", 1234);
}

static void test_dns_ipv6_simple ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true).allow_dns (true);

    test_resolve (resolver_opts, "ip.zeromq.org", "fdf5:d058:d656::1");
}

static void test_dns_ipv6_only ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true).allow_dns (true);

    test_resolve (resolver_opts, "ipv6only.zeromq.org", "fdf5:d058:d656::2");
}

static void test_dns_ipv6_invalid ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true).allow_dns (true);

    test_resolve (resolver_opts, "invalid.zeromq.org", NULL);
}

static void test_dns_ipv6_ipv4 ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true).allow_dns (true);

    //  If a host doesn't have an IPv6 then it should resolve as an embedded v4
    //  address in an IPv6
    test_resolve (resolver_opts, "ipv4only.zeromq.org", "::ffff:10.100.0.2");
}

static void test_dns_ipv6_numeric ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true).allow_dns (true);

    //  Numeric IPs should still work
    test_resolve (resolver_opts, "fdf5:d058:d656::1", "fdf5:d058:d656::1");
}

static void test_dns_ipv6_port ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (true).expect_port (true).allow_dns (true);

    test_resolve (resolver_opts, "ip.zeromq.org:1234", "fdf5:d058:d656::1",
                  1234);
}

void test_dns_brackets ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.allow_dns (true);

    test_resolve (resolver_opts, "[ip.zeromq.org]", "10.100.0.1");
}

void test_dns_brackets_bad ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.allow_dns (true);

    test_resolve (resolver_opts, "[ip.zeromq].org", NULL);
}

void test_dns_brackets_port ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.allow_dns (true);

    test_resolve (resolver_opts, "[ip.zeromq.org]:22", "10.100.0.1", 22);
}

void test_dns_brackets_port_bad ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.allow_dns (true);

    test_resolve (resolver_opts, "[ip.zeromq.org:22]", NULL);
}

void test_dns_deny (bool ipv6_)
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.allow_dns (false).ipv6 (ipv6_);

    //  DNS resolution shouldn't work when disallowed
    test_resolve (resolver_opts, "ip.zeromq.org", NULL);
}
MAKE_TEST_V4V6 (test_dns_deny)

void test_dns_ipv6_scope ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.allow_dns (true).ipv6 (true);

    //  Not sure if that's very useful but you could technically add a scope
    //  identifier to a hostname
    test_resolve (resolver_opts, "ip.zeromq.org%lo0", "fdf5:d058:d656::1", 0,
                  1);
}

void test_dns_ipv6_scope_port ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.allow_dns (true).expect_port (true).ipv6 (true);

    //  Not sure if that's very useful but you could technically add a scope
    //  identifier to a hostname
    test_resolve (resolver_opts, "ip.zeromq.org%lo0:4444", "fdf5:d058:d656::1",
                  4444, 1);
}

void test_dns_ipv6_scope_port_brackets ()
{
    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.allow_dns (true).expect_port (true).ipv6 (true);

    test_resolve (resolver_opts, "[ip.zeromq.org%lo0]:4444",
                  "fdf5:d058:d656::1", 4444, 1);
}

static void test_addr (int family_, const char *addr_, bool multicast_)
{
    if (family_ == AF_INET6 && !is_ipv6_available ()) {
        TEST_IGNORE_MESSAGE ("ipv6 is not available");
    }

    zmq::ip_resolver_options_t resolver_opts;

    resolver_opts.ipv6 (family_ == AF_INET6);

    test_ip_resolver_t resolver (resolver_opts);
    zmq::ip_addr_t addr;

    TEST_ASSERT_SUCCESS_ERRNO (resolver.resolve (&addr, addr_));

    TEST_ASSERT_EQUAL (family_, addr.family ());
    TEST_ASSERT_EQUAL (multicast_, addr.is_multicast ());
}

static void test_addr_unicast_ipv4 ()
{
    test_addr (AF_INET, "1.2.3.4", false);
}

static void test_addr_unicast_ipv6 ()
{
    test_addr (AF_INET6, "abcd::1", false);
}

static void test_addr_multicast_ipv4 ()
{
    test_addr (AF_INET, "230.1.2.3", true);
}

static void test_addr_multicast_ipv6 ()
{
    test_addr (AF_INET6, "ffab::1234", true);
}

static void test_addr_multicast_ipv4_min ()
{
    test_addr (AF_INET, "224.0.0.0", true);
}

static void test_addr_multicast_ipv6_min ()
{
    test_addr (AF_INET6, "ff00::", true);
}

static void test_addr_multicast_ipv4_max ()
{
    test_addr (AF_INET, "239.255.255.255", true);
}

static void test_addr_multicast_ipv6_max ()
{
    test_addr (AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", true);
}

static void test_addr_multicast_ipv4_sub ()
{
    test_addr (AF_INET, "223.255.255.255", false);
}

static void test_addr_multicast_ipv6_sub ()
{
    test_addr (AF_INET6, "feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", false);
}

static void test_addr_multicast_ipv4_over ()
{
    test_addr (AF_INET, "240.0.0.0", false);
}

int main (void)
{
    zmq::initialize_network ();
    setup_test_environment ();

    UNITY_BEGIN ();

    RUN_TEST (test_bind_any_ipv4);
    RUN_TEST (test_bind_any_ipv6);
    RUN_TEST (test_bind_any_port0_ipv4);
    RUN_TEST (test_bind_any_port0_ipv6);
    RUN_TEST (test_nobind_any_ipv4);
    RUN_TEST (test_nobind_any_ipv6);
    RUN_TEST (test_nobind_any_port_ipv4);
    RUN_TEST (test_nobind_any_port_ipv6);
    RUN_TEST (test_nobind_addr_anyport_ipv4);
    RUN_TEST (test_nobind_addr_anyport_ipv6);
    RUN_TEST (test_nobind_addr_port0_ipv4);
    RUN_TEST (test_nobind_addr_port0_ipv6);
    RUN_TEST (test_parse_ipv4_simple);
    RUN_TEST (test_parse_ipv4_zero);
    RUN_TEST (test_parse_ipv4_max);
    RUN_TEST (test_parse_ipv4_brackets);
    RUN_TEST (test_parse_ipv4_brackets_missingl);
    RUN_TEST (test_parse_ipv4_brackets_missingr);
    RUN_TEST (test_parse_ipv4_brackets_bad);
    RUN_TEST (test_parse_ipv4_reject_port);
    RUN_TEST (test_parse_ipv4_reject_any);
    RUN_TEST (test_parse_ipv4_reject_ipv6);
    RUN_TEST (test_parse_ipv4_port);
    RUN_TEST (test_parse_ipv4_port0);
    RUN_TEST (test_parse_ipv4_port_garbage);
    RUN_TEST (test_parse_ipv4_port_missing);
    RUN_TEST (test_parse_ipv4_port_bad);
    RUN_TEST (test_parse_ipv4_port_brackets);
    RUN_TEST (test_parse_ipv4_port_brackets_bad);
    RUN_TEST (test_parse_ipv4_port_brackets_bad2);
    RUN_TEST (test_parse_ipv4_wild_brackets_bad);
    RUN_TEST (test_parse_ipv4_port_ipv6_reject);
    RUN_TEST (test_parse_ipv6_simple);
    RUN_TEST (test_parse_ipv6_simple2);
    RUN_TEST (test_parse_ipv6_zero);
    RUN_TEST (test_parse_ipv6_max);
    RUN_TEST (test_parse_ipv6_brackets);
    RUN_TEST (test_parse_ipv6_brackets_missingl);
    RUN_TEST (test_parse_ipv6_brackets_missingr);
    RUN_TEST (test_parse_ipv6_brackets_bad);
    RUN_TEST (test_parse_ipv6_port);
    RUN_TEST (test_parse_ipv6_port_any);
    RUN_TEST (test_parse_ipv6_port_nobrackets);
    RUN_TEST (test_parse_ipv4_in_ipv6);
    RUN_TEST (test_parse_ipv4_in_ipv6_port);
    RUN_TEST (test_parse_ipv6_scope_int);
    RUN_TEST (test_parse_ipv6_scope_zero);
    RUN_TEST (test_parse_ipv6_scope_int_port);
    RUN_TEST (test_parse_ipv6_scope_if);
    RUN_TEST (test_parse_ipv6_scope_if_port);
    RUN_TEST (test_parse_ipv6_scope_if_port_brackets);
    RUN_TEST (test_parse_ipv6_scope_badif);
    RUN_TEST (test_dns_ipv4_simple);
    RUN_TEST (test_dns_ipv4_only);
    RUN_TEST (test_dns_ipv4_invalid);
    RUN_TEST (test_dns_ipv4_ipv6);
    RUN_TEST (test_dns_ipv4_numeric);
    RUN_TEST (test_dns_ipv4_port);
    RUN_TEST (test_dns_ipv6_simple);
    RUN_TEST (test_dns_ipv6_only);
    RUN_TEST (test_dns_ipv6_invalid);
    RUN_TEST (test_dns_ipv6_ipv4);
    RUN_TEST (test_dns_ipv6_numeric);
    RUN_TEST (test_dns_ipv6_port);
    RUN_TEST (test_dns_brackets);
    RUN_TEST (test_dns_brackets_bad);
    RUN_TEST (test_dns_deny_ipv4);
    RUN_TEST (test_dns_deny_ipv6);
    RUN_TEST (test_dns_ipv6_scope);
    RUN_TEST (test_dns_ipv6_scope_port);
    RUN_TEST (test_dns_ipv6_scope_port_brackets);
    RUN_TEST (test_addr_unicast_ipv4);
    RUN_TEST (test_addr_unicast_ipv6);
    RUN_TEST (test_addr_multicast_ipv4);
    RUN_TEST (test_addr_multicast_ipv6);
    RUN_TEST (test_addr_multicast_ipv4_min);
    RUN_TEST (test_addr_multicast_ipv6_min);
    RUN_TEST (test_addr_multicast_ipv4_max);
    RUN_TEST (test_addr_multicast_ipv6_max);
    RUN_TEST (test_addr_multicast_ipv4_sub);
    RUN_TEST (test_addr_multicast_ipv6_sub);
    RUN_TEST (test_addr_multicast_ipv4_over);

    zmq::shutdown_network ();

    return UNITY_END ();
}
