Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libc/tests/net/inet_net_test.cc
48249 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2025 Lexi Winter <[email protected]>
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
335