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