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