Electroneum
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 
53 namespace 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 
76  peerlist_storage(const peerlist_storage&) = delete;
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);
116  bool set_peer_unreachable(const peerlist_entry& pr);
117  bool is_host_allowed(const epee::net_utils::network_address &address);
118  bool get_random_gray_peer(peerlist_entry& pe);
119  bool remove_from_peer_gray(const peerlist_entry& pe);
120  bool get_and_empty_anchor_peerlist(std::vector<anchor_peerlist_entry>& apl);
121  bool remove_from_peer_anchor(const epee::net_utils::network_address& addr);
122  bool remove_from_peer_white(const peerlist_entry& pe);
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  {
222  append_with_peer_gray(be);
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 
int64_t last_seen
std::vector< peerlist_entry > white
Definition: net_peerlist.h:57
bool foreach(bool white, const F &f)
Definition: net_peerlist.h:305
bool append_with_peer_anchor(const anchor_peerlist_entry &ple)
Definition: net_peerlist.h:403
uint16_t rpc_port
bool get_and_empty_anchor_peerlist(std::vector< anchor_peerlist_entry > &apl)
Definition: net_peerlist.h:478
AddressType adr
::std::string string
Definition: gtest-port.h:1097
CXA_THROW_INFO_T void(* dest)(void *))
Definition: stack_trace.cpp:91
static boost::optional< peerlist_storage > open(std::istream &src, const bool new_format)
bool remove_from_peer_white(const peerlist_entry &pe)
Definition: net_peerlist.h:442
std::vector< peerlist_entry > gray
Definition: net_peerlist.h:58
peerlist_entry_base< epee::net_utils::network_address > peerlist_entry
AddressType adr
bool remove_from_peer_anchor(const epee::net_utils::network_address &addr)
Definition: net_peerlist.h:499
uint32_t pruning_seed
STL namespace.
unsigned short uint16_t
Definition: stdint.h:125
bool append_with_peer_gray(const peerlist_entry &pr)
Definition: net_peerlist.h:368
std::vector< anchor_peerlist_entry > anchor
Definition: net_peerlist.h:59
std::enable_if< std::is_unsigned< T >::value, T >::type rand_idx(T sz)
Definition: crypto.h:244
bool get_white_peer_by_index(peerlist_entry &p, size_t i)
Definition: net_peerlist.h:230
#define P2P_DEFAULT_PEERS_IN_HANDSHAKE
peerid_type id
time_t time
Definition: blockchain.cpp:93
#define TRY_ENTRY()
Definition: misc_log_ex.h:151
unsigned int uint32_t
Definition: stdint.h:126
#define CRITICAL_REGION_LOCAL(x)
Definition: syncobj.h:228
bool remove_from_peer_gray(const peerlist_entry &pe)
Definition: net_peerlist.h:460
uint64_t peerid_type
#define P2P_LOCAL_GRAY_PEERLIST_LIMIT
bool merge_peerlist(const std::vector< peerlist_entry > &outer_bs)
Definition: net_peerlist.h:217
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition: pointer.h:1124
bool is_host_allowed(const epee::net_utils::network_address &address)
Definition: net_peerlist.h:254
bool get_random_gray_peer(peerlist_entry &pe)
Definition: net_peerlist.h:421
bool set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address &addr, uint32_t pruning_seed, uint16_t rpc_port)
Definition: net_peerlist.h:316
anchor_peerlist_entry_base< epee::net_utils::network_address > anchor_peerlist_entry
bool append_with_peer_white(const peerlist_entry &pr)
Definition: net_peerlist.h:332
bool get_gray_peer_by_index(peerlist_entry &p, size_t i)
Definition: net_peerlist.h:242
#define F(s)
peerlist_types take_zone(epee::net_utils::zone zone)
const char * address
Definition: multisig.cpp:37
t_iterator move_it_backward(t_iterator it, size_t count)
Definition: misc_language.h:73
#define CATCH_ENTRY_L0(lacation, return_val)
Definition: misc_log_ex.h:165
#define P2P_LOCAL_WHITE_PEERLIST_LIMIT
bool get_peerlist_head(std::vector< peerlist_entry > &bs_head, bool anonymize, uint32_t depth=P2P_DEFAULT_PEERS_IN_HANDSHAKE)
Definition: net_peerlist.h:267
bool store(std::ostream &dest, const peerlist_types &other) const
Save peers from this and other in stream dest.