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