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