rippled
Loading...
Searching...
No Matches
IPEndpoint_test.cpp
1// MODULES: ../impl/IPEndpoint.cpp ../impl/IPAddressV4.cpp
2// ../impl/IPAddressV6.cpp
3
4#include <test/beast/IPEndpointCommon.h>
5
6#include <xrpl/basics/random.h>
7#include <xrpl/beast/net/IPEndpoint.h>
8#include <xrpl/beast/unit_test.h>
9
10#include <boost/algorithm/string.hpp>
11#include <boost/asio/ip/address.hpp>
12#include <boost/predef.h>
13
14namespace beast {
15namespace IP {
16
17//------------------------------------------------------------------------------
18
20{
21public:
22 void
24 std::string const& s,
25 std::uint32_t value,
26 std::string const& normal = "")
27 {
28 boost::system::error_code ec;
29 Address const result{boost::asio::ip::make_address(s, ec)};
30 if (!BEAST_EXPECTS(!ec, ec.message()))
31 return;
32 if (!BEAST_EXPECTS(result.is_v4(), s + " not v4"))
33 return;
34 if (!BEAST_EXPECTS(
35 result.to_v4().to_uint() == value, s + " value mismatch"))
36 return;
37 BEAST_EXPECTS(
38 result.to_string() == (normal.empty() ? s : normal),
39 s + " as string");
40 }
41
42 void
44 {
45 boost::system::error_code ec;
46 auto a = boost::asio::ip::make_address(s, ec);
47 BEAST_EXPECTS(ec, s + " parses as " + a.to_string());
48 }
49
50 void
52 {
53 testcase("AddressV4");
54
55 BEAST_EXPECT(AddressV4{}.to_uint() == 0);
56 BEAST_EXPECT(is_unspecified(AddressV4{}));
57 BEAST_EXPECT(AddressV4{0x01020304}.to_uint() == 0x01020304);
58
59 {
60 AddressV4::bytes_type d = {{1, 2, 3, 4}};
61 BEAST_EXPECT(AddressV4{d}.to_uint() == 0x01020304);
62
64 }
65
66 AddressV4 const v1{1};
67 BEAST_EXPECT(AddressV4{v1}.to_uint() == 1);
68
69 {
70 AddressV4 v;
71 v = v1;
72 BEAST_EXPECT(v.to_uint() == v1.to_uint());
73 }
74
75 {
76 AddressV4 v;
77 auto d = v.to_bytes();
78 d[0] = 1;
79 d[1] = 2;
80 d[2] = 3;
81 d[3] = 4;
82 v = AddressV4{d};
83 BEAST_EXPECT(v.to_uint() == 0x01020304);
84 }
85
86 BEAST_EXPECT(AddressV4(0x01020304).to_string() == "1.2.3.4");
87
88 shouldParseAddrV4("1.2.3.4", 0x01020304);
89 shouldParseAddrV4("255.255.255.255", 0xffffffff);
90 shouldParseAddrV4("0.0.0.0", 0);
91
92 failParseAddr(".");
93 failParseAddr("..");
94 failParseAddr("...");
95 failParseAddr("....");
96#if BOOST_OS_WINDOWS
97 // WINDOWS bug in asio - I don't think these should parse
98 // at all, and in-fact they do not on mac/linux
99 shouldParseAddrV4("1", 0x00000001, "0.0.0.1");
100 shouldParseAddrV4("1.2", 0x01000002, "1.0.0.2");
101 shouldParseAddrV4("1.2.3", 0x01020003, "1.2.0.3");
102#else
103 failParseAddr("1");
104 failParseAddr("1.2");
105 failParseAddr("1.2.3");
106#endif
107 failParseAddr("1.");
108 failParseAddr("1.2.");
109 failParseAddr("1.2.3.");
110 failParseAddr("256.0.0.0");
111 failParseAddr("-1.2.3.4");
112 }
113
114 void
116 {
117 testcase("AddressV4::Bytes");
118
119 AddressV4::bytes_type d1 = {{10, 0, 0, 1}};
120 AddressV4 v4{d1};
121 BEAST_EXPECT(v4.to_bytes()[0] == 10);
122 BEAST_EXPECT(v4.to_bytes()[1] == 0);
123 BEAST_EXPECT(v4.to_bytes()[2] == 0);
124 BEAST_EXPECT(v4.to_bytes()[3] == 1);
125
126 BEAST_EXPECT((~((0xff) << 16)) == 0xff00ffff);
127
128 auto d2 = v4.to_bytes();
129 d2[1] = 10;
130 v4 = AddressV4{d2};
131 BEAST_EXPECT(v4.to_bytes()[0] == 10);
132 BEAST_EXPECT(v4.to_bytes()[1] == 10);
133 BEAST_EXPECT(v4.to_bytes()[2] == 0);
134 BEAST_EXPECT(v4.to_bytes()[3] == 1);
135 }
136
137 //--------------------------------------------------------------------------
138
139 void
141 {
142 testcase("Address");
143
144 boost::system::error_code ec;
145 Address result{boost::asio::ip::make_address("1.2.3.4", ec)};
146 AddressV4::bytes_type d = {{1, 2, 3, 4}};
147 BEAST_EXPECT(!ec);
148 BEAST_EXPECT(result.is_v4() && result.to_v4() == AddressV4{d});
149 }
150
151 //--------------------------------------------------------------------------
152
153 void
155 std::string const& s,
156 AddressV4::bytes_type const& value,
158 std::string const& normal = "")
159 {
160 auto const result = Endpoint::from_string_checked(s);
161 if (!BEAST_EXPECT(result))
162 return;
163 if (!BEAST_EXPECT(result->address().is_v4()))
164 return;
165 if (!BEAST_EXPECT(result->address().to_v4() == AddressV4{value}))
166 return;
167
168 BEAST_EXPECT(result->port() == p);
169 BEAST_EXPECT(to_string(*result) == (normal.empty() ? s : normal));
170 }
171
172 void
174 std::string const& s,
175 AddressV6::bytes_type const& value,
177 std::string const& normal = "")
178 {
179 auto result = Endpoint::from_string_checked(s);
180 if (!BEAST_EXPECT(result))
181 return;
182 if (!BEAST_EXPECT(result->address().is_v6()))
183 return;
184 if (!BEAST_EXPECT(result->address().to_v6() == AddressV6{value}))
185 return;
186
187 BEAST_EXPECT(result->port() == p);
188 BEAST_EXPECT(to_string(*result) == (normal.empty() ? s : normal));
189 }
190
191 void
193 {
194 auto a1 = Endpoint::from_string(s);
195 BEAST_EXPECTS(is_unspecified(a1), s + " parses as " + a1.to_string());
196
197 auto a2 = Endpoint::from_string(s);
198 BEAST_EXPECTS(is_unspecified(a2), s + " parses as " + a2.to_string());
199
200 boost::replace_last(s, ":", " ");
201 auto a3 = Endpoint::from_string(s);
202 BEAST_EXPECTS(is_unspecified(a3), s + " parses as " + a3.to_string());
203 }
204
205 void
207 {
208 testcase("Endpoint");
209
210 shouldParseEPV4("1.2.3.4", {{1, 2, 3, 4}}, 0);
211 shouldParseEPV4("1.2.3.4:5", {{1, 2, 3, 4}}, 5);
212 shouldParseEPV4("1.2.3.4 5", {{1, 2, 3, 4}}, 5, "1.2.3.4:5");
213 // leading, trailing space
214 shouldParseEPV4(" 1.2.3.4:5", {{1, 2, 3, 4}}, 5, "1.2.3.4:5");
215 shouldParseEPV4("1.2.3.4:5 ", {{1, 2, 3, 4}}, 5, "1.2.3.4:5");
216 shouldParseEPV4("1.2.3.4 ", {{1, 2, 3, 4}}, 0, "1.2.3.4");
217 shouldParseEPV4(" 1.2.3.4", {{1, 2, 3, 4}}, 0, "1.2.3.4");
219 "2001:db8:a0b:12f0::1",
220 {{32, 01, 13, 184, 10, 11, 18, 240, 0, 0, 0, 0, 0, 0, 0, 1}},
221 0);
223 "[2001:db8:a0b:12f0::1]:8",
224 {{32, 01, 13, 184, 10, 11, 18, 240, 0, 0, 0, 0, 0, 0, 0, 1}},
225 8);
227 "[2001:2002:2003:2004:2005:2006:2007:2008]:65535",
228 {{32, 1, 32, 2, 32, 3, 32, 4, 32, 5, 32, 6, 32, 7, 32, 8}},
229 65535);
231 "2001:2002:2003:2004:2005:2006:2007:2008 65535",
232 {{32, 1, 32, 2, 32, 3, 32, 4, 32, 5, 32, 6, 32, 7, 32, 8}},
233 65535,
234 "[2001:2002:2003:2004:2005:2006:2007:2008]:65535");
235
236 Endpoint ep;
237
238 AddressV4::bytes_type d = {{127, 0, 0, 1}};
239 ep = Endpoint(AddressV4{d}, 80);
240 BEAST_EXPECT(!is_unspecified(ep));
241 BEAST_EXPECT(!is_public(ep));
242 BEAST_EXPECT(is_private(ep));
243 BEAST_EXPECT(!is_multicast(ep));
244 BEAST_EXPECT(is_loopback(ep));
245 BEAST_EXPECT(to_string(ep) == "127.0.0.1:80");
246 // same address as v4 mapped in ipv6
247 ep = Endpoint(
248 boost::asio::ip::make_address_v6(
249 boost::asio::ip::v4_mapped, AddressV4{d}),
250 80);
251 BEAST_EXPECT(!is_unspecified(ep));
252 BEAST_EXPECT(!is_public(ep));
253 BEAST_EXPECT(is_private(ep));
254 BEAST_EXPECT(!is_multicast(ep));
255 BEAST_EXPECT(!is_loopback(ep)); // mapped loopback is not a loopback
256 BEAST_EXPECTS(to_string(ep) == "[::ffff:127.0.0.1]:80", to_string(ep));
257
258 d = {{10, 0, 0, 1}};
259 ep = Endpoint(AddressV4{d});
260 BEAST_EXPECT(get_class(ep.to_v4()) == 'A');
261 BEAST_EXPECT(!is_unspecified(ep));
262 BEAST_EXPECT(!is_public(ep));
263 BEAST_EXPECT(is_private(ep));
264 BEAST_EXPECT(!is_multicast(ep));
265 BEAST_EXPECT(!is_loopback(ep));
266 BEAST_EXPECT(to_string(ep) == "10.0.0.1");
267 // same address as v4 mapped in ipv6
268 ep = Endpoint(boost::asio::ip::make_address_v6(
269 boost::asio::ip::v4_mapped, AddressV4{d}));
270 BEAST_EXPECT(
271 get_class(boost::asio::ip::make_address_v4(
272 boost::asio::ip::v4_mapped, ep.to_v6())) == 'A');
273 BEAST_EXPECT(!is_unspecified(ep));
274 BEAST_EXPECT(!is_public(ep));
275 BEAST_EXPECT(is_private(ep));
276 BEAST_EXPECT(!is_multicast(ep));
277 BEAST_EXPECT(!is_loopback(ep));
278 BEAST_EXPECTS(to_string(ep) == "::ffff:10.0.0.1", to_string(ep));
279
280 d = {{166, 78, 151, 147}};
281 ep = Endpoint(AddressV4{d});
282 BEAST_EXPECT(!is_unspecified(ep));
283 BEAST_EXPECT(is_public(ep));
284 BEAST_EXPECT(!is_private(ep));
285 BEAST_EXPECT(!is_multicast(ep));
286 BEAST_EXPECT(!is_loopback(ep));
287 BEAST_EXPECT(to_string(ep) == "166.78.151.147");
288 // same address as v4 mapped in ipv6
289 ep = Endpoint(boost::asio::ip::make_address_v6(
290 boost::asio::ip::v4_mapped, AddressV4{d}));
291 BEAST_EXPECT(!is_unspecified(ep));
292 BEAST_EXPECT(is_public(ep));
293 BEAST_EXPECT(!is_private(ep));
294 BEAST_EXPECT(!is_multicast(ep));
295 BEAST_EXPECT(!is_loopback(ep));
296 BEAST_EXPECTS(to_string(ep) == "::ffff:166.78.151.147", to_string(ep));
297
298 // a private IPv6
299 AddressV6::bytes_type d2 = {
300 {253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}};
301 ep = Endpoint(AddressV6{d2});
302 BEAST_EXPECT(!is_unspecified(ep));
303 BEAST_EXPECT(!is_public(ep));
304 BEAST_EXPECT(is_private(ep));
305 BEAST_EXPECT(!is_multicast(ep));
306 BEAST_EXPECT(!is_loopback(ep));
307 BEAST_EXPECTS(to_string(ep) == "fd00::1", to_string(ep));
308
309 {
310 ep = Endpoint::from_string("192.0.2.112");
311 BEAST_EXPECT(!is_unspecified(ep));
312 BEAST_EXPECT(ep == Endpoint::from_string("192.0.2.112"));
313
314 auto const ep1 = Endpoint::from_string("192.0.2.112:2016");
315 BEAST_EXPECT(!is_unspecified(ep1));
316 BEAST_EXPECT(ep.address() == ep1.address());
317 BEAST_EXPECT(ep1.port() == 2016);
318
319 auto const ep2 = Endpoint::from_string("192.0.2.112:2016");
320 BEAST_EXPECT(!is_unspecified(ep2));
321 BEAST_EXPECT(ep.address() == ep2.address());
322 BEAST_EXPECT(ep2.port() == 2016);
323 BEAST_EXPECT(ep1 == ep2);
324
325 auto const ep3 = Endpoint::from_string("192.0.2.112 2016");
326 BEAST_EXPECT(!is_unspecified(ep3));
327 BEAST_EXPECT(ep.address() == ep3.address());
328 BEAST_EXPECT(ep3.port() == 2016);
329 BEAST_EXPECT(ep2 == ep3);
330
331 auto const ep4 = Endpoint::from_string("192.0.2.112 2016");
332 BEAST_EXPECT(!is_unspecified(ep4));
333 BEAST_EXPECT(ep.address() == ep4.address());
334 BEAST_EXPECT(ep4.port() == 2016);
335 BEAST_EXPECT(ep3 == ep4);
336
337 BEAST_EXPECT(to_string(ep1) == to_string(ep2));
338 BEAST_EXPECT(to_string(ep1) == to_string(ep3));
339 BEAST_EXPECT(to_string(ep1) == to_string(ep4));
340 }
341
342 {
343 ep = Endpoint::from_string("[::]:2017");
344 BEAST_EXPECT(is_unspecified(ep));
345 BEAST_EXPECT(ep.port() == 2017);
346 BEAST_EXPECT(ep.address() == AddressV6{});
347 }
348
349 // Failures:
350 failParseEP("192.0.2.112:port");
351 failParseEP("ip:port");
352 failParseEP("");
353 failParseEP("1.2.3.256");
354
355#if BOOST_OS_WINDOWS
356 // windows asio bugs...false positives
357 shouldParseEPV4("255", {{ 0, 0, 0, 255 }}, 0, "0.0.0.255");
358 shouldParseEPV4("512", {{ 0, 0, 2, 0 }}, 0, "0.0.2.0");
359 shouldParseEPV4("1.2.3:80", {{ 1, 2, 0, 3 }}, 80, "1.2.0.3:80");
360#else
361 failParseEP("255");
362 failParseEP("512");
363 failParseEP("1.2.3:80");
364#endif
365
366 failParseEP("1.2.3.4:65536");
367 failParseEP("1.2.3.4:89119");
368 failParseEP("1.2.3:89119");
369 failParseEP("[::1]:89119");
370 failParseEP("[::az]:1");
371 failParseEP("[1234:5678:90ab:cdef:1234:5678:90ab:cdef:1111]:1");
372 failParseEP("[1234:5678:90ab:cdef:1234:5678:90ab:cdef:1111]:12345");
373 failParseEP("abcdef:12345");
374 failParseEP("[abcdef]:12345");
375 failParseEP("foo.org 12345");
376
377 // test with hashed container
379 constexpr auto items{100};
380 float max_lf{0};
381 for (auto i = 0; i < items; ++i)
382 {
383 eps.insert(randomEP(ripple::rand_int(0, 1) == 1));
384 max_lf = std::max(max_lf, eps.load_factor());
385 }
386 BEAST_EXPECT(eps.bucket_count() >= items);
387 BEAST_EXPECT(max_lf > 0.90);
388 }
389
390 //--------------------------------------------------------------------------
391
392 template <typename T>
393 bool
394 parse(std::string const& text, T& t)
395 {
396 std::istringstream stream{text};
397 stream >> t;
398 return !stream.fail();
399 }
400
401 template <typename T>
402 void
403 shouldPass(std::string const& text, std::string const& normal = "")
404 {
405 using namespace std::literals;
406 T t;
407 BEAST_EXPECT(parse(text, t));
408 BEAST_EXPECTS(
409 to_string(t) == (normal.empty() ? text : normal),
410 "string mismatch for "s + text);
411 }
412
413 template <typename T>
414 void
416 {
417 T t;
418 unexpected(parse(text, t), text + " should not parse");
419 }
420
421 template <typename T>
422 void
423 testParse(char const* name)
424 {
425 testcase(name);
426
427 shouldPass<T>("0.0.0.0");
428 shouldPass<T>("192.168.0.1");
429 shouldPass<T>("168.127.149.132");
430 shouldPass<T>("168.127.149.132:80");
431 shouldPass<T>("168.127.149.132:54321");
432 shouldPass<T>("2001:db8:a0b:12f0::1");
433 shouldPass<T>("[2001:db8:a0b:12f0::1]:8");
434 shouldPass<T>("2001:db8:a0b:12f0::1 8", "[2001:db8:a0b:12f0::1]:8");
435 shouldPass<T>("[::1]:8");
436 shouldPass<T>("[2001:2002:2003:2004:2005:2006:2007:2008]:65535");
437
438 shouldFail<T>("1.2.3.256");
439 shouldFail<T>("");
440#if BOOST_OS_WINDOWS
441 // windows asio bugs...false positives
442 shouldPass<T>("512", "0.0.2.0");
443 shouldPass<T>("255", "0.0.0.255");
444 shouldPass<T>("1.2.3:80", "1.2.0.3:80");
445#else
446 shouldFail<T>("512");
447 shouldFail<T>("255");
448 shouldFail<T>("1.2.3:80");
449#endif
450 shouldFail<T>("1.2.3:65536");
451 shouldFail<T>("1.2.3:72131");
452 shouldFail<T>("[::1]:89119");
453 shouldFail<T>("[::az]:1");
454 shouldFail<T>("[1234:5678:90ab:cdef:1234:5678:90ab:cdef:1111]:1");
455 shouldFail<T>("[1234:5678:90ab:cdef:1234:5678:90ab:cdef:1111]:12345");
456 }
457
458 void
459 run() override
460 {
463 testAddress();
464 testEndpoint();
465 testParse<Endpoint>("Parse Endpoint");
466 }
467};
468
469BEAST_DEFINE_TESTSUITE(IPEndpoint, beast, beast);
470
471} // namespace IP
472} // namespace beast
T bucket_count(T... args)
A version-independent IP address and port combination.
Definition IPEndpoint.h:19
static std::optional< Endpoint > from_string_checked(std::string const &s)
Create an Endpoint from a string.
static Endpoint from_string(std::string const &s)
void run() override
Runs the suite.
void shouldPass(std::string const &text, std::string const &normal="")
void shouldParseEPV4(std::string const &s, AddressV4::bytes_type const &value, std::uint16_t p, std::string const &normal="")
bool parse(std::string const &text, T &t)
void shouldParseAddrV4(std::string const &s, std::uint32_t value, std::string const &normal="")
void failParseAddr(std::string const &s)
void shouldFail(std::string const &text)
void testParse(char const *name)
void failParseEP(std::string s)
void shouldParseEPV6(std::string const &s, AddressV6::bytes_type const &value, std::uint16_t p, std::string const &normal="")
A testsuite class.
Definition suite.h:52
bool unexpected(Condition shouldBeFalse, String const &reason)
Definition suite.h:496
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
T insert(T... args)
T load_factor(T... args)
T max(T... args)
bool is_multicast(Address const &addr)
Returns true if the address is a multicast address.
Definition IPAddress.h:45
bool is_loopback(Address const &addr)
Returns true if this is a loopback address.
Definition IPAddress.h:31
char get_class(AddressV4 const &address)
Returns the address class for the given address.
boost::asio::ip::address_v6 AddressV6
Definition IPAddressV6.h:11
bool is_public(Address const &addr)
Returns true if the address is a public routable address.
Definition IPAddress.h:59
Endpoint randomEP(bool v4=true)
bool is_unspecified(Address const &addr)
Returns true if the address is unspecified.
Definition IPAddress.h:38
boost::asio::ip::address_v4 AddressV4
Definition IPAddressV4.h:11
boost::asio::ip::address Address
Definition IPAddress.h:20
bool is_private(Address const &addr)
Returns true if the address is a private unroutable address.
Definition IPAddress.h:52
std::string to_string(Address const &addr)
Returns the address represented as a string.
Definition IPAddress.h:24
std::enable_if_t< std::is_integral< Integral >::value, Integral > rand_int()