Electroneum
Loading...
Searching...
No Matches
net_peerlist.h
Go to the documentation of this file.
1// Copyrights(c) 2017-2021, The Electroneum Project
2// Copyrights(c) 2014-2019, The Monero Project
3//
4// All rights reserved.
5//
6// Redistribution and use in source and binary forms, with or without modification, are
7// permitted provided that the following conditions are met:
8//
9// 1. Redistributions of source code must retain the above copyright notice, this list of
10// conditions and the following disclaimer.
11//
12// 2. Redistributions in binary form must reproduce the above copyright notice, this list
13// of conditions and the following disclaimer in the documentation and/or other
14// materials provided with the distribution.
15//
16// 3. Neither the name of the copyright holder nor the names of its contributors may be
17// used to endorse or promote products derived from this software without specific
18// prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29//
30// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
31
32#pragma once
33
34#include <iosfwd>
35#include <list>
36#include <string>
37#include <vector>
38
39#include <boost/multi_index_container.hpp>
40#include <boost/multi_index/ordered_index.hpp>
41#include <boost/multi_index/identity.hpp>
42#include <boost/multi_index/member.hpp>
43#include <boost/optional/optional.hpp>
44#include <boost/range/adaptor/reversed.hpp>
45
46
47#include "cryptonote_config.h"
48#include "net/enums.h"
49#include "net/local_ip.h"
50#include "p2p_protocol_defs.h"
51#include "syncobj.h"
52
53namespace nodetool
54{
56 {
57 std::vector<peerlist_entry> white;
58 std::vector<peerlist_entry> gray;
59 std::vector<anchor_peerlist_entry> anchor;
60 };
61
63 {
64 public:
66 : m_types{}
67 {}
68
70 static boost::optional<peerlist_storage> open(std::istream& src, const bool new_format);
71
73 static boost::optional<peerlist_storage> open(const std::string& path);
74
77
78 ~peerlist_storage() noexcept;
79
80 peerlist_storage& operator=(peerlist_storage&&) = default;
81 peerlist_storage& operator=(const peerlist_storage&) = delete;
82
84 bool store(std::ostream& dest, const peerlist_types& other) const;
85
87 bool store(const std::string& path, const peerlist_types& other) const;
88
90 peerlist_types take_zone(epee::net_utils::zone zone);
91
92 private:
93 peerlist_types m_types;
94 };
95
96 /************************************************************************/
97 /* */
98 /************************************************************************/
100 {
101 public:
102 bool init(peerlist_types&& peers, bool allow_local_ip);
103 size_t get_white_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_white.size();}
104 size_t get_gray_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_gray.size();}
105 bool merge_peerlist(const std::vector<peerlist_entry>& outer_bs);
106 bool get_peerlist_head(std::vector<peerlist_entry>& bs_head, bool anonymize, uint32_t depth = P2P_DEFAULT_PEERS_IN_HANDSHAKE);
107 void get_peerlist(std::vector<peerlist_entry>& pl_gray, std::vector<peerlist_entry>& pl_white);
108 void get_peerlist(peerlist_types& peers);
109 bool get_white_peer_by_index(peerlist_entry& p, size_t i);
110 bool get_gray_peer_by_index(peerlist_entry& p, size_t i);
111 template<typename F> bool foreach(bool white, const F &f);
112 bool append_with_peer_white(const peerlist_entry& pr);
113 bool append_with_peer_gray(const peerlist_entry& pr);
114 bool append_with_peer_anchor(const anchor_peerlist_entry& ple);
115 bool set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr, uint32_t pruning_seed, uint16_t rpc_port);
120 bool get_and_empty_anchor_peerlist(std::vector<anchor_peerlist_entry>& apl);
123
124 private:
125 struct by_time{};
126 struct by_id{};
127 struct by_addr{};
128
129 struct modify_all_but_id
130 {
131 modify_all_but_id(const peerlist_entry& ple):m_ple(ple){}
132 void operator()(peerlist_entry& e)
133 {
134 e.id = m_ple.id;
135 }
136 private:
137 const peerlist_entry& m_ple;
138 };
139
140 struct modify_all
141 {
142 modify_all(const peerlist_entry& ple):m_ple(ple){}
143 void operator()(peerlist_entry& e)
144 {
145 e = m_ple;
146 }
147 private:
148 const peerlist_entry& m_ple;
149 };
150
151 struct modify_last_seen
152 {
153 modify_last_seen(time_t last_seen):m_last_seen(last_seen){}
154 void operator()(peerlist_entry& e)
155 {
156 e.last_seen = m_last_seen;
157 }
158 private:
159 time_t m_last_seen;
160 };
161
162
163 typedef boost::multi_index_container<
165 boost::multi_index::indexed_by<
166 // access by peerlist_entry::net_adress
167 boost::multi_index::ordered_unique<boost::multi_index::tag<by_addr>, boost::multi_index::member<peerlist_entry,epee::net_utils::network_address,&peerlist_entry::adr> >,
168 // sort by peerlist_entry::last_seen<
169 boost::multi_index::ordered_non_unique<boost::multi_index::tag<by_time>, boost::multi_index::member<peerlist_entry,int64_t,&peerlist_entry::last_seen> >
170 >
171 > peers_indexed;
172
173 typedef boost::multi_index_container<
175 boost::multi_index::indexed_by<
176 // access by anchor_peerlist_entry::net_adress
177 boost::multi_index::ordered_unique<boost::multi_index::tag<by_addr>, boost::multi_index::member<anchor_peerlist_entry,epee::net_utils::network_address,&anchor_peerlist_entry::adr> >,
178 // sort by anchor_peerlist_entry::first_seen
179 boost::multi_index::ordered_non_unique<boost::multi_index::tag<by_time>, boost::multi_index::member<anchor_peerlist_entry,int64_t,&anchor_peerlist_entry::first_seen> >
180 >
181 > anchor_peers_indexed;
182
183 private:
184 void trim_white_peerlist();
185 void trim_gray_peerlist();
186
187 friend class boost::serialization::access;
188 epee::critical_section m_peerlist_lock;
189 std::string m_config_folder;
190 bool m_allow_local_ip;
191
192
193 peers_indexed m_peers_gray;
194 peers_indexed m_peers_white;
195 anchor_peers_indexed m_peers_anchor;
196 };
197 //--------------------------------------------------------------------------------------------------
198 inline void peerlist_manager::trim_gray_peerlist()
199 {
200 while(m_peers_gray.size() > P2P_LOCAL_GRAY_PEERLIST_LIMIT)
201 {
202 peers_indexed::index<by_time>::type& sorted_index=m_peers_gray.get<by_time>();
203 sorted_index.erase(sorted_index.begin());
204 }
205 }
206 //--------------------------------------------------------------------------------------------------
207 inline void peerlist_manager::trim_white_peerlist()
208 {
209 while(m_peers_white.size() > P2P_LOCAL_WHITE_PEERLIST_LIMIT)
210 {
211 peers_indexed::index<by_time>::type& sorted_index=m_peers_white.get<by_time>();
212 sorted_index.erase(sorted_index.begin());
213 }
214 }
215 //--------------------------------------------------------------------------------------------------
216 inline
217 bool peerlist_manager::merge_peerlist(const std::vector<peerlist_entry>& outer_bs)
218 {
219 CRITICAL_REGION_LOCAL(m_peerlist_lock);
220 for(const peerlist_entry& be: outer_bs)
221 {
223 }
224 // delete extra elements
225 trim_gray_peerlist();
226 return true;
227 }
228 //--------------------------------------------------------------------------------------------------
229 inline
231 {
232 CRITICAL_REGION_LOCAL(m_peerlist_lock);
233 if(i >= m_peers_white.size())
234 return false;
235
236 peers_indexed::index<by_time>::type& by_time_index = m_peers_white.get<by_time>();
237 p = *epee::misc_utils::move_it_backward(--by_time_index.end(), i);
238 return true;
239 }
240 //--------------------------------------------------------------------------------------------------
241 inline
243 {
244 CRITICAL_REGION_LOCAL(m_peerlist_lock);
245 if(i >= m_peers_gray.size())
246 return false;
247
248 peers_indexed::index<by_time>::type& by_time_index = m_peers_gray.get<by_time>();
249 p = *epee::misc_utils::move_it_backward(--by_time_index.end(), i);
250 return true;
251 }
252 //--------------------------------------------------------------------------------------------------
253 inline
255 {
256 //never allow loopback ip
257 if(address.is_loopback())
258 return false;
259
260 if(!m_allow_local_ip && address.is_local())
261 return false;
262
263 return true;
264 }
265 //--------------------------------------------------------------------------------------------------
266 inline
267 bool peerlist_manager::get_peerlist_head(std::vector<peerlist_entry>& bs_head, bool anonymize, uint32_t depth)
268 {
269 CRITICAL_REGION_LOCAL(m_peerlist_lock);
270 peers_indexed::index<by_time>::type& by_time_index=m_peers_white.get<by_time>();
271 uint32_t cnt = 0;
272
273 // picks a random set of peers within the first 120%, rather than a set of the first 100%.
274 // The intent is that if someone asks twice, they can't easily tell:
275 // - this address was not in the first list, but is in the second, so the only way this can be
276 // is if its last_seen was recently reset, so this means the target node recently had a new
277 // connection to that address
278 // - this address was in the first list, and not in the second, which means either the address
279 // was moved to the gray list (if it's not accessibe, which the attacker can check if
280 // the address accepts incoming connections) or it was the oldest to still fit in the 250 items,
281 // so its last_seen is old.
282 const uint32_t pick_depth = anonymize ? depth + depth / 5 : depth;
283 bs_head.reserve(pick_depth);
284 for(const peers_indexed::value_type& vl: boost::adaptors::reverse(by_time_index))
285 {
286 if(cnt++ >= pick_depth)
287 break;
288
289 bs_head.push_back(vl);
290 }
291
292 if (anonymize)
293 {
294 std::random_shuffle(bs_head.begin(), bs_head.end());
295 if (bs_head.size() > depth)
296 bs_head.resize(depth);
297 for (auto &e: bs_head)
298 e.last_seen = 0;
299 }
300
301 return true;
302 }
303 //--------------------------------------------------------------------------------------------------
304 template<typename F> inline
305 bool peerlist_manager::foreach(bool white, const F &f)
306 {
307 CRITICAL_REGION_LOCAL(m_peerlist_lock);
308 peers_indexed::index<by_time>::type& by_time_index = white ? m_peers_white.get<by_time>() : m_peers_gray.get<by_time>();
309 for(const peers_indexed::value_type& vl: boost::adaptors::reverse(by_time_index))
310 if (!f(vl))
311 return false;
312 return true;
313 }
314 //--------------------------------------------------------------------------------------------------
315 inline
317 {
318 TRY_ENTRY();
319 CRITICAL_REGION_LOCAL(m_peerlist_lock);
320 //find in white list
321 peerlist_entry ple;
322 ple.adr = addr;
323 ple.id = peer;
324 ple.last_seen = time(NULL);
325 ple.pruning_seed = pruning_seed;
326 ple.rpc_port = rpc_port;
327 return append_with_peer_white(ple);
328 CATCH_ENTRY_L0("peerlist_manager::set_peer_just_seen()", false);
329 }
330 //--------------------------------------------------------------------------------------------------
331 inline
333 {
334 TRY_ENTRY();
335 if(!is_host_allowed(ple.adr))
336 return true;
337
338 CRITICAL_REGION_LOCAL(m_peerlist_lock);
339 //find in white list
340 auto by_addr_it_wt = m_peers_white.get<by_addr>().find(ple.adr);
341 if(by_addr_it_wt == m_peers_white.get<by_addr>().end())
342 {
343 //put new record into white list
344 m_peers_white.insert(ple);
345 trim_white_peerlist();
346 }else
347 {
348 //update record in white list
349 peerlist_entry new_ple = ple;
350 if (by_addr_it_wt->pruning_seed && ple.pruning_seed == 0) // guard against older nodes not passing pruning info around
351 new_ple.pruning_seed = by_addr_it_wt->pruning_seed;
352 if (by_addr_it_wt->rpc_port && ple.rpc_port == 0) // guard against older nodes not passing RPC port around
353 new_ple.rpc_port = by_addr_it_wt->rpc_port;
354 new_ple.last_seen = by_addr_it_wt->last_seen; // do not overwrite the last seen timestamp, incoming peer list are untrusted
355 m_peers_white.replace(by_addr_it_wt, new_ple);
356 }
357 //remove from gray list, if need
358 auto by_addr_it_gr = m_peers_gray.get<by_addr>().find(ple.adr);
359 if(by_addr_it_gr != m_peers_gray.get<by_addr>().end())
360 {
361 m_peers_gray.erase(by_addr_it_gr);
362 }
363 return true;
364 CATCH_ENTRY_L0("peerlist_manager::append_with_peer_white()", false);
365 }
366 //--------------------------------------------------------------------------------------------------
367 inline
369 {
370 TRY_ENTRY();
371 if(!is_host_allowed(ple.adr))
372 return true;
373
374 CRITICAL_REGION_LOCAL(m_peerlist_lock);
375 //find in white list
376 auto by_addr_it_wt = m_peers_white.get<by_addr>().find(ple.adr);
377 if(by_addr_it_wt != m_peers_white.get<by_addr>().end())
378 return true;
379
380 //update gray list
381 auto by_addr_it_gr = m_peers_gray.get<by_addr>().find(ple.adr);
382 if(by_addr_it_gr == m_peers_gray.get<by_addr>().end())
383 {
384 //put new record into white list
385 m_peers_gray.insert(ple);
386 trim_gray_peerlist();
387 }else
388 {
389 //update record in gray list
390 peerlist_entry new_ple = ple;
391 if (by_addr_it_gr->pruning_seed && ple.pruning_seed == 0) // guard against older nodes not passing pruning info around
392 new_ple.pruning_seed = by_addr_it_gr->pruning_seed;
393 if (by_addr_it_gr->rpc_port && ple.rpc_port == 0) // guard against older nodes not passing RPC port around
394 new_ple.rpc_port = by_addr_it_gr->rpc_port;
395 new_ple.last_seen = by_addr_it_gr->last_seen; // do not overwrite the last seen timestamp, incoming peer list are untrusted
396 m_peers_gray.replace(by_addr_it_gr, new_ple);
397 }
398 return true;
399 CATCH_ENTRY_L0("peerlist_manager::append_with_peer_gray()", false);
400 }
401 //--------------------------------------------------------------------------------------------------
402 inline
404 {
405 TRY_ENTRY();
406
407 CRITICAL_REGION_LOCAL(m_peerlist_lock);
408
409 auto by_addr_it_anchor = m_peers_anchor.get<by_addr>().find(ple.adr);
410
411 if(by_addr_it_anchor == m_peers_anchor.get<by_addr>().end()) {
412 m_peers_anchor.insert(ple);
413 }
414
415 return true;
416
417 CATCH_ENTRY_L0("peerlist_manager::append_with_peer_anchor()", false);
418 }
419 //--------------------------------------------------------------------------------------------------
420 inline
422 {
423 TRY_ENTRY();
424
425 CRITICAL_REGION_LOCAL(m_peerlist_lock);
426
427 if (m_peers_gray.empty()) {
428 return false;
429 }
430
431 size_t random_index = crypto::rand_idx(m_peers_gray.size());
432
433 peers_indexed::index<by_time>::type& by_time_index = m_peers_gray.get<by_time>();
434 pe = *epee::misc_utils::move_it_backward(--by_time_index.end(), random_index);
435
436 return true;
437
438 CATCH_ENTRY_L0("peerlist_manager::get_random_gray_peer()", false);
439 }
440 //--------------------------------------------------------------------------------------------------
441 inline
443 {
444 TRY_ENTRY();
445
446 CRITICAL_REGION_LOCAL(m_peerlist_lock);
447
448 peers_indexed::index_iterator<by_addr>::type iterator = m_peers_white.get<by_addr>().find(pe.adr);
449
450 if (iterator != m_peers_white.get<by_addr>().end()) {
451 m_peers_white.erase(iterator);
452 }
453
454 return true;
455
456 CATCH_ENTRY_L0("peerlist_manager::remove_from_peer_white()", false);
457 }
458 //--------------------------------------------------------------------------------------------------
459 inline
461 {
462 TRY_ENTRY();
463
464 CRITICAL_REGION_LOCAL(m_peerlist_lock);
465
466 peers_indexed::index_iterator<by_addr>::type iterator = m_peers_gray.get<by_addr>().find(pe.adr);
467
468 if (iterator != m_peers_gray.get<by_addr>().end()) {
469 m_peers_gray.erase(iterator);
470 }
471
472 return true;
473
474 CATCH_ENTRY_L0("peerlist_manager::remove_from_peer_gray()", false);
475 }
476 //--------------------------------------------------------------------------------------------------
477 inline
478 bool peerlist_manager::get_and_empty_anchor_peerlist(std::vector<anchor_peerlist_entry>& apl)
479 {
480 TRY_ENTRY();
481
482 CRITICAL_REGION_LOCAL(m_peerlist_lock);
483
484 auto begin = m_peers_anchor.get<by_time>().begin();
485 auto end = m_peers_anchor.get<by_time>().end();
486
487 std::for_each(begin, end, [&apl](const anchor_peerlist_entry &a) {
488 apl.push_back(a);
489 });
490
491 m_peers_anchor.get<by_time>().clear();
492
493 return true;
494
495 CATCH_ENTRY_L0("peerlist_manager::get_and_empty_anchor_peerlist()", false);
496 }
497 //--------------------------------------------------------------------------------------------------
498 inline
500 {
501 TRY_ENTRY();
502
503 CRITICAL_REGION_LOCAL(m_peerlist_lock);
504
505 anchor_peers_indexed::index_iterator<by_addr>::type iterator = m_peers_anchor.get<by_addr>().find(addr);
506
507 if (iterator != m_peers_anchor.get<by_addr>().end()) {
508 m_peers_anchor.erase(iterator);
509 }
510
511 return true;
512
513 CATCH_ENTRY_L0("peerlist_manager::remove_from_peer_anchor()", false);
514 }
515 //--------------------------------------------------------------------------------------------------
516}
517
time_t time
bool get_gray_peer_by_index(peerlist_entry &p, size_t i)
bool foreach(bool white, const F &f)
bool append_with_peer_anchor(const anchor_peerlist_entry &ple)
bool get_and_empty_anchor_peerlist(std::vector< anchor_peerlist_entry > &apl)
bool get_random_gray_peer(peerlist_entry &pe)
bool remove_from_peer_white(const peerlist_entry &pe)
bool append_with_peer_white(const peerlist_entry &pr)
bool init(peerlist_types &&peers, bool allow_local_ip)
bool append_with_peer_gray(const peerlist_entry &pr)
bool merge_peerlist(const std::vector< peerlist_entry > &outer_bs)
bool remove_from_peer_gray(const peerlist_entry &pe)
bool remove_from_peer_anchor(const epee::net_utils::network_address &addr)
bool get_peerlist_head(std::vector< peerlist_entry > &bs_head, bool anonymize, uint32_t depth=P2P_DEFAULT_PEERS_IN_HANDSHAKE)
bool get_white_peer_by_index(peerlist_entry &p, size_t i)
bool set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address &addr, uint32_t pruning_seed, uint16_t rpc_port)
bool is_host_allowed(const epee::net_utils::network_address &address)
bool set_peer_unreachable(const peerlist_entry &pr)
static boost::optional< peerlist_storage > open(std::istream &src, const bool new_format)
peerlist_storage(peerlist_storage &&)=default
bool store(std::ostream &dest, const peerlist_types &other) const
Save peers from this and other in stream dest.
peerlist_storage(const peerlist_storage &)=delete
peerlist_types take_zone(epee::net_utils::zone zone)
#define P2P_LOCAL_GRAY_PEERLIST_LIMIT
#define P2P_LOCAL_WHITE_PEERLIST_LIMIT
#define P2P_DEFAULT_PEERS_IN_HANDSHAKE
#define F(s)
#define CATCH_ENTRY_L0(lacation, return_val)
#define TRY_ENTRY()
std::enable_if< std::is_unsigned< T >::value, T >::type rand_idx(T sz)
Definition crypto.h:244
t_iterator move_it_backward(t_iterator it, size_t count)
uint64_t peerid_type
anchor_peerlist_entry_base< epee::net_utils::network_address > anchor_peerlist_entry
peerlist_entry_base< epee::net_utils::network_address > peerlist_entry
STL namespace.
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition pointer.h:1124
CXA_THROW_INFO_T void(* dest)(void *))
unsigned short uint16_t
Definition stdint.h:125
unsigned int uint32_t
Definition stdint.h:126
AddressType adr
uint32_t pruning_seed
AddressType adr
uint16_t rpc_port
peerid_type id
int64_t last_seen
std::vector< anchor_peerlist_entry > anchor
std::vector< peerlist_entry > gray
std::vector< peerlist_entry > white
#define CRITICAL_REGION_LOCAL(x)
Definition syncobj.h:228
const char * address
Definition multisig.cpp:37