#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ranges>
#include <string>
#include <vector>
#include <atf-c++.hpp>
using namespace std::literals;
ATF_TEST_CASE_WITHOUT_HEAD(inet_net_inet4)
ATF_TEST_CASE_BODY(inet_net_inet4)
{
struct test_addr {
std::string input;
int bits;
std::string output;
};
auto test_addrs = std::vector<test_addr>{
{ "10.0.0.0/8", 8, "10/8" },
{ "10.1.0.0/16", 16, "10.1/16" },
{ "10.1.2.0/24", 24, "10.1.2/24" },
{ "10.1.2.3/32", 32, "10.1.2.3/32" },
{ "10/8", 8, "10/8" },
{ "10.1/16", 16, "10.1/16" },
{ "10.1.2/24", 24, "10.1.2/24" },
{ "10.1.64/18", 18, "10.1.64/18" },
{ "10.0.0.1/8", 8, "10/8" },
};
for (auto const &addr: test_addrs) {
auto in = in_addr{};
auto bits = inet_net_pton(AF_INET, addr.input.c_str(),
&in, sizeof(in));
ATF_REQUIRE(bits != -1);
ATF_REQUIRE_EQ(bits, addr.bits);
auto strbuf = std::vector<char>(INET_ADDRSTRLEN + 3 + 1, 'Z');
auto ret = inet_net_ntop(AF_INET, &in, bits,
strbuf.data(), strbuf.size());
ATF_REQUIRE(ret != NULL);
ATF_REQUIRE_EQ(ret, strbuf.data());
ATF_REQUIRE(strbuf.size() >= 1);
auto end = std::ranges::find(strbuf, '\0');
ATF_REQUIRE(end != strbuf.end());
auto str = std::string(std::ranges::begin(strbuf), end);
ATF_REQUIRE_EQ(str, addr.output);
}
}
ATF_TEST_CASE_WITHOUT_HEAD(inet_net_inet6)
ATF_TEST_CASE_BODY(inet_net_inet6)
{
struct test_addr {
std::string input;
int bits;
std::string output;
};
auto test_addrs = std::vector<test_addr>{
{ "2001:db8::/32", 32, "2001:db8::/32" },
{ "::ffff:0:0/96", 96, "::ffff:0.0.0.0/96" },
{ "::ffff:0.0.0.0/96", 96, "::ffff:0.0.0.0/96" },
{ "2001:db8::1/128", 128, "2001:db8::1/128" },
{ "2001:db8:1:1:1:1:1:1/32", 32, "2001:db8::/32" },
{ "2001:db8::1/32", 32, "2001:db8::/32" },
{ "::/0", 0, "::/0" },
{ "2001:db8:1:1:1:1:1:1/128", 128, "2001:db8:1:1:1:1:1:1/128" },
{ "2001:db8:1:1:0:0:0:0/64", 64, "2001:db8:1:1::/64" },
{ "2001:db8:c000::/56", 56, "2001:db8:c000::/56" },
{ "2001:db8:c100::/57", 57, "2001:db8:c100::/57" },
{ "2001:db8::", 128, "2001:db8::/128" },
{ "2001:db8::1", 128, "2001:db8::1/128" },
{ "fe80::1/64", 64, "fe80::/64" },
{ "fe80::f000:74ff:fe54:bed2/64",
64, "fe80::/64" },
{ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64",
64, "ffff:ffff:ffff:ffff::/64" },
{ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128,
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" },
};
for (auto const &addr: test_addrs) {
auto in6 = in6_addr{};
errno = 0;
auto bits = inet_net_pton(AF_INET6, addr.input.c_str(),
&in6, sizeof(in6));
ATF_REQUIRE(bits != -1);
ATF_REQUIRE_EQ(bits, addr.bits);
auto strbuf = std::vector<char>(INET6_ADDRSTRLEN + 4 + 1, 'Z');
auto ret = inet_net_ntop(AF_INET6, &in6, bits,
strbuf.data(), strbuf.size());
ATF_REQUIRE(ret != NULL);
ATF_REQUIRE_EQ(ret, strbuf.data());
ATF_REQUIRE(strbuf.size() >= 1);
auto end = std::ranges::find(strbuf, '\0');
ATF_REQUIRE(end != strbuf.end());
auto str = std::string(std::ranges::begin(strbuf), end);
ATF_REQUIRE_EQ(str, addr.output);
}
}
ATF_TEST_CASE_WITHOUT_HEAD(inet_net_pton_invalid)
ATF_TEST_CASE_BODY(inet_net_pton_invalid)
{
auto ret = int{};
auto addr4 = in_addr{};
auto str4 = "10.0.0.0"s;
auto addr6 = in6_addr{};
auto str6 = "2001:db8::"s;
ret = inet_net_pton(AF_INET6, str6.c_str(), &addr6, sizeof(addr6) - 1);
ATF_REQUIRE_EQ(ret, -1);
ret = inet_net_pton(AF_INET, str4.c_str(), &addr4, sizeof(addr4) - 1);
ATF_REQUIRE_EQ(ret, -1);
auto invalid4 = std::vector<std::string>{
"10.0.0.0/33",
"10.0.0.0/-1",
"10.0.0.0/foo",
"this is not an IP address",
};
for (auto const &addr: invalid4) {
auto ret = inet_net_pton(AF_INET, addr.c_str(), &addr4,
sizeof(addr4));
ATF_REQUIRE_EQ(ret, -1);
}
auto invalid6 = std::vector<std::string>{
"2001:db8::/129",
"2001:db8::/-1",
"2001:db8::/foo",
"this is not an IP address",
};
for (auto const &addr: invalid6) {
auto ret = inet_net_pton(AF_INET6, addr.c_str(), &addr6,
sizeof(addr6));
ATF_REQUIRE_EQ(ret, -1);
}
}
ATF_TEST_CASE_WITHOUT_HEAD(inet_net_ntop_invalid)
ATF_TEST_CASE_BODY(inet_net_ntop_invalid)
{
auto addr4 = in_addr{};
auto addr6 = in6_addr{};
auto strbuf = std::vector<char>(INET6_ADDRSTRLEN + 4 + 1);
std::ranges::fill(strbuf, 'Z');
auto ret = inet_net_ntop(AF_INET6, &addr6, 128, strbuf.data(), 1);
ATF_REQUIRE_EQ(ret, nullptr);
ATF_REQUIRE_EQ(strbuf[1], 'Z');
std::ranges::fill(strbuf, 'Z');
ret = inet_net_ntop(AF_INET, &addr4, 32, strbuf.data(), 1);
ATF_REQUIRE_EQ(ret, nullptr);
ATF_REQUIRE_EQ(strbuf[1], 'Z');
ret = inet_net_ntop(AF_INET6, &addr6, 129, strbuf.data(), strbuf.size());
ATF_REQUIRE_EQ(ret, nullptr);
ret = inet_net_ntop(AF_INET6, &addr6, -1, strbuf.data(), strbuf.size());
ATF_REQUIRE_EQ(ret, nullptr);
ret = inet_net_ntop(AF_INET, &addr4, 33, strbuf.data(), strbuf.size());
ATF_REQUIRE_EQ(ret, nullptr);
ret = inet_net_ntop(AF_INET, &addr4, -1, strbuf.data(), strbuf.size());
ATF_REQUIRE_EQ(ret, nullptr);
}
ATF_INIT_TEST_CASES(tcs)
{
ATF_ADD_TEST_CASE(tcs, inet_net_inet4);
ATF_ADD_TEST_CASE(tcs, inet_net_inet6);
ATF_ADD_TEST_CASE(tcs, inet_net_pton_invalid);
ATF_ADD_TEST_CASE(tcs, inet_net_ntop_invalid);
}