net_permissions.cpp raw

   1  // Copyright (c) 2009-2021 The Limenka 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  #include <common/messages.h>
   6  #include <common/system.h>
   7  #include <net_permissions.h>
   8  #include <netbase.h>
   9  #include <util/translation.h>
  10  
  11  using common::ResolveErrMsg;
  12  
  13  const std::vector<std::string> NET_PERMISSIONS_DOC{
  14      "bloomfilter (allow requesting BIP37 filtered blocks and transactions)",
  15      "blockfilters (serve compact block filters to peers per BIP157)",
  16      "noban (do not ban for misbehavior; implies download)",
  17      "forcerelay (relay transactions that are already in the mempool; implies relay)",
  18      "relay (relay even in -blocksonly mode, and unlimited transaction announcements)",
  19      "mempool (allow requesting BIP35 mempool contents)",
  20      "download (allow getheaders during IBD, no disconnect after maxuploadtarget limit)",
  21      "addr (responses to GETADDR avoid hitting the cache and contain random records with the most up-to-date info)",
  22      "forceinbound (when connections are full, attempt to evict a random unprotected inbound peer to open a slot; implies noban)"
  23  };
  24  
  25  namespace {
  26  
  27  // Parse the following format: "perm1,perm2@xxxxxx"
  28  static bool TryParsePermissionFlags(const std::string& str, NetPermissionFlags& output, ConnectionDirection* output_connection_direction, size_t& readen, bilingual_str& error)
  29  {
  30      NetPermissionFlags flags = NetPermissionFlags::None;
  31      ConnectionDirection connection_direction = ConnectionDirection::None;
  32      const auto atSeparator = str.find('@');
  33  
  34      // if '@' is not found (ie, "xxxxx"), the caller should apply implicit permissions
  35      if (atSeparator == std::string::npos) {
  36          NetPermissions::AddFlag(flags, NetPermissionFlags::Implicit);
  37          readen = 0;
  38      }
  39      // else (ie, "perm1,perm2@xxxxx"), let's enumerate the permissions by splitting by ',' and calculate the flags
  40      else {
  41          readen = 0;
  42          // permissions == perm1,perm2
  43          const auto permissions = str.substr(0, atSeparator);
  44          while (readen < permissions.length()) {
  45              const auto commaSeparator = permissions.find(',', readen);
  46              const auto len = commaSeparator == std::string::npos ? permissions.length() - readen : commaSeparator - readen;
  47              // permission == perm1
  48              const auto permission = permissions.substr(readen, len);
  49              readen += len; // We read "perm1"
  50              if (commaSeparator != std::string::npos) readen++; // We read ","
  51  
  52              if (permission == "bloomfilter" || permission == "bloom") NetPermissions::AddFlag(flags, NetPermissionFlags::BloomFilter);
  53              else if (permission == "blockfilters" || permission == "compactfilters" || permission == "cfilters") NetPermissions::AddFlag(flags, NetPermissionFlags::BlockFilters_Explicit);
  54              else if (permission == "noban") NetPermissions::AddFlag(flags, NetPermissionFlags::NoBan);
  55              else if (permission == "forcerelay") NetPermissions::AddFlag(flags, NetPermissionFlags::ForceRelay);
  56              else if (permission == "mempool") NetPermissions::AddFlag(flags, NetPermissionFlags::Mempool);
  57              else if (permission == "download") NetPermissions::AddFlag(flags, NetPermissionFlags::Download);
  58              else if (permission == "all") NetPermissions::AddFlag(flags, NetPermissionFlags::All);
  59              else if (permission == "relay") NetPermissions::AddFlag(flags, NetPermissionFlags::Relay);
  60              else if (permission == "addr") NetPermissions::AddFlag(flags, NetPermissionFlags::Addr);
  61              else if (permission == "forceinbound") NetPermissions::AddFlag(flags, NetPermissionFlags::ForceInbound);
  62              else if (permission == "in") connection_direction |= ConnectionDirection::In;
  63              else if (permission == "out") {
  64                  if (output_connection_direction == nullptr) {
  65                      // Only NetWhitebindPermissions() should pass a nullptr.
  66                      error = _("whitebind may only be used for incoming connections (\"out\" was passed)");
  67                      return false;
  68                  }
  69                  connection_direction |= ConnectionDirection::Out;
  70              }
  71              else if (permission.length() == 0); // Allow empty entries
  72              else {
  73                  error = strprintf(_("Invalid P2P permission: '%s'"), permission);
  74                  return false;
  75              }
  76          }
  77          readen++;
  78      }
  79  
  80      // By default, whitelist only applies to incoming connections
  81      if (connection_direction == ConnectionDirection::None) {
  82          connection_direction = ConnectionDirection::In;
  83      } else if (flags == NetPermissionFlags::None) {
  84          error = strprintf(_("Only direction was set, no permissions: '%s'"), str);
  85          return false;
  86      }
  87  
  88      output = flags;
  89      if (output_connection_direction) *output_connection_direction = connection_direction;
  90      error = Untranslated("");
  91      return true;
  92  }
  93  
  94  }
  95  
  96  std::vector<std::string> NetPermissions::ToStrings(NetPermissionFlags flags)
  97  {
  98      std::vector<std::string> strings;
  99      if (NetPermissions::HasFlag(flags, NetPermissionFlags::BlockFilters)) strings.emplace_back("blockfilters");
 100      if (NetPermissions::HasFlag(flags, NetPermissionFlags::BloomFilter)) strings.emplace_back("bloomfilter");
 101      if (NetPermissions::HasFlag(flags, NetPermissionFlags::NoBan)) strings.emplace_back("noban");
 102      if (NetPermissions::HasFlag(flags, NetPermissionFlags::ForceRelay)) strings.emplace_back("forcerelay");
 103      if (NetPermissions::HasFlag(flags, NetPermissionFlags::Relay)) strings.emplace_back("relay");
 104      if (NetPermissions::HasFlag(flags, NetPermissionFlags::Mempool)) strings.emplace_back("mempool");
 105      if (NetPermissions::HasFlag(flags, NetPermissionFlags::Download)) strings.emplace_back("download");
 106      if (NetPermissions::HasFlag(flags, NetPermissionFlags::Addr)) strings.emplace_back("addr");
 107      return strings;
 108  }
 109  
 110  bool NetWhitebindPermissions::TryParse(const std::string& str, NetWhitebindPermissions& output, bilingual_str& error)
 111  {
 112      NetPermissionFlags flags;
 113      size_t offset;
 114      if (!TryParsePermissionFlags(str, flags, /*output_connection_direction=*/nullptr, offset, error)) return false;
 115  
 116      const std::string strBind = str.substr(offset);
 117      const std::optional<CService> addrBind{Lookup(strBind, 0, false)};
 118      if (!addrBind.has_value()) {
 119          error = ResolveErrMsg("whitebind", strBind);
 120          return false;
 121      }
 122      if (addrBind.value().GetPort() == 0) {
 123          error = strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind);
 124          return false;
 125      }
 126  
 127      output.m_flags = flags;
 128      output.m_service = addrBind.value();
 129      error = Untranslated("");
 130      return true;
 131  }
 132  
 133  bool NetWhitelistPermissions::TryParse(const std::string& str, NetWhitelistPermissions& output, ConnectionDirection& output_connection_direction, bilingual_str& error)
 134  {
 135      NetPermissionFlags flags;
 136      size_t offset;
 137      // Only NetWhitebindPermissions should pass a nullptr for output_connection_direction.
 138      if (!TryParsePermissionFlags(str, flags, &output_connection_direction, offset, error)) return false;
 139  
 140      const std::string net = str.substr(offset);
 141      const CSubNet subnet{LookupSubNet(net)};
 142      if (!subnet.IsValid()) {
 143          error = strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net);
 144          return false;
 145      }
 146  
 147      output.m_flags = flags;
 148      output.m_subnet = subnet;
 149      error = Untranslated("");
 150      return true;
 151  }
 152