Skip to content

Commit 66bc6e2

Browse files
luke-jrbrunoerg
authored andcommitted
Accept "in" and "out" flags to -whitelist to allow whitelisting manual connections
1 parent 8e06be3 commit 66bc6e2

File tree

7 files changed

+71
-21
lines changed

7 files changed

+71
-21
lines changed

src/init.cpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ void SetupServerArgs(ArgsManager& argsman)
473473
argsman.AddArg("-blocknotify=<cmd>", "Execute command when the best block changes (%s in cmd is replaced by block hash)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
474474
#endif
475475
argsman.AddArg("-blockreconstructionextratxn=<n>", strprintf("Extra transactions to keep in memory for compact block reconstructions (default: %u)", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
476-
argsman.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Automatic broadcast and rebroadcast of any transactions from inbound peers is disabled, unless the peer has the 'forcerelay' permission. RPC transactions are not affected. (default: %u)", DEFAULT_BLOCKSONLY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
476+
argsman.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Disables automatic broadcast and rebroadcast of transactions, unless the source peer has the 'forcerelay' permission. RPC transactions are not affected. (default: %u)", DEFAULT_BLOCKSONLY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
477477
argsman.AddArg("-coinstatsindex", strprintf("Maintain coinstats index used by the gettxoutsetinfo RPC (default: %u)", DEFAULT_COINSTATSINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
478478
argsman.AddArg("-conf=<file>", strprintf("Specify path to read-only configuration file. Relative paths will be prefixed by datadir location (only useable from command line, not configuration file) (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
479479
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -567,9 +567,11 @@ void SetupServerArgs(ArgsManager& argsman)
567567
"Use [host]:port notation for IPv6. Allowed permissions: " + Join(NET_PERMISSIONS_DOC, ", ") + ". "
568568
"Specify multiple permissions separated by commas (default: download,noban,mempool,relay). Can be specified multiple times.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
569569

570-
argsman.AddArg("-whitelist=<[permissions@]IP address or network>", "Add permission flags to the peers connecting from the given IP address (e.g. 1.2.3.4) or "
570+
argsman.AddArg("-whitelist=<[permissions@]IP address or network>", "Add permission flags to the peers using the given IP address (e.g. 1.2.3.4) or "
571571
"CIDR-notated network (e.g. 1.2.3.0/24). Uses the same permissions as "
572-
"-whitebind. Can be specified multiple times." , ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
572+
"-whitebind. "
573+
"Additional flags \"in\" and \"out\" control whether permissions apply to incoming connections and/or manual (default: incoming only). "
574+
"Can be specified multiple times.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
573575

574576
g_wallet_init_interface.AddWalletOptions(argsman);
575577

@@ -639,8 +641,8 @@ void SetupServerArgs(ArgsManager& argsman)
639641
OptionsCategory::NODE_RELAY);
640642
argsman.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kvB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",
641643
CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
642-
argsman.AddArg("-whitelistforcerelay", strprintf("Add 'forcerelay' permission to whitelisted inbound peers with default permissions. This will relay transactions even if the transactions were already in the mempool. (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
643-
argsman.AddArg("-whitelistrelay", strprintf("Add 'relay' permission to whitelisted inbound peers with default permissions. This will accept relayed transactions even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
644+
argsman.AddArg("-whitelistforcerelay", strprintf("Add 'forcerelay' permission to whitelisted peers with default permissions. This will relay transactions even if the transactions were already in the mempool. (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
645+
argsman.AddArg("-whitelistrelay", strprintf("Add 'relay' permission to whitelisted peers with default permissions. This will accept relayed transactions even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
644646

645647

646648
argsman.AddArg("-blockmaxweight=<n>", strprintf("Set maximum BIP141 block weight (default: %d)", DEFAULT_BLOCK_MAX_WEIGHT), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
@@ -1861,9 +1863,15 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
18611863

18621864
for (const auto& net : args.GetArgs("-whitelist")) {
18631865
NetWhitelistPermissions subnet;
1866+
ConnectionDirection connection_direction;
18641867
bilingual_str error;
1865-
if (!NetWhitelistPermissions::TryParse(net, subnet, error)) return InitError(error);
1866-
connOptions.vWhitelistedRange.push_back(subnet);
1868+
if (!NetWhitelistPermissions::TryParse(net, subnet, connection_direction, error)) return InitError(error);
1869+
if (connection_direction & ConnectionDirection::In) {
1870+
connOptions.vWhitelistedRangeIncoming.push_back(subnet);
1871+
}
1872+
if (connection_direction & ConnectionDirection::Out) {
1873+
connOptions.vWhitelistedRangeOutgoing.push_back(subnet);
1874+
}
18671875
}
18681876

18691877
connOptions.vSeedNodes = args.GetArgs("-seednode");

src/net.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,10 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
519519
return nullptr;
520520
}
521521

522+
NetPermissionFlags permission_flags = NetPermissionFlags::None;
523+
std::vector<NetWhitelistPermissions> whitelist_permissions = conn_type == ConnectionType::MANUAL ? vWhitelistedRangeOutgoing : std::vector<NetWhitelistPermissions>{};
524+
AddWhitelistPermissionFlags(permission_flags, addrConnect, whitelist_permissions);
525+
522526
// Add node
523527
NodeId id = GetNewNodeId();
524528
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
@@ -535,6 +539,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
535539
conn_type,
536540
/*inbound_onion=*/false,
537541
CNodeOptions{
542+
.permission_flags = permission_flags,
538543
.i2p_sam_session = std::move(i2p_transient_session),
539544
.recv_flood_size = nReceiveFloodSize,
540545
.use_v2transport = use_v2transport,
@@ -1735,7 +1740,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
17351740
{
17361741
int nInbound = 0;
17371742

1738-
AddWhitelistPermissionFlags(permission_flags, addr, vWhitelistedRange);
1743+
AddWhitelistPermissionFlags(permission_flags, addr, vWhitelistedRangeIncoming);
17391744

17401745
{
17411746
LOCK(m_nodes_mutex);

src/net.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,7 +1048,8 @@ class CConnman
10481048
uint64_t nMaxOutboundLimit = 0;
10491049
int64_t m_peer_connect_timeout = DEFAULT_PEER_CONNECT_TIMEOUT;
10501050
std::vector<std::string> vSeedNodes;
1051-
std::vector<NetWhitelistPermissions> vWhitelistedRange;
1051+
std::vector<NetWhitelistPermissions> vWhitelistedRangeIncoming;
1052+
std::vector<NetWhitelistPermissions> vWhitelistedRangeOutgoing;
10521053
std::vector<NetWhitebindPermissions> vWhiteBinds;
10531054
std::vector<CService> vBinds;
10541055
std::vector<CService> onion_binds;
@@ -1084,7 +1085,8 @@ class CConnman
10841085
LOCK(m_total_bytes_sent_mutex);
10851086
nMaxOutboundLimit = connOptions.nMaxOutboundLimit;
10861087
}
1087-
vWhitelistedRange = connOptions.vWhitelistedRange;
1088+
vWhitelistedRangeIncoming = connOptions.vWhitelistedRangeIncoming;
1089+
vWhitelistedRangeOutgoing = connOptions.vWhitelistedRangeOutgoing;
10881090
{
10891091
LOCK(m_added_nodes_mutex);
10901092
// Attempt v2 connection if we support v2 - we'll reconnect with v1 if our
@@ -1397,7 +1399,9 @@ class CConnman
13971399

13981400
// Whitelisted ranges. Any node connecting from these is automatically
13991401
// whitelisted (as well as those connecting to whitelisted binds).
1400-
std::vector<NetWhitelistPermissions> vWhitelistedRange;
1402+
std::vector<NetWhitelistPermissions> vWhitelistedRangeIncoming;
1403+
// Whitelisted ranges for outgoing connections.
1404+
std::vector<NetWhitelistPermissions> vWhitelistedRangeOutgoing;
14011405

14021406
unsigned int nSendBufferMaxSize{0};
14031407
unsigned int nReceiveFloodSize{0};

src/net_permissions.cpp

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ const std::vector<std::string> NET_PERMISSIONS_DOC{
2121
namespace {
2222

2323
// Parse the following format: "perm1,perm2@xxxxxx"
24-
bool TryParsePermissionFlags(const std::string& str, NetPermissionFlags& output, size_t& readen, bilingual_str& error)
24+
static bool TryParsePermissionFlags(const std::string& str, NetPermissionFlags& output, ConnectionDirection* output_connection_direction, size_t& readen, bilingual_str& error)
2525
{
2626
NetPermissionFlags flags = NetPermissionFlags::None;
27+
ConnectionDirection connection_direction = ConnectionDirection::None;
2728
const auto atSeparator = str.find('@');
2829

2930
// if '@' is not found (ie, "xxxxx"), the caller should apply implicit permissions
@@ -52,6 +53,15 @@ bool TryParsePermissionFlags(const std::string& str, NetPermissionFlags& output,
5253
else if (permission == "all") NetPermissions::AddFlag(flags, NetPermissionFlags::All);
5354
else if (permission == "relay") NetPermissions::AddFlag(flags, NetPermissionFlags::Relay);
5455
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+
}
5565
else if (permission.length() == 0); // Allow empty entries
5666
else {
5767
error = strprintf(_("Invalid P2P permission: '%s'"), permission);
@@ -61,7 +71,16 @@ bool TryParsePermissionFlags(const std::string& str, NetPermissionFlags& output,
6171
readen++;
6272
}
6373

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+
6482
output = flags;
83+
if (output_connection_direction) *output_connection_direction = connection_direction;
6584
error = Untranslated("");
6685
return true;
6786
}
@@ -85,7 +104,7 @@ bool NetWhitebindPermissions::TryParse(const std::string& str, NetWhitebindPermi
85104
{
86105
NetPermissionFlags flags;
87106
size_t offset;
88-
if (!TryParsePermissionFlags(str, flags, offset, error)) return false;
107+
if (!TryParsePermissionFlags(str, flags, /*output_connection_direction=*/nullptr, offset, error)) return false;
89108

90109
const std::string strBind = str.substr(offset);
91110
const std::optional<CService> addrBind{Lookup(strBind, 0, false)};
@@ -104,11 +123,12 @@ bool NetWhitebindPermissions::TryParse(const std::string& str, NetWhitebindPermi
104123
return true;
105124
}
106125

107-
bool NetWhitelistPermissions::TryParse(const std::string& str, NetWhitelistPermissions& output, bilingual_str& error)
126+
bool NetWhitelistPermissions::TryParse(const std::string& str, NetWhitelistPermissions& output, ConnectionDirection& output_connection_direction, bilingual_str& error)
108127
{
109128
NetPermissionFlags flags;
110129
size_t offset;
111-
if (!TryParsePermissionFlags(str, flags, offset, error)) return false;
130+
// Only NetWhitebindPermissions should pass a nullptr for output_connection_direction.
131+
if (!TryParsePermissionFlags(str, flags, &output_connection_direction, offset, error)) return false;
112132

113133
const std::string net = str.substr(offset);
114134
const CSubNet subnet{LookupSubNet(net)};

src/net_permissions.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

55
#include <netaddress.h>
6+
#include <netbase.h>
67

78
#include <string>
89
#include <type_traits>
@@ -88,7 +89,7 @@ class NetWhitebindPermissions : public NetPermissions
8889
class NetWhitelistPermissions : public NetPermissions
8990
{
9091
public:
91-
static bool TryParse(const std::string& str, NetWhitelistPermissions& output, bilingual_str& error);
92+
static bool TryParse(const std::string& str, NetWhitelistPermissions& output, ConnectionDirection& output_connection_direction, bilingual_str& error);
9293
CSubNet m_subnet;
9394
};
9495

src/test/fuzz/net_permissions.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

55
#include <net_permissions.h>
6+
#include <netbase.h>
67
#include <test/fuzz/FuzzedDataProvider.h>
78
#include <test/fuzz/fuzz.h>
89
#include <test/fuzz/util.h>
@@ -31,8 +32,9 @@ FUZZ_TARGET(net_permissions)
3132
}
3233

3334
NetWhitelistPermissions net_whitelist_permissions;
35+
ConnectionDirection connection_direction;
3436
bilingual_str error_net_whitelist_permissions;
35-
if (NetWhitelistPermissions::TryParse(s, net_whitelist_permissions, error_net_whitelist_permissions)) {
37+
if (NetWhitelistPermissions::TryParse(s, net_whitelist_permissions, connection_direction, error_net_whitelist_permissions)) {
3638
(void)NetPermissions::ToStrings(net_whitelist_permissions.m_flags);
3739
(void)NetPermissions::AddFlag(net_whitelist_permissions.m_flags, net_permission_flags);
3840
assert(NetPermissions::HasFlag(net_whitelist_permissions.m_flags, net_permission_flags));

src/test/netbase_tests.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ BOOST_AUTO_TEST_CASE(netpermissions_test)
366366
bilingual_str error;
367367
NetWhitebindPermissions whitebindPermissions;
368368
NetWhitelistPermissions whitelistPermissions;
369+
ConnectionDirection connection_direction;
369370

370371
// Detect invalid white bind
371372
BOOST_CHECK(!NetWhitebindPermissions::TryParse("", whitebindPermissions, error));
@@ -435,24 +436,33 @@ BOOST_AUTO_TEST_CASE(netpermissions_test)
435436
BOOST_CHECK(NetWhitebindPermissions::TryParse(",,@1.2.3.4:32", whitebindPermissions, error));
436437
BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, NetPermissionFlags::None);
437438

439+
BOOST_CHECK(!NetWhitebindPermissions::TryParse("out,[email protected]:32", whitebindPermissions, error));
440+
BOOST_CHECK(error.original.find("whitebind may only be used for incoming connections (\"out\" was passed)") != std::string::npos);
441+
438442
// Detect invalid flag
439443
BOOST_CHECK(!NetWhitebindPermissions::TryParse("bloom,forcerelay,[email protected]:32", whitebindPermissions, error));
440444
BOOST_CHECK(error.original.find("Invalid P2P permission") != std::string::npos);
441445

442446
// Check netmask error
443-
BOOST_CHECK(!NetWhitelistPermissions::TryParse("bloom,forcerelay,[email protected]:32", whitelistPermissions, error));
447+
BOOST_CHECK(!NetWhitelistPermissions::TryParse("bloom,forcerelay,[email protected]:32", whitelistPermissions, connection_direction, error));
444448
BOOST_CHECK(error.original.find("Invalid netmask specified in -whitelist") != std::string::npos);
445449

446450
// Happy path for whitelist parsing
447-
BOOST_CHECK(NetWhitelistPermissions::TryParse("[email protected]", whitelistPermissions, error));
451+
BOOST_CHECK(NetWhitelistPermissions::TryParse("[email protected]", whitelistPermissions, connection_direction, error));
448452
BOOST_CHECK_EQUAL(whitelistPermissions.m_flags, NetPermissionFlags::NoBan);
449453
BOOST_CHECK(NetPermissions::HasFlag(whitelistPermissions.m_flags, NetPermissionFlags::NoBan));
450454

451-
BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,[email protected]/32", whitelistPermissions, error));
455+
BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,[email protected]/32", whitelistPermissions, connection_direction, error));
452456
BOOST_CHECK_EQUAL(whitelistPermissions.m_flags, NetPermissionFlags::BloomFilter | NetPermissionFlags::ForceRelay | NetPermissionFlags::NoBan | NetPermissionFlags::Relay);
453457
BOOST_CHECK(error.empty());
454458
BOOST_CHECK_EQUAL(whitelistPermissions.m_subnet.ToString(), "1.2.3.4/32");
455-
BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,relay,[email protected]/32", whitelistPermissions, error));
459+
BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,relay,[email protected]/32", whitelistPermissions, connection_direction, error));
460+
BOOST_CHECK(NetWhitelistPermissions::TryParse("in,[email protected]", whitelistPermissions, connection_direction, error));
461+
BOOST_CHECK_EQUAL(connection_direction, ConnectionDirection::In);
462+
BOOST_CHECK(NetWhitelistPermissions::TryParse("out,[email protected]", whitelistPermissions, connection_direction, error));
463+
BOOST_CHECK_EQUAL(connection_direction, ConnectionDirection::Out);
464+
BOOST_CHECK(NetWhitelistPermissions::TryParse("in,out,[email protected]", whitelistPermissions, connection_direction, error));
465+
BOOST_CHECK_EQUAL(connection_direction, ConnectionDirection::Both);
456466

457467
const auto strings = NetPermissions::ToStrings(NetPermissionFlags::All);
458468
BOOST_CHECK_EQUAL(strings.size(), 7U);

0 commit comments

Comments
 (0)