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