net.h raw

   1  // Copyright (c) 2009-present The Bitcoin Core developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #ifndef BITCOIN_TEST_FUZZ_UTIL_NET_H
   6  #define BITCOIN_TEST_FUZZ_UTIL_NET_H
   7  
   8  #include <addrman.h>
   9  #include <addrman_impl.h>
  10  #include <net.h>
  11  #include <net_permissions.h>
  12  #include <netaddress.h>
  13  #include <node/connection_types.h>
  14  #include <node/eviction.h>
  15  #include <protocol.h>
  16  #include <sync.h>
  17  #include <test/fuzz/FuzzedDataProvider.h>
  18  #include <test/fuzz/util.h>
  19  #include <test/util/net.h>
  20  #include <test/util/time.h>
  21  #include <util/asmap.h>
  22  #include <util/sock.h>
  23  
  24  #include <chrono>
  25  #include <cstddef>
  26  #include <cstdint>
  27  #include <limits>
  28  #include <memory>
  29  #include <optional>
  30  #include <string>
  31  
  32  /**
  33   * Create a CNetAddr. It may have `addr.IsValid() == false`.
  34   * @param[in,out] fuzzed_data_provider Take data for the address from this, if `rand` is `nullptr`.
  35   * @param[in,out] rand If not nullptr, take data from it instead of from `fuzzed_data_provider`.
  36   * Prefer generating addresses using `fuzzed_data_provider` because it is not uniform. Only use
  37   * `rand` if `fuzzed_data_provider` is exhausted or its data is needed for other things.
  38   * @return a "random" network address.
  39   */
  40  CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider, FastRandomContext* rand = nullptr) noexcept;
  41  
  42  class AddrManDeterministic : public AddrMan
  43  {
  44  public:
  45      explicit AddrManDeterministic(const NetGroupManager& netgroupman, FuzzedDataProvider& fuzzed_data_provider, int32_t check_ratio)
  46          : AddrMan(netgroupman, /*deterministic=*/true, check_ratio)
  47      {
  48          WITH_LOCK(m_impl->cs, m_impl->insecure_rand.Reseed(ConsumeUInt256(fuzzed_data_provider)));
  49      }
  50  
  51      /**
  52       * Compare with another AddrMan.
  53       * This compares:
  54       * - the values in `mapInfo` (the keys aka ids are ignored)
  55       * - vvNew entries refer to the same addresses
  56       * - vvTried entries refer to the same addresses
  57       */
  58      bool operator==(const AddrManDeterministic& other) const
  59      {
  60          LOCK2(m_impl->cs, other.m_impl->cs);
  61  
  62          if (m_impl->mapInfo.size() != other.m_impl->mapInfo.size() || m_impl->nNew != other.m_impl->nNew ||
  63              m_impl->nTried != other.m_impl->nTried) {
  64              return false;
  65          }
  66  
  67          // Check that all values in `mapInfo` are equal to all values in `other.mapInfo`.
  68          // Keys may be different.
  69  
  70          auto addrinfo_hasher = [](const AddrInfo& a) {
  71              CSipHasher hasher(0, 0);
  72              auto addr_key = a.GetKey();
  73              auto source_key = a.source.GetAddrBytes();
  74              hasher.Write(TicksSinceEpoch<std::chrono::seconds>(a.m_last_success));
  75              hasher.Write(a.nAttempts);
  76              hasher.Write(a.nRefCount);
  77              hasher.Write(a.fInTried);
  78              hasher.Write(a.GetNetwork());
  79              hasher.Write(a.source.GetNetwork());
  80              hasher.Write(addr_key.size());
  81              hasher.Write(source_key.size());
  82              hasher.Write(addr_key);
  83              hasher.Write(source_key);
  84              return (size_t)hasher.Finalize();
  85          };
  86  
  87          auto addrinfo_eq = [](const AddrInfo& lhs, const AddrInfo& rhs) {
  88              return std::tie(static_cast<const CService&>(lhs), lhs.source, lhs.m_last_success, lhs.nAttempts, lhs.nRefCount, lhs.fInTried) ==
  89                     std::tie(static_cast<const CService&>(rhs), rhs.source, rhs.m_last_success, rhs.nAttempts, rhs.nRefCount, rhs.fInTried);
  90          };
  91  
  92          using Addresses = std::unordered_set<AddrInfo, decltype(addrinfo_hasher), decltype(addrinfo_eq)>;
  93  
  94          const size_t num_addresses{m_impl->mapInfo.size()};
  95  
  96          Addresses addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
  97          for (const auto& [id, addr] : m_impl->mapInfo) {
  98              addresses.insert(addr);
  99          }
 100  
 101          Addresses other_addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
 102          for (const auto& [id, addr] : other.m_impl->mapInfo) {
 103              other_addresses.insert(addr);
 104          }
 105  
 106          if (addresses != other_addresses) {
 107              return false;
 108          }
 109  
 110          auto IdsReferToSameAddress = [&](nid_type id, nid_type other_id) EXCLUSIVE_LOCKS_REQUIRED(m_impl->cs, other.m_impl->cs) {
 111              if (id == -1 && other_id == -1) {
 112                  return true;
 113              }
 114              if ((id == -1 && other_id != -1) || (id != -1 && other_id == -1)) {
 115                  return false;
 116              }
 117              return m_impl->mapInfo.at(id) == other.m_impl->mapInfo.at(other_id);
 118          };
 119  
 120          // Check that `vvNew` contains the same addresses as `other.vvNew`. Notice - `vvNew[i][j]`
 121          // contains just an id and the address is to be found in `mapInfo.at(id)`. The ids
 122          // themselves may differ between `vvNew` and `other.vvNew`.
 123          for (size_t i = 0; i < ADDRMAN_NEW_BUCKET_COUNT; ++i) {
 124              for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) {
 125                  if (!IdsReferToSameAddress(m_impl->vvNew[i][j], other.m_impl->vvNew[i][j])) {
 126                      return false;
 127                  }
 128              }
 129          }
 130  
 131          // Same for `vvTried`.
 132          for (size_t i = 0; i < ADDRMAN_TRIED_BUCKET_COUNT; ++i) {
 133              for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) {
 134                  if (!IdsReferToSameAddress(m_impl->vvTried[i][j], other.m_impl->vvTried[i][j])) {
 135                      return false;
 136                  }
 137              }
 138          }
 139  
 140          return true;
 141      }
 142  };
 143  
 144  class FuzzedNetEvents : public NetEventsInterface
 145  {
 146  public:
 147      FuzzedNetEvents(FuzzedDataProvider& fdp) : m_fdp(fdp) {}
 148  
 149      virtual void InitializeNode(const CNode&, ServiceFlags) override {}
 150  
 151      virtual void FinalizeNode(const CNode&) override {}
 152  
 153      virtual bool HasAllDesirableServiceFlags(ServiceFlags) const override { return m_fdp.ConsumeBool(); }
 154  
 155      virtual bool ProcessMessages(CNode&, std::atomic<bool>&) override { return m_fdp.ConsumeBool(); }
 156  
 157      virtual bool SendMessages(CNode&) override { return m_fdp.ConsumeBool(); }
 158  
 159  private:
 160      FuzzedDataProvider& m_fdp;
 161  };
 162  
 163  class FuzzedSock : public Sock
 164  {
 165      FuzzedDataProvider& m_fuzzed_data_provider;
 166  
 167      /**
 168       * Data to return when `MSG_PEEK` is used as a `Recv()` flag.
 169       * If `MSG_PEEK` is used, then our `Recv()` returns some random data as usual, but on the next
 170       * `Recv()` call we must return the same data, thus we remember it here.
 171       */
 172      mutable std::vector<uint8_t> m_peek_data;
 173  
 174      /**
 175       * Whether to pretend that the socket is select(2)-able. This is randomly set in the
 176       * constructor. It should remain constant so that repeated calls to `IsSelectable()`
 177       * return the same value.
 178       */
 179      const bool m_selectable;
 180  
 181      /**
 182       * Externally-provided context used to mock the steady clock in methods
 183       * waiting for a given duration. It is a reference (rather than an owned
 184       * member) so that several FuzzedSock instances sharing a test case (e.g.
 185       * one per peer, or one created from Accept()) advance a single mocked
 186       * clock.
 187       */
 188      FakeSteadyClock& m_clock;
 189  
 190  public:
 191      explicit FuzzedSock(FuzzedDataProvider& fuzzed_data_provider, FakeSteadyClock& clock);
 192  
 193      ~FuzzedSock() override;
 194  
 195      FuzzedSock& operator=(Sock&& other) override;
 196  
 197      ssize_t Send(const void* data, size_t len, int flags) const override;
 198  
 199      ssize_t Recv(void* buf, size_t len, int flags) const override;
 200  
 201      int Connect(const sockaddr*, socklen_t) const override;
 202  
 203      int Bind(const sockaddr*, socklen_t) const override;
 204  
 205      int Listen(int backlog) const override;
 206  
 207      std::unique_ptr<Sock> Accept(sockaddr* addr, socklen_t* addr_len) const override;
 208  
 209      int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override;
 210  
 211      int SetSockOpt(int level, int opt_name, const void* opt_val, socklen_t opt_len) const override;
 212  
 213      int GetSockName(sockaddr* name, socklen_t* name_len) const override;
 214  
 215      bool SetNonBlocking() const override;
 216  
 217      bool IsSelectable() const override;
 218  
 219      bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override;
 220  
 221      bool WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const override;
 222  
 223      bool IsConnected(std::string& errmsg) const override;
 224  };
 225  
 226  [[nodiscard]] inline FuzzedNetEvents ConsumeNetEvents(FuzzedDataProvider& fdp) noexcept
 227  {
 228      return FuzzedNetEvents{fdp};
 229  }
 230  
 231  [[nodiscard]] inline FuzzedSock ConsumeSock(FuzzedDataProvider& fuzzed_data_provider, FakeSteadyClock& clock)
 232  {
 233      return FuzzedSock{fuzzed_data_provider, clock};
 234  }
 235  
 236  [[nodiscard]] inline NetGroupManager ConsumeNetGroupManager(FuzzedDataProvider& fuzzed_data_provider) noexcept
 237  {
 238      std::vector<std::byte> asmap{ConsumeRandomLengthByteVector<std::byte>(fuzzed_data_provider)};
 239      if (!CheckStandardAsmap(asmap)) {
 240          return NetGroupManager::NoAsmap();
 241      }
 242      return NetGroupManager::WithLoadedAsmap(std::move(asmap));
 243  }
 244  
 245  inline CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept
 246  {
 247      return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint8_t>()};
 248  }
 249  
 250  inline CService ConsumeService(FuzzedDataProvider& fuzzed_data_provider) noexcept
 251  {
 252      return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint16_t>()};
 253  }
 254  
 255  inline std::vector<CService> ConsumeServiceVector(FuzzedDataProvider& fuzzed_data_provider,
 256                                                    size_t max_vector_size = 5) noexcept
 257  {
 258      std::vector<CService> ret;
 259      const size_t size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_vector_size);
 260      ret.reserve(size);
 261      for (size_t i = 0; i < size; ++i) {
 262          ret.emplace_back(ConsumeService(fuzzed_data_provider));
 263      }
 264      return ret;
 265  }
 266  
 267  CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcept;
 268  
 269  template <bool ReturnUniquePtr = false>
 270  auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, FakeSteadyClock& clock, const std::optional<NodeId>& node_id_in = std::nullopt) noexcept
 271  {
 272      const NodeId node_id = node_id_in.value_or(fuzzed_data_provider.ConsumeIntegralInRange<NodeId>(0, std::numeric_limits<NodeId>::max()));
 273      const auto sock = std::make_shared<FuzzedSock>(fuzzed_data_provider, clock);
 274      const CAddress address = ConsumeAddress(fuzzed_data_provider);
 275      const uint64_t keyed_net_group = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
 276      const uint64_t local_host_nonce = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
 277      const CAddress addr_bind = ConsumeAddress(fuzzed_data_provider);
 278      const std::string addr_name = fuzzed_data_provider.ConsumeRandomLengthString(64);
 279      const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray(ALL_CONNECTION_TYPES);
 280      const bool inbound_onion{conn_type == ConnectionType::INBOUND ? fuzzed_data_provider.ConsumeBool() : false};
 281      const uint64_t network_id = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
 282  
 283      NetPermissionFlags permission_flags = ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS);
 284      if constexpr (ReturnUniquePtr) {
 285          return std::make_unique<CNode>(node_id,
 286                                         sock,
 287                                         address,
 288                                         keyed_net_group,
 289                                         local_host_nonce,
 290                                         addr_bind,
 291                                         addr_name,
 292                                         conn_type,
 293                                         inbound_onion,
 294                                         network_id,
 295                                         CNodeOptions{ .permission_flags = permission_flags });
 296      } else {
 297          return CNode{node_id,
 298                       sock,
 299                       address,
 300                       keyed_net_group,
 301                       local_host_nonce,
 302                       addr_bind,
 303                       addr_name,
 304                       conn_type,
 305                       inbound_onion,
 306                       network_id,
 307                       CNodeOptions{ .permission_flags = permission_flags }};
 308      }
 309  }
 310  inline std::unique_ptr<CNode> ConsumeNodeAsUniquePtr(FuzzedDataProvider& fdp, FakeSteadyClock& clock, const std::optional<NodeId>& node_id_in = std::nullopt) { return ConsumeNode<true>(fdp, clock, node_id_in); }
 311  
 312  void FillNode(FuzzedDataProvider& fuzzed_data_provider, ConnmanTestMsg& connman, CNode& node) noexcept EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex);
 313  
 314  #endif // BITCOIN_TEST_FUZZ_UTIL_NET_H
 315