Skip to content

Commit 94d335d

Browse files
committed
net: allow CSubNet of non-IP networks
Allow creation of valid `CSubNet` objects of non-IP networks and only match the single address they were created from (like /32 for IPv4 or /128 for IPv6). This fixes a deficiency in `CConnman::DisconnectNode(const CNetAddr& addr)` and in `BanMan` which assume that creating a subnet from any address using the `CSubNet(CNetAddr)` constructor would later match that address only. Before this change a non-IP subnet would be invalid and would not match any address.
1 parent bc8ada1 commit 94d335d

File tree

3 files changed

+107
-19
lines changed

3 files changed

+107
-19
lines changed

src/netaddress.cpp

+67-14
Original file line numberDiff line numberDiff line change
@@ -1068,15 +1068,24 @@ CSubNet::CSubNet(const CNetAddr& addr, const CNetAddr& mask) : CSubNet()
10681068

10691069
CSubNet::CSubNet(const CNetAddr& addr) : CSubNet()
10701070
{
1071-
valid = addr.IsIPv4() || addr.IsIPv6();
1072-
if (!valid) {
1071+
switch (addr.m_net) {
1072+
case NET_IPV4:
1073+
case NET_IPV6:
1074+
valid = true;
1075+
assert(addr.m_addr.size() <= sizeof(netmask));
1076+
memset(netmask, 0xFF, addr.m_addr.size());
1077+
break;
1078+
case NET_ONION:
1079+
case NET_I2P:
1080+
case NET_CJDNS:
1081+
valid = true;
1082+
break;
1083+
case NET_INTERNAL:
1084+
case NET_UNROUTABLE:
1085+
case NET_MAX:
10731086
return;
10741087
}
10751088

1076-
assert(addr.m_addr.size() <= sizeof(netmask));
1077-
1078-
memset(netmask, 0xFF, addr.m_addr.size());
1079-
10801089
network = addr;
10811090
}
10821091

@@ -1088,6 +1097,21 @@ bool CSubNet::Match(const CNetAddr &addr) const
10881097
{
10891098
if (!valid || !addr.IsValid() || network.m_net != addr.m_net)
10901099
return false;
1100+
1101+
switch (network.m_net) {
1102+
case NET_IPV4:
1103+
case NET_IPV6:
1104+
break;
1105+
case NET_ONION:
1106+
case NET_I2P:
1107+
case NET_CJDNS:
1108+
case NET_INTERNAL:
1109+
return addr == network;
1110+
case NET_UNROUTABLE:
1111+
case NET_MAX:
1112+
return false;
1113+
}
1114+
10911115
assert(network.m_addr.size() == addr.m_addr.size());
10921116
for (size_t x = 0; x < addr.m_addr.size(); ++x) {
10931117
if ((addr.m_addr[x] & netmask[x]) != network.m_addr[x]) {
@@ -1099,18 +1123,35 @@ bool CSubNet::Match(const CNetAddr &addr) const
10991123

11001124
std::string CSubNet::ToString() const
11011125
{
1102-
assert(network.m_addr.size() <= sizeof(netmask));
1126+
std::string suffix;
11031127

1104-
uint8_t cidr = 0;
1128+
switch (network.m_net) {
1129+
case NET_IPV4:
1130+
case NET_IPV6: {
1131+
assert(network.m_addr.size() <= sizeof(netmask));
11051132

1106-
for (size_t i = 0; i < network.m_addr.size(); ++i) {
1107-
if (netmask[i] == 0x00) {
1108-
break;
1133+
uint8_t cidr = 0;
1134+
1135+
for (size_t i = 0; i < network.m_addr.size(); ++i) {
1136+
if (netmask[i] == 0x00) {
1137+
break;
1138+
}
1139+
cidr += NetmaskBits(netmask[i]);
11091140
}
1110-
cidr += NetmaskBits(netmask[i]);
1141+
1142+
suffix = strprintf("/%u", cidr);
1143+
break;
1144+
}
1145+
case NET_ONION:
1146+
case NET_I2P:
1147+
case NET_CJDNS:
1148+
case NET_INTERNAL:
1149+
case NET_UNROUTABLE:
1150+
case NET_MAX:
1151+
break;
11111152
}
11121153

1113-
return network.ToString() + strprintf("/%u", cidr);
1154+
return network.ToString() + suffix;
11141155
}
11151156

11161157
bool CSubNet::IsValid() const
@@ -1120,7 +1161,19 @@ bool CSubNet::IsValid() const
11201161

11211162
bool CSubNet::SanityCheck() const
11221163
{
1123-
if (!(network.IsIPv4() || network.IsIPv6())) return false;
1164+
switch (network.m_net) {
1165+
case NET_IPV4:
1166+
case NET_IPV6:
1167+
break;
1168+
case NET_ONION:
1169+
case NET_I2P:
1170+
case NET_CJDNS:
1171+
return true;
1172+
case NET_INTERNAL:
1173+
case NET_UNROUTABLE:
1174+
case NET_MAX:
1175+
return false;
1176+
}
11241177

11251178
for (size_t x = 0; x < network.m_addr.size(); ++x) {
11261179
if (network.m_addr[x] & ~netmask[x]) return false;

src/netaddress.h

+23-1
Original file line numberDiff line numberDiff line change
@@ -462,11 +462,33 @@ class CSubNet
462462
bool SanityCheck() const;
463463

464464
public:
465+
/**
466+
* Construct an invalid subnet (empty, `Match()` always returns false).
467+
*/
465468
CSubNet();
469+
470+
/**
471+
* Construct from a given network start and number of bits (CIDR mask).
472+
* @param[in] addr Network start. Must be IPv4 or IPv6, otherwise an invalid subnet is
473+
* created.
474+
* @param[in] mask CIDR mask, must be in [0, 32] for IPv4 addresses and in [0, 128] for
475+
* IPv6 addresses. Otherwise an invalid subnet is created.
476+
*/
466477
CSubNet(const CNetAddr& addr, uint8_t mask);
478+
479+
/**
480+
* Construct from a given network start and mask.
481+
* @param[in] addr Network start. Must be IPv4 or IPv6, otherwise an invalid subnet is
482+
* created.
483+
* @param[in] mask Network mask, must be of the same type as `addr` and not contain 0-bits
484+
* followed by 1-bits. Otherwise an invalid subnet is created.
485+
*/
467486
CSubNet(const CNetAddr& addr, const CNetAddr& mask);
468487

469-
//constructor for single ip subnet (<ipv4>/32 or <ipv6>/128)
488+
/**
489+
* Construct a single-host subnet.
490+
* @param[in] addr The sole address to be contained in the subnet, can also be non-IPv[46].
491+
*/
470492
explicit CSubNet(const CNetAddr& addr);
471493

472494
bool Match(const CNetAddr &addr) const;

src/test/netbase_tests.cpp

+17-4
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,22 @@ BOOST_AUTO_TEST_CASE(subnet_test)
226226
// IPv4 address with IPv6 netmask or the other way around.
227227
BOOST_CHECK(!CSubNet(ResolveIP("1.1.1.1"), ResolveIP("ffff::")).IsValid());
228228
BOOST_CHECK(!CSubNet(ResolveIP("::1"), ResolveIP("255.0.0.0")).IsValid());
229-
// Can't subnet TOR (or any other non-IPv4 and non-IPv6 network).
230-
BOOST_CHECK(!CSubNet(ResolveIP("5wyqrzbvrdsumnok.onion"), ResolveIP("255.0.0.0")).IsValid());
229+
230+
// Create Non-IP subnets.
231+
232+
const CNetAddr tor_addr{
233+
ResolveIP("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion")};
234+
235+
subnet = CSubNet(tor_addr);
236+
BOOST_CHECK(subnet.IsValid());
237+
BOOST_CHECK_EQUAL(subnet.ToString(), tor_addr.ToString());
238+
BOOST_CHECK(subnet.Match(tor_addr));
239+
BOOST_CHECK(
240+
!subnet.Match(ResolveIP("kpgvmscirrdqpekbqjsvw5teanhatztpp2gl6eee4zkowvwfxwenqaid.onion")));
241+
BOOST_CHECK(!subnet.Match(ResolveIP("1.2.3.4")));
242+
243+
BOOST_CHECK(!CSubNet(tor_addr, 200).IsValid());
244+
BOOST_CHECK(!CSubNet(tor_addr, ResolveIP("255.0.0.0")).IsValid());
231245

232246
subnet = ResolveSubNet("1.2.3.4/255.255.255.255");
233247
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/32");
@@ -442,8 +456,7 @@ BOOST_AUTO_TEST_CASE(netbase_dont_resolve_strings_with_embedded_nul_characters)
442456
BOOST_CHECK(!LookupSubNet("1.2.3.0/24\0"s, ret));
443457
BOOST_CHECK(!LookupSubNet("1.2.3.0/24\0example.com"s, ret));
444458
BOOST_CHECK(!LookupSubNet("1.2.3.0/24\0example.com\0"s, ret));
445-
// We only do subnetting for IPv4 and IPv6
446-
BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion"s, ret));
459+
BOOST_CHECK(LookupSubNet("5wyqrzbvrdsumnok.onion"s, ret));
447460
BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion\0"s, ret));
448461
BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion\0example.com"s, ret));
449462
BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion\0example.com\0"s, ret));

0 commit comments

Comments
 (0)