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)
ATF_TEST_CASE_BODY(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)
ATF_TEST_CASE_BODY(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)
ATF_TEST_CASE_BODY(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)
ATF_TEST_CASE_BODY(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
ATF_INIT_TEST_CASES(tcs)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