1 /* 2 * SPDX-License-Identifier: ISC 3 * 4 * Copyright (c) 2025 Lexi Winter <ivy@FreeBSD.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * Tests for inet_net_pton() and inet_net_ntop(). 21 */ 22 23 #include <sys/types.h> 24 #include <sys/socket.h> 25 26 #include <netinet/in.h> 27 #include <arpa/inet.h> 28 29 #include <ranges> 30 #include <string> 31 #include <vector> 32 33 #include <atf-c++.hpp> 34 35 using namespace std::literals; 36 37 /* 38 * inet_net_ntop() and inet_net_pton() for IPv4. 39 */ 40 ATF_TEST_CASE_WITHOUT_HEAD(inet_net_inet4) 41 ATF_TEST_CASE_BODY(inet_net_inet4) 42 { 43 /* 44 * Define a list of addresses we want to check. Each address is passed 45 * to inet_net_pton() to convert it to an in_addr, then we convert the 46 * in_addr back to a string and compare it with the expected value. We 47 * want to test over-long prefixes here (such as 10.0.0.1/8), so we also 48 * specify what the result is expected to be. 49 */ 50 51 struct test_addr { 52 std::string input; 53 int bits; 54 std::string output; 55 }; 56 57 auto test_addrs = std::vector<test_addr>{ 58 // Simple prefixes that fall on octet boundaries. 59 { "10.0.0.0/8", 8, "10/8" }, 60 { "10.1.0.0/16", 16, "10.1/16" }, 61 { "10.1.2.0/24", 24, "10.1.2/24" }, 62 { "10.1.2.3/32", 32, "10.1.2.3/32" }, 63 64 // Simple prefixes with the short-form address. 65 { "10/8", 8, "10/8" }, 66 { "10.1/16", 16, "10.1/16" }, 67 { "10.1.2/24", 24, "10.1.2/24" }, 68 69 // A prefix that doesn't fall on an octet boundary. 70 { "10.1.64/18", 18, "10.1.64/18" }, 71 72 // An overlong prefix with bits that aren't part of the prefix. 73 { "10.0.0.1/8", 8, "10/8" }, 74 }; 75 76 for (auto const &addr: test_addrs) { 77 /* 78 * Convert the input string to an in_addr + bits, and make 79 * sure the result produces the number of bits we expected. 80 */ 81 82 auto in = in_addr{}; 83 auto bits = inet_net_pton(AF_INET, addr.input.c_str(), 84 &in, sizeof(in)); 85 ATF_REQUIRE(bits != -1); 86 ATF_REQUIRE_EQ(bits, addr.bits); 87 88 /* 89 * Convert the in_addr back to a string 90 */ 91 92 /* 93 * XXX: Should there be a constant for the size of the result 94 * buffer? For now, use ADDRSTRLEN + 3 ("/32") + 1 (NUL). 95 * 96 * Fill the buffer with 'Z', so we can check the result was 97 * properly terminated. 98 */ 99 auto strbuf = std::vector<char>(INET_ADDRSTRLEN + 3 + 1, 'Z'); 100 auto ret = inet_net_ntop(AF_INET, &in, bits, 101 strbuf.data(), strbuf.size()); 102 ATF_REQUIRE(ret != NULL); 103 ATF_REQUIRE_EQ(ret, strbuf.data()); 104 105 /* Make sure the result was NUL-terminated and find the NUL */ 106 ATF_REQUIRE(strbuf.size() >= 1); 107 auto end = std::ranges::find(strbuf, '\0'); 108 ATF_REQUIRE(end != strbuf.end()); 109 110 /* 111 * Check the result matches what we expect. Use a temporary 112 * string here instead of std::ranges::equal because this 113 * means ATF can print the mismatch. 114 */ 115 auto str = std::string(std::ranges::begin(strbuf), end); 116 ATF_REQUIRE_EQ(str, addr.output); 117 } 118 } 119 120 /* 121 * inet_net_ntop() and inet_net_pton() for IPv6. 122 */ 123 ATF_TEST_CASE_WITHOUT_HEAD(inet_net_inet6) 124 ATF_TEST_CASE_BODY(inet_net_inet6) 125 { 126 /* 127 * Define a list of addresses we want to check. Each address is 128 * passed to inet_net_pton() to convert it to an in6_addr, then we 129 * convert the in6_addr back to a string and compare it with the 130 * expected value. We want to test over-long prefixes here (such 131 * as 2001:db8::1/32), so we also specify what the result is 132 * expected to be. 133 */ 134 135 struct test_addr { 136 std::string input; 137 int bits; 138 std::string output; 139 }; 140 141 auto test_addrs = std::vector<test_addr>{ 142 // A prefix with a trailing :: 143 { "2001:db8::/32", 32, "2001:db8::/32" }, 144 145 // A prefix with a leading ::. Note that the output is 146 // different from the input because inet_ntop() renders 147 // this prefix with an IPv4 suffix for legacy reasons. 148 { "::ffff:0:0/96", 96, "::ffff:0.0.0.0/96" }, 149 150 // The same prefix but with the IPv4 legacy form as input. 151 { "::ffff:0.0.0.0/96", 96, "::ffff:0.0.0.0/96" }, 152 153 // A prefix with an infix ::. 154 { "2001:db8::1/128", 128, "2001:db8::1/128" }, 155 156 // A prefix with bits set which are outside the prefix; 157 // these should be silently ignored. 158 { "2001:db8:1:1:1:1:1:1/32", 32, "2001:db8::/32" }, 159 160 // As above but with infix ::. 161 { "2001:db8::1/32", 32, "2001:db8::/32" }, 162 163 // A prefix with only ::, commonly used to represent the 164 // entire address space. 165 { "::/0", 0, "::/0" }, 166 167 // A single address with no ::. 168 { "2001:db8:1:1:1:1:1:1/128", 128, "2001:db8:1:1:1:1:1:1/128" }, 169 170 // A prefix with no ::. 171 { "2001:db8:1:1:0:0:0:0/64", 64, "2001:db8:1:1::/64" }, 172 173 // A prefix which isn't on a 16-bit boundary. 174 { "2001:db8:c000::/56", 56, "2001:db8:c000::/56" }, 175 176 // A prefix which isn't on a nibble boundary. 177 { "2001:db8:c100::/57", 57, "2001:db8:c100::/57" }, 178 179 // An address without a prefix length, which should be treated 180 // as a /128. 181 { "2001:db8::", 128, "2001:db8::/128" }, 182 { "2001:db8::1", 128, "2001:db8::1/128" }, 183 184 // Test vectors provided in PR bin/289198. 185 { "fe80::1/64", 64, "fe80::/64" }, 186 { "fe80::f000:74ff:fe54:bed2/64", 187 64, "fe80::/64" }, 188 { "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64", 189 64, "ffff:ffff:ffff:ffff::/64" }, 190 { "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, 191 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" }, 192 }; 193 194 for (auto const &addr: test_addrs) { 195 /* 196 * Convert the input string to an in6_addr + bits, and make 197 * sure the result produces the number of bits we expected. 198 */ 199 200 auto in6 = in6_addr{}; 201 errno = 0; 202 auto bits = inet_net_pton(AF_INET6, addr.input.c_str(), 203 &in6, sizeof(in6)); 204 ATF_REQUIRE(bits != -1); 205 ATF_REQUIRE_EQ(bits, addr.bits); 206 207 /* 208 * Convert the in6_addr back to a string 209 */ 210 211 /* 212 * XXX: Should there be a constant for the size of the result 213 * buffer? For now, use ADDRSTRLEN + 4 ("/128") + 1 (NUL). 214 * 215 * Fill the buffer with 'Z', so we can check the result was 216 * properly terminated. 217 */ 218 auto strbuf = std::vector<char>(INET6_ADDRSTRLEN + 4 + 1, 'Z'); 219 auto ret = inet_net_ntop(AF_INET6, &in6, bits, 220 strbuf.data(), strbuf.size()); 221 ATF_REQUIRE(ret != NULL); 222 ATF_REQUIRE_EQ(ret, strbuf.data()); 223 224 /* Make sure the result was NUL-terminated and find the NUL */ 225 ATF_REQUIRE(strbuf.size() >= 1); 226 auto end = std::ranges::find(strbuf, '\0'); 227 ATF_REQUIRE(end != strbuf.end()); 228 229 /* 230 * Check the result matches what we expect. Use a temporary 231 * string here instead of std::ranges::equal because this 232 * means ATF can print the mismatch. 233 */ 234 auto str = std::string(std::ranges::begin(strbuf), end); 235 ATF_REQUIRE_EQ(str, addr.output); 236 } 237 } 238 239 ATF_TEST_CASE_WITHOUT_HEAD(inet_net_pton_invalid) 240 ATF_TEST_CASE_BODY(inet_net_pton_invalid) 241 { 242 auto ret = int{}; 243 auto addr4 = in_addr{}; 244 auto str4 = "10.0.0.0"s; 245 auto addr6 = in6_addr{}; 246 auto str6 = "2001:db8::"s; 247 248 /* Passing an address which is too short should be an error */ 249 ret = inet_net_pton(AF_INET6, str6.c_str(), &addr6, sizeof(addr6) - 1); 250 ATF_REQUIRE_EQ(ret, -1); 251 252 ret = inet_net_pton(AF_INET, str4.c_str(), &addr4, sizeof(addr4) - 1); 253 ATF_REQUIRE_EQ(ret, -1); 254 255 /* Test some generally invalid addresses. */ 256 auto invalid4 = std::vector<std::string>{ 257 // Prefix length too big 258 "10.0.0.0/33", 259 // Prefix length is negative 260 "10.0.0.0/-1", 261 // Prefix length is not a number 262 "10.0.0.0/foo", 263 // Input is not a network prefix 264 "this is not an IP address", 265 }; 266 267 for (auto const &addr: invalid4) { 268 auto ret = inet_net_pton(AF_INET, addr.c_str(), &addr4, 269 sizeof(addr4)); 270 ATF_REQUIRE_EQ(ret, -1); 271 } 272 273 auto invalid6 = std::vector<std::string>{ 274 // Prefix length too big 275 "2001:db8::/129", 276 // Prefix length is negative 277 "2001:db8::/-1", 278 // Prefix length is not a number 279 "2001:db8::/foo", 280 // Input is not a network prefix 281 "this is not an IP address", 282 }; 283 284 for (auto const &addr: invalid6) { 285 auto ret = inet_net_pton(AF_INET6, addr.c_str(), &addr6, 286 sizeof(addr6)); 287 ATF_REQUIRE_EQ(ret, -1); 288 } 289 } 290 291 ATF_TEST_CASE_WITHOUT_HEAD(inet_net_ntop_invalid) 292 ATF_TEST_CASE_BODY(inet_net_ntop_invalid) 293 { 294 auto addr4 = in_addr{}; 295 auto addr6 = in6_addr{}; 296 auto strbuf = std::vector<char>(INET6_ADDRSTRLEN + 4 + 1); 297 298 /* 299 * Passing a buffer which is too small should not overrun the buffer. 300 * Test this by initialising the buffer to 'Z', and only providing 301 * part of it to the function. 302 */ 303 304 std::ranges::fill(strbuf, 'Z'); 305 auto ret = inet_net_ntop(AF_INET6, &addr6, 128, strbuf.data(), 1); 306 ATF_REQUIRE_EQ(ret, nullptr); 307 ATF_REQUIRE_EQ(strbuf[1], 'Z'); 308 309 std::ranges::fill(strbuf, 'Z'); 310 ret = inet_net_ntop(AF_INET, &addr4, 32, strbuf.data(), 1); 311 ATF_REQUIRE_EQ(ret, nullptr); 312 ATF_REQUIRE_EQ(strbuf[1], 'Z'); 313 314 /* Check that invalid prefix lengths return an error */ 315 316 ret = inet_net_ntop(AF_INET6, &addr6, 129, strbuf.data(), strbuf.size()); 317 ATF_REQUIRE_EQ(ret, nullptr); 318 ret = inet_net_ntop(AF_INET6, &addr6, -1, strbuf.data(), strbuf.size()); 319 ATF_REQUIRE_EQ(ret, nullptr); 320 321 ret = inet_net_ntop(AF_INET, &addr4, 33, strbuf.data(), strbuf.size()); 322 ATF_REQUIRE_EQ(ret, nullptr); 323 ret = inet_net_ntop(AF_INET, &addr4, -1, strbuf.data(), strbuf.size()); 324 ATF_REQUIRE_EQ(ret, nullptr); 325 } 326 327 ATF_INIT_TEST_CASES(tcs) 328 { 329 ATF_ADD_TEST_CASE(tcs, inet_net_inet4); 330 ATF_ADD_TEST_CASE(tcs, inet_net_inet6); 331 ATF_ADD_TEST_CASE(tcs, inet_net_pton_invalid); 332 ATF_ADD_TEST_CASE(tcs, inet_net_ntop_invalid); 333 } 334