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