Path: blob/main/lib/libc/tests/net/inet_net_test.cc
48249 views
/*1* SPDX-License-Identifier: ISC2*3* Copyright (c) 2025 Lexi Winter <[email protected]>4*5* Permission to use, copy, modify, and distribute this software for any6* purpose with or without fee is hereby granted, provided that the above7* copyright notice and this permission notice appear in all copies.8*9* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES10* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF11* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR12* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES13* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN14* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF15* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.16*/1718/*19* Tests for inet_net_pton() and inet_net_ntop().20*/2122#include <sys/types.h>23#include <sys/socket.h>2425#include <netinet/in.h>26#include <arpa/inet.h>2728#include <ranges>29#include <string>30#include <vector>3132#include <atf-c++.hpp>3334using namespace std::literals;3536/*37* inet_net_ntop() and inet_net_pton() for IPv4.38*/39ATF_TEST_CASE_WITHOUT_HEAD(inet_net_inet4)40ATF_TEST_CASE_BODY(inet_net_inet4)41{42/*43* Define a list of addresses we want to check. Each address is passed44* to inet_net_pton() to convert it to an in_addr, then we convert the45* in_addr back to a string and compare it with the expected value. We46* want to test over-long prefixes here (such as 10.0.0.1/8), so we also47* specify what the result is expected to be.48*/4950struct test_addr {51std::string input;52int bits;53std::string output;54};5556auto test_addrs = std::vector<test_addr>{57// Simple prefixes that fall on octet boundaries.58{ "10.0.0.0/8", 8, "10/8" },59{ "10.1.0.0/16", 16, "10.1/16" },60{ "10.1.2.0/24", 24, "10.1.2/24" },61{ "10.1.2.3/32", 32, "10.1.2.3/32" },6263// Simple prefixes with the short-form address.64{ "10/8", 8, "10/8" },65{ "10.1/16", 16, "10.1/16" },66{ "10.1.2/24", 24, "10.1.2/24" },6768// A prefix that doesn't fall on an octet boundary.69{ "10.1.64/18", 18, "10.1.64/18" },7071// An overlong prefix with bits that aren't part of the prefix.72{ "10.0.0.1/8", 8, "10/8" },73};7475for (auto const &addr: test_addrs) {76/*77* Convert the input string to an in_addr + bits, and make78* sure the result produces the number of bits we expected.79*/8081auto in = in_addr{};82auto bits = inet_net_pton(AF_INET, addr.input.c_str(),83&in, sizeof(in));84ATF_REQUIRE(bits != -1);85ATF_REQUIRE_EQ(bits, addr.bits);8687/*88* Convert the in_addr back to a string89*/9091/*92* XXX: Should there be a constant for the size of the result93* buffer? For now, use ADDRSTRLEN + 3 ("/32") + 1 (NUL).94*95* Fill the buffer with 'Z', so we can check the result was96* properly terminated.97*/98auto strbuf = std::vector<char>(INET_ADDRSTRLEN + 3 + 1, 'Z');99auto ret = inet_net_ntop(AF_INET, &in, bits,100strbuf.data(), strbuf.size());101ATF_REQUIRE(ret != NULL);102ATF_REQUIRE_EQ(ret, strbuf.data());103104/* Make sure the result was NUL-terminated and find the NUL */105ATF_REQUIRE(strbuf.size() >= 1);106auto end = std::ranges::find(strbuf, '\0');107ATF_REQUIRE(end != strbuf.end());108109/*110* Check the result matches what we expect. Use a temporary111* string here instead of std::ranges::equal because this112* means ATF can print the mismatch.113*/114auto str = std::string(std::ranges::begin(strbuf), end);115ATF_REQUIRE_EQ(str, addr.output);116}117}118119/*120* inet_net_ntop() and inet_net_pton() for IPv6.121*/122ATF_TEST_CASE_WITHOUT_HEAD(inet_net_inet6)123ATF_TEST_CASE_BODY(inet_net_inet6)124{125/*126* Define a list of addresses we want to check. Each address is127* passed to inet_net_pton() to convert it to an in6_addr, then we128* convert the in6_addr back to a string and compare it with the129* expected value. We want to test over-long prefixes here (such130* as 2001:db8::1/32), so we also specify what the result is131* expected to be.132*/133134struct test_addr {135std::string input;136int bits;137std::string output;138};139140auto test_addrs = std::vector<test_addr>{141// A prefix with a trailing ::142{ "2001:db8::/32", 32, "2001:db8::/32" },143144// A prefix with a leading ::. Note that the output is145// different from the input because inet_ntop() renders146// this prefix with an IPv4 suffix for legacy reasons.147{ "::ffff:0:0/96", 96, "::ffff:0.0.0.0/96" },148149// The same prefix but with the IPv4 legacy form as input.150{ "::ffff:0.0.0.0/96", 96, "::ffff:0.0.0.0/96" },151152// A prefix with an infix ::.153{ "2001:db8::1/128", 128, "2001:db8::1/128" },154155// A prefix with bits set which are outside the prefix;156// these should be silently ignored.157{ "2001:db8:1:1:1:1:1:1/32", 32, "2001:db8::/32" },158159// As above but with infix ::.160{ "2001:db8::1/32", 32, "2001:db8::/32" },161162// A prefix with only ::, commonly used to represent the163// entire address space.164{ "::/0", 0, "::/0" },165166// A single address with no ::.167{ "2001:db8:1:1:1:1:1:1/128", 128, "2001:db8:1:1:1:1:1:1/128" },168169// A prefix with no ::.170{ "2001:db8:1:1:0:0:0:0/64", 64, "2001:db8:1:1::/64" },171172// A prefix which isn't on a 16-bit boundary.173{ "2001:db8:c000::/56", 56, "2001:db8:c000::/56" },174175// A prefix which isn't on a nibble boundary.176{ "2001:db8:c100::/57", 57, "2001:db8:c100::/57" },177178// An address without a prefix length, which should be treated179// as a /128.180{ "2001:db8::", 128, "2001:db8::/128" },181{ "2001:db8::1", 128, "2001:db8::1/128" },182183// Test vectors provided in PR bin/289198.184{ "fe80::1/64", 64, "fe80::/64" },185{ "fe80::f000:74ff:fe54:bed2/64",18664, "fe80::/64" },187{ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64",18864, "ffff:ffff:ffff:ffff::/64" },189{ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128,190"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" },191};192193for (auto const &addr: test_addrs) {194/*195* Convert the input string to an in6_addr + bits, and make196* sure the result produces the number of bits we expected.197*/198199auto in6 = in6_addr{};200errno = 0;201auto bits = inet_net_pton(AF_INET6, addr.input.c_str(),202&in6, sizeof(in6));203ATF_REQUIRE(bits != -1);204ATF_REQUIRE_EQ(bits, addr.bits);205206/*207* Convert the in6_addr back to a string208*/209210/*211* XXX: Should there be a constant for the size of the result212* buffer? For now, use ADDRSTRLEN + 4 ("/128") + 1 (NUL).213*214* Fill the buffer with 'Z', so we can check the result was215* properly terminated.216*/217auto strbuf = std::vector<char>(INET6_ADDRSTRLEN + 4 + 1, 'Z');218auto ret = inet_net_ntop(AF_INET6, &in6, bits,219strbuf.data(), strbuf.size());220ATF_REQUIRE(ret != NULL);221ATF_REQUIRE_EQ(ret, strbuf.data());222223/* Make sure the result was NUL-terminated and find the NUL */224ATF_REQUIRE(strbuf.size() >= 1);225auto end = std::ranges::find(strbuf, '\0');226ATF_REQUIRE(end != strbuf.end());227228/*229* Check the result matches what we expect. Use a temporary230* string here instead of std::ranges::equal because this231* means ATF can print the mismatch.232*/233auto str = std::string(std::ranges::begin(strbuf), end);234ATF_REQUIRE_EQ(str, addr.output);235}236}237238ATF_TEST_CASE_WITHOUT_HEAD(inet_net_pton_invalid)239ATF_TEST_CASE_BODY(inet_net_pton_invalid)240{241auto ret = int{};242auto addr4 = in_addr{};243auto str4 = "10.0.0.0"s;244auto addr6 = in6_addr{};245auto str6 = "2001:db8::"s;246247/* Passing an address which is too short should be an error */248ret = inet_net_pton(AF_INET6, str6.c_str(), &addr6, sizeof(addr6) - 1);249ATF_REQUIRE_EQ(ret, -1);250251ret = inet_net_pton(AF_INET, str4.c_str(), &addr4, sizeof(addr4) - 1);252ATF_REQUIRE_EQ(ret, -1);253254/* Test some generally invalid addresses. */255auto invalid4 = std::vector<std::string>{256// Prefix length too big257"10.0.0.0/33",258// Prefix length is negative259"10.0.0.0/-1",260// Prefix length is not a number261"10.0.0.0/foo",262// Input is not a network prefix263"this is not an IP address",264};265266for (auto const &addr: invalid4) {267auto ret = inet_net_pton(AF_INET, addr.c_str(), &addr4,268sizeof(addr4));269ATF_REQUIRE_EQ(ret, -1);270}271272auto invalid6 = std::vector<std::string>{273// Prefix length too big274"2001:db8::/129",275// Prefix length is negative276"2001:db8::/-1",277// Prefix length is not a number278"2001:db8::/foo",279// Input is not a network prefix280"this is not an IP address",281};282283for (auto const &addr: invalid6) {284auto ret = inet_net_pton(AF_INET6, addr.c_str(), &addr6,285sizeof(addr6));286ATF_REQUIRE_EQ(ret, -1);287}288}289290ATF_TEST_CASE_WITHOUT_HEAD(inet_net_ntop_invalid)291ATF_TEST_CASE_BODY(inet_net_ntop_invalid)292{293auto addr4 = in_addr{};294auto addr6 = in6_addr{};295auto strbuf = std::vector<char>(INET6_ADDRSTRLEN + 4 + 1);296297/*298* Passing a buffer which is too small should not overrun the buffer.299* Test this by initialising the buffer to 'Z', and only providing300* part of it to the function.301*/302303std::ranges::fill(strbuf, 'Z');304auto ret = inet_net_ntop(AF_INET6, &addr6, 128, strbuf.data(), 1);305ATF_REQUIRE_EQ(ret, nullptr);306ATF_REQUIRE_EQ(strbuf[1], 'Z');307308std::ranges::fill(strbuf, 'Z');309ret = inet_net_ntop(AF_INET, &addr4, 32, strbuf.data(), 1);310ATF_REQUIRE_EQ(ret, nullptr);311ATF_REQUIRE_EQ(strbuf[1], 'Z');312313/* Check that invalid prefix lengths return an error */314315ret = inet_net_ntop(AF_INET6, &addr6, 129, strbuf.data(), strbuf.size());316ATF_REQUIRE_EQ(ret, nullptr);317ret = inet_net_ntop(AF_INET6, &addr6, -1, strbuf.data(), strbuf.size());318ATF_REQUIRE_EQ(ret, nullptr);319320ret = inet_net_ntop(AF_INET, &addr4, 33, strbuf.data(), strbuf.size());321ATF_REQUIRE_EQ(ret, nullptr);322ret = inet_net_ntop(AF_INET, &addr4, -1, strbuf.data(), strbuf.size());323ATF_REQUIRE_EQ(ret, nullptr);324}325326ATF_INIT_TEST_CASES(tcs)327{328ATF_ADD_TEST_CASE(tcs, inet_net_inet4);329ATF_ADD_TEST_CASE(tcs, inet_net_inet6);330ATF_ADD_TEST_CASE(tcs, inet_net_pton_invalid);331ATF_ADD_TEST_CASE(tcs, inet_net_ntop_invalid);332}333334335