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