#include <catch.hpp> #include <internal/facts/windows/networking_resolver.hpp> #include <leatherman/windows/windows.hpp> #include <internal/util/windows/wsa.hpp> #include <Ws2tcpip.h> using namespace std; // Test address manipulation utilities. struct networking_utilities : facter::facts::windows::networking_resolver { public: using networking_resolver::create_ipv4_mask; using networking_resolver::create_ipv6_mask; using networking_resolver::mask_ipv4_address; using networking_resolver::mask_ipv6_address; template <typename T> std::string address_to_string(T const& addr) { return winsock.address_to_string(const_cast<T&>(addr)); } template <typename T> std::string address_to_string(T const* addr, T const* mask) { return {}; } facter::util::windows::wsa winsock; }; template <> std::string networking_utilities::address_to_string<sockaddr_in>(sockaddr_in const* addr, sockaddr_in const* mask) { auto masked = mask_ipv4_address(reinterpret_cast<sockaddr const*>(addr), *mask); return address_to_string(masked); } template <> std::string networking_utilities::address_to_string<sockaddr_in6>(sockaddr_in6 const* addr, sockaddr_in6 const* mask) { auto masked = mask_ipv6_address(reinterpret_cast<sockaddr const*>(addr), *mask); return address_to_string(masked); } static constexpr sockaddr_in make_sockaddr_in(u_char a, u_char b, u_char c, u_char d) { return {AF_INET, 0u, in_addr{a, b, c, d}, {0, 0, 0, 0, 0, 0, 0, 0}}; } bool operator== (in_addr const& a, in_addr const& b) { return a.S_un.S_addr == b.S_un.S_addr; } bool operator!= (in_addr const& a, in_addr const& b) { return !(a == b); } bool operator== (sockaddr_in const& a, sockaddr_in const& b) { return a.sin_family == b.sin_family && a.sin_addr == b.sin_addr; } bool operator!= (sockaddr_in const& a, sockaddr_in const& b) { return !(a == b); } struct ipv4_case { uint8_t masklen; sockaddr_in addr; string str; }; static const ipv4_case ip4_masks[] = { {0u, make_sockaddr_in(0u, 0u, 0u, 0u), "0.0.0.0"}, {255u, make_sockaddr_in(255u, 255u, 255u, 255u), "255.255.255.255"}, {32u, make_sockaddr_in(255u, 255u, 255u, 255u), "255.255.255.255"}, {33u, make_sockaddr_in(255u, 255u, 255u, 255u), "255.255.255.255"}, {24u, make_sockaddr_in(255u, 255u, 255u, 0u), "255.255.255.0"}, {9u, make_sockaddr_in(255u, 128u, 0u, 0u), "255.128.0.0"}, {1u, make_sockaddr_in(128u, 0u, 0u, 0u), "128.0.0.0"}, {31u, make_sockaddr_in(255u, 255u, 255u, 254u), "255.255.255.254"} }; SCENARIO("create IPv4 masks") { networking_utilities util; // Test various valid masklen, too large masklen, verify output. for (auto const& item : ip4_masks) { auto mask = util.create_ipv4_mask(item.masklen); REQUIRE(mask == item.addr); } // Verify the boolean operator behaves as expected. REQUIRE(ip4_masks[0].addr != ip4_masks[1].addr); REQUIRE(ip4_masks[0].addr != ip4_masks[6].addr); } SCENARIO("IPv4 address to string") { networking_utilities util; static const pair<sockaddr_in, string> ip4_cases[] = { {make_sockaddr_in(192u, 168u, 0u, 1u), "192.168.0.1"}, {make_sockaddr_in(200u, 0u, 154u, 12u), "200.0.154.12"}, {make_sockaddr_in(1u, 255u, 128u, 42u), "1.255.128.42"} }; // Test various valid addresses to string. for (auto const& item : ip4_cases) { REQUIRE(item.second == util.address_to_string(item.first)); } // Test various mask addresses to string. for (auto const& item : ip4_masks) { REQUIRE(item.str == util.address_to_string(item.addr)); } } SCENARIO("IPv4 address with mask to string") { networking_utilities util; // Test address_to_string with masks applied. auto min = make_sockaddr_in(0u, 0u, 0u, 0u); auto max = make_sockaddr_in(255u, 255u, 255u, 255u); auto zoro = make_sockaddr_in(255u, 255u, 128u, 0u); auto v = make_sockaddr_in(128u, 0u, 0u, 0u); auto local = make_sockaddr_in(192u, 168u, 0u, 1u); auto outer = make_sockaddr_in(200u, 0u, 154u, 12u); REQUIRE("0.0.0.0" == util.address_to_string(&local, &min)); REQUIRE("192.168.0.1" == util.address_to_string(&local, &max)); REQUIRE("192.168.0.0" == util.address_to_string(&local, &zoro)); REQUIRE("128.0.0.0" == util.address_to_string(&local, &v)); REQUIRE("0.0.0.0" == util.address_to_string(&outer, &min)); REQUIRE("200.0.154.12" == util.address_to_string(&outer, &max)); REQUIRE("200.0.128.0" == util.address_to_string(&outer, &zoro)); REQUIRE("128.0.0.0" == util.address_to_string(&outer, &v)); } static sockaddr_in6 make_sockaddr_in6(array<u_char, 16> x) { sockaddr_in6 addr = {AF_INET6}; memcpy(addr.sin6_addr.u.Byte, x.data(), 16*sizeof(u_char)); return addr; } bool operator== (in6_addr const& a, in6_addr const& b) { return 0 == memcmp(a.u.Word, b.u.Word, 8*sizeof(u_short)); } bool operator!= (in6_addr const& a, in6_addr const& b) { return !(a == b); } bool operator== (sockaddr_in6 const& a, sockaddr_in6 const& b) { return a.sin6_family == b.sin6_family && a.sin6_addr == b.sin6_addr; } bool operator!= (sockaddr_in6 const& a, sockaddr_in6 const& b) { return !(a == b); } struct ipv6_case { uint8_t masklen; string str; sockaddr_in6 addr; }; static const ipv6_case ip6_masks[] = { {0u, "::", make_sockaddr_in6({})}, {1u, "8000::", make_sockaddr_in6({0x80u})}, {4u, "f000::", make_sockaddr_in6({0xf0u})}, {64u, "ffff:ffff:ffff:ffff::", make_sockaddr_in6({0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu})}, {65u, "ffff:ffff:ffff:ffff:8000::", make_sockaddr_in6({0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0x80u})}, {127u, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", make_sockaddr_in6({0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xfeu})}, {128u, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", make_sockaddr_in6({0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu})}, {129u, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", make_sockaddr_in6({0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu})} }; SCENARIO("create IPv6 mask") { networking_utilities util; // Test various valid masklen, too large masklen, verify output. for (auto const& item : ip6_masks) { auto mask = util.create_ipv6_mask(item.masklen); REQUIRE(item.addr == mask); } // Verify the boolean operator behaves as expected. REQUIRE(ip6_masks[0].addr != ip6_masks[1].addr); REQUIRE(ip6_masks[0].addr != ip6_masks[6].addr); } SCENARIO("IPv6 address to string") { networking_utilities util; static const pair<sockaddr_in6, string> ip6_cases[] = { {make_sockaddr_in6({0u, 0u, 0u, 0u, 0xffu, 0xe9u, 0xffu, 0xffu, 0xffu, 0xabu, 1u}), "::ffe9:ffff:ffab:100:0:0"}, {make_sockaddr_in6({0u, 0xffu, 0xe9u, 0xffu, 0xffu, 0xffu, 0xabu, 1u}), "ff:e9ff:ffff:ab01::"}, {make_sockaddr_in6({0xfeu, 0x80u, 0x01u, 0x23u, 0u, 0u, 0u, 0u, 0x45u, 0x67u, 0x89u, 0xabu}), "fe80:123::4567:89ab:0:0"}, {make_sockaddr_in6({0xfeu, 0x80u, 0x01u, 0x23u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0x45u, 0x67u, 0x89u, 0xabu}), "fe80:123::4567:89ab"}, {make_sockaddr_in6({0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 1u}), "::1"}, }; // Test various valid addresses to string. for (auto const& item : ip6_cases) { REQUIRE(item.second == util.address_to_string(item.first)); } // Test various mask addresses to string. for (auto const& item : ip6_masks) { REQUIRE(item.str == util.address_to_string(item.addr)); } } SCENARIO("IPv6 address with mask to string") { networking_utilities util; // Test address_to_string with masks applied. auto min = make_sockaddr_in6({}); auto max = make_sockaddr_in6({0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu}); auto zoro = make_sockaddr_in6({0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu}); auto v = make_sockaddr_in6({0xf8u}); auto local = make_sockaddr_in6({0xfeu, 0x80u, 0x01u, 0x23u, 0u, 0u, 0u, 0u, 0x45u, 0x67u, 0x89u, 0xabu}); auto outer = make_sockaddr_in6({0u, 0xffu, 0xe9u, 0xffu, 0xffu, 0xffu, 0xabu, 1u}); REQUIRE("::" == util.address_to_string(&local, &min)); REQUIRE("fe80:123::4567:89ab:0:0" == util.address_to_string(&local, &max)); REQUIRE("fe80:123::" == util.address_to_string(&local, &zoro)); REQUIRE("f800::" == util.address_to_string(&local, &v)); REQUIRE("::" == util.address_to_string(&outer, &min)); REQUIRE("ff:e9ff:ffff:ab01::" == util.address_to_string(&outer, &max)); REQUIRE("ff:e9ff:ffff:ab01::" == util.address_to_string(&outer, &zoro)); REQUIRE("::" == util.address_to_string(&outer, &v)); }