net_permissions.cpp
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 #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 }