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