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 }