Skip to content

Commit 0655e9d

Browse files
committed
Merge bitcoin#27071: Handle CJDNS from LookupSubNet()
0e6f6eb net: remove unused CConnman::FindNode(const CSubNet&) (Vasil Dimov) 9482cb7 netbase: possibly change the result of LookupSubNet() to CJDNS (Vasil Dimov) 53afa68 net: move MaybeFlipIPv6toCJDNS() from net to netbase (Vasil Dimov) 6e30865 net: move IsReachable() code to netbase and encapsulate it (Vasil Dimov) c42ded3 fuzz: ConsumeNetAddr(): avoid IPv6 addresses that look like CJDNS (Vasil Dimov) 64d6f77 net: put CJDNS prefix byte in a constant (Vasil Dimov) Pull request description: `LookupSubNet()` would treat addresses that start with `fc` as IPv6 even if `-cjdnsreachable` is set. This creates the following problems where it is called: * `NetWhitelistPermissions::TryParse()`: otherwise `-whitelist=` fails to white list CJDNS addresses: when a CJDNS peer connects to us, it will be matched against IPv6 `fc...` subnet and the match will never succeed. * `BanMapFromJson()`: CJDNS bans are stored as just IPv6 addresses in `banlist.json`. Upon reading from disk they have to be converted back to CJDNS, otherwise, after restart, a ban entry like (`fc00::1`, IPv6) would not match a peer (`fc00::1`, CJDNS). * `RPCConsole::unbanSelectedNode()`: in the GUI the ban entries go through `CSubNet::ToString()` and back via `LookupSubNet()`. Then it must match whatever is stored in `BanMan`, otherwise it is impossible to unban via the GUI. These were uncovered by bitcoin#26859. Thus, flip the result of `LookupSubNet()` to CJDNS if the network base address starts with `fc` and `-cjdnsreachable` is set. Since subnetting/masking does not make sense for CJDNS (the address is "random" bytes, like Tor and I2P, there is no hierarchy) treat `fc.../mask` as an invalid `CSubNet`. To achieve that, `MaybeFlipIPv6toCJDNS()` has to be moved from `net` to `netbase` and thus also `IsReachable()`. In the process of moving `IsReachable()`, `SetReachable()` and `vfLimited[]` encapsulate those in a class. ACKs for top commit: jonatack: Code review ACK 0e6f6eb achow101: ACK 0e6f6eb mzumsande: re-ACK 0e6f6eb Tree-SHA512: 4767a60dc882916de4c8b110ce8de208ff3f58daaa0b560e6547d72e604d07c4157e72cf98b237228310fc05c0a3922f446674492e2ba02e990a272d288bd566
2 parents 6e721c9 + 0e6f6eb commit 0655e9d

13 files changed

+168
-139
lines changed

src/httpserver.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ CService HTTPRequest::GetPeer() const
682682
evhttp_connection_get_peer(con, (char**)&address, &port);
683683
#endif // HAVE_EVHTTP_CONNECTION_GET_PEER_CONST_CHAR
684684

685-
peer = LookupNumeric(address, port);
685+
peer = MaybeFlipIPv6toCJDNS(LookupNumeric(address, port));
686686
}
687687
return peer;
688688
}

src/init.cpp

+10-16
Original file line numberDiff line numberDiff line change
@@ -1314,38 +1314,32 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
13141314
}
13151315

13161316
if (args.IsArgSet("-onlynet")) {
1317-
std::set<enum Network> nets;
1317+
g_reachable_nets.RemoveAll();
13181318
for (const std::string& snet : args.GetArgs("-onlynet")) {
13191319
enum Network net = ParseNetwork(snet);
13201320
if (net == NET_UNROUTABLE)
13211321
return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet));
1322-
nets.insert(net);
1323-
}
1324-
for (int n = 0; n < NET_MAX; n++) {
1325-
enum Network net = (enum Network)n;
1326-
assert(IsReachable(net));
1327-
if (!nets.count(net))
1328-
SetReachable(net, false);
1322+
g_reachable_nets.Add(net);
13291323
}
13301324
}
13311325

13321326
if (!args.IsArgSet("-cjdnsreachable")) {
1333-
if (args.IsArgSet("-onlynet") && IsReachable(NET_CJDNS)) {
1327+
if (args.IsArgSet("-onlynet") && g_reachable_nets.Contains(NET_CJDNS)) {
13341328
return InitError(
13351329
_("Outbound connections restricted to CJDNS (-onlynet=cjdns) but "
13361330
"-cjdnsreachable is not provided"));
13371331
}
1338-
SetReachable(NET_CJDNS, false);
1332+
g_reachable_nets.Remove(NET_CJDNS);
13391333
}
1340-
// Now IsReachable(NET_CJDNS) is true if:
1334+
// Now g_reachable_nets.Contains(NET_CJDNS) is true if:
13411335
// 1. -cjdnsreachable is given and
13421336
// 2.1. -onlynet is not given or
13431337
// 2.2. -onlynet=cjdns is given
13441338

13451339
// Requesting DNS seeds entails connecting to IPv4/IPv6, which -onlynet options may prohibit:
13461340
// If -dnsseed=1 is explicitly specified, abort. If it's left unspecified by the user, we skip
13471341
// the DNS seeds by adjusting -dnsseed in InitParameterInteraction.
1348-
if (args.GetBoolArg("-dnsseed") == true && !IsReachable(NET_IPV4) && !IsReachable(NET_IPV6)) {
1342+
if (args.GetBoolArg("-dnsseed") == true && !g_reachable_nets.Contains(NET_IPV4) && !g_reachable_nets.Contains(NET_IPV6)) {
13491343
return InitError(strprintf(_("Incompatible options: -dnsseed=1 was explicitly specified, but -onlynet forbids connections to IPv4/IPv6")));
13501344
};
13511345

@@ -1375,7 +1369,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
13751369
onion_proxy = addrProxy;
13761370
}
13771371

1378-
const bool onlynet_used_with_onion{args.IsArgSet("-onlynet") && IsReachable(NET_ONION)};
1372+
const bool onlynet_used_with_onion{args.IsArgSet("-onlynet") && g_reachable_nets.Contains(NET_ONION)};
13791373

13801374
// -onion can be used to set only a proxy for .onion, or override normal proxy for .onion addresses
13811375
// -noonion (or -onion=0) disables connecting to .onion entirely
@@ -1410,7 +1404,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
14101404
"reaching the Tor network is not provided: none of -proxy, -onion or "
14111405
"-listenonion is given"));
14121406
}
1413-
SetReachable(NET_ONION, false);
1407+
g_reachable_nets.Remove(NET_ONION);
14141408
}
14151409

14161410
for (const std::string& strAddr : args.GetArgs("-externalip")) {
@@ -1885,12 +1879,12 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
18851879
}
18861880
SetProxy(NET_I2P, Proxy{addr.value()});
18871881
} else {
1888-
if (args.IsArgSet("-onlynet") && IsReachable(NET_I2P)) {
1882+
if (args.IsArgSet("-onlynet") && g_reachable_nets.Contains(NET_I2P)) {
18891883
return InitError(
18901884
_("Outbound connections restricted to i2p (-onlynet=i2p) but "
18911885
"-i2psam is not provided"));
18921886
}
1893-
SetReachable(NET_I2P, false);
1887+
g_reachable_nets.Remove(NET_I2P);
18941888
}
18951889

18961890
connOptions.m_i2p_accept_incoming = args.GetBoolArg("-i2pacceptincoming", DEFAULT_I2P_ACCEPT_INCOMING);

src/net.cpp

+7-53
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ bool fDiscover = true;
115115
bool fListen = true;
116116
GlobalMutex g_maplocalhost_mutex;
117117
std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(g_maplocalhost_mutex);
118-
static bool vfLimited[NET_MAX] GUARDED_BY(g_maplocalhost_mutex) = {};
119118
std::string strSubVersion;
120119

121120
size_t CSerializedNetMsg::GetMemoryUsage() const noexcept
@@ -232,7 +231,7 @@ static int GetnScore(const CService& addr)
232231
{
233232
CService addrLocal = pnode->GetAddrLocal();
234233
return fDiscover && pnode->addr.IsRoutable() && addrLocal.IsRoutable() &&
235-
IsReachable(addrLocal.GetNetwork());
234+
g_reachable_nets.Contains(addrLocal);
236235
}
237236

238237
std::optional<CService> GetLocalAddrForPeer(CNode& node)
@@ -270,22 +269,6 @@ std::optional<CService> GetLocalAddrForPeer(CNode& node)
270269
return std::nullopt;
271270
}
272271

273-
/**
274-
* If an IPv6 address belongs to the address range used by the CJDNS network and
275-
* the CJDNS network is reachable (-cjdnsreachable config is set), then change
276-
* the type from NET_IPV6 to NET_CJDNS.
277-
* @param[in] service Address to potentially convert.
278-
* @return a copy of `service` either unmodified or changed to CJDNS.
279-
*/
280-
CService MaybeFlipIPv6toCJDNS(const CService& service)
281-
{
282-
CService ret{service};
283-
if (ret.IsIPv6() && ret.HasCJDNSPrefix() && IsReachable(NET_CJDNS)) {
284-
ret.m_net = NET_CJDNS;
285-
}
286-
return ret;
287-
}
288-
289272
// learn a new local address
290273
bool AddLocal(const CService& addr_, int nScore)
291274
{
@@ -297,7 +280,7 @@ bool AddLocal(const CService& addr_, int nScore)
297280
if (!fDiscover && nScore < LOCAL_MANUAL)
298281
return false;
299282

300-
if (!IsReachable(addr))
283+
if (!g_reachable_nets.Contains(addr))
301284
return false;
302285

303286
LogPrintf("AddLocal(%s,%i)\n", addr.ToStringAddrPort(), nScore);
@@ -327,25 +310,6 @@ void RemoveLocal(const CService& addr)
327310
mapLocalHost.erase(addr);
328311
}
329312

330-
void SetReachable(enum Network net, bool reachable)
331-
{
332-
if (net == NET_UNROUTABLE || net == NET_INTERNAL)
333-
return;
334-
LOCK(g_maplocalhost_mutex);
335-
vfLimited[net] = !reachable;
336-
}
337-
338-
bool IsReachable(enum Network net)
339-
{
340-
LOCK(g_maplocalhost_mutex);
341-
return !vfLimited[net];
342-
}
343-
344-
bool IsReachable(const CNetAddr &addr)
345-
{
346-
return IsReachable(addr.GetNetwork());
347-
}
348-
349313
/** vote for a local address */
350314
bool SeenLocal(const CService& addr)
351315
{
@@ -375,17 +339,6 @@ CNode* CConnman::FindNode(const CNetAddr& ip)
375339
return nullptr;
376340
}
377341

378-
CNode* CConnman::FindNode(const CSubNet& subNet)
379-
{
380-
LOCK(m_nodes_mutex);
381-
for (CNode* pnode : m_nodes) {
382-
if (subNet.Match(static_cast<CNetAddr>(pnode->addr))) {
383-
return pnode;
384-
}
385-
}
386-
return nullptr;
387-
}
388-
389342
CNode* CConnman::FindNode(const std::string& addrName)
390343
{
391344
LOCK(m_nodes_mutex);
@@ -2433,7 +2386,7 @@ std::unordered_set<Network> CConnman::GetReachableEmptyNetworks() const
24332386
for (int n = 0; n < NET_MAX; n++) {
24342387
enum Network net = (enum Network)n;
24352388
if (net == NET_UNROUTABLE || net == NET_INTERNAL) continue;
2436-
if (IsReachable(net) && addrman.Size(net, std::nullopt) == 0) {
2389+
if (g_reachable_nets.Contains(net) && addrman.Size(net, std::nullopt) == 0) {
24372390
networks.insert(net);
24382391
}
24392392
}
@@ -2453,7 +2406,7 @@ bool CConnman::MaybePickPreferredNetwork(std::optional<Network>& network)
24532406

24542407
LOCK(m_nodes_mutex);
24552408
for (const auto net : nets) {
2456-
if (IsReachable(net) && m_network_conn_counts[net] == 0 && addrman.Size(net) != 0) {
2409+
if (g_reachable_nets.Contains(net) && m_network_conn_counts[net] == 0 && addrman.Size(net) != 0) {
24572410
network = net;
24582411
return true;
24592412
}
@@ -2683,7 +2636,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
26832636
if (anchor && !m_anchors.empty()) {
26842637
const CAddress addr = m_anchors.back();
26852638
m_anchors.pop_back();
2686-
if (!addr.IsValid() || IsLocal(addr) || !IsReachable(addr) ||
2639+
if (!addr.IsValid() || IsLocal(addr) || !g_reachable_nets.Contains(addr) ||
26872640
!HasAllDesirableServiceFlags(addr.nServices) ||
26882641
outbound_ipv46_peer_netgroups.count(m_netgroupman.GetGroup(addr))) continue;
26892642
addrConnect = addr;
@@ -2738,8 +2691,9 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
27382691
break;
27392692
}
27402693

2741-
if (!IsReachable(addr))
2694+
if (!g_reachable_nets.Contains(addr)) {
27422695
continue;
2696+
}
27432697

27442698
// only consider very recently tried nodes after 30 failed attempts
27452699
if (current_time - addr_last_try < 10min && nTries < 30) {

src/net.h

-13
Original file line numberDiff line numberDiff line change
@@ -160,24 +160,12 @@ enum
160160
/** Returns a local address that we should advertise to this peer. */
161161
std::optional<CService> GetLocalAddrForPeer(CNode& node);
162162

163-
/**
164-
* Mark a network as reachable or unreachable (no automatic connects to it)
165-
* @note Networks are reachable by default
166-
*/
167-
void SetReachable(enum Network net, bool reachable);
168-
/** @returns true if the network is reachable, false otherwise */
169-
bool IsReachable(enum Network net);
170-
/** @returns true if the address is in a reachable network, false otherwise */
171-
bool IsReachable(const CNetAddr& addr);
172-
173163
bool AddLocal(const CService& addr, int nScore = LOCAL_NONE);
174164
bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE);
175165
void RemoveLocal(const CService& addr);
176166
bool SeenLocal(const CService& addr);
177167
bool IsLocal(const CService& addr);
178168
CService GetLocalAddress(const CNode& peer);
179-
CService MaybeFlipIPv6toCJDNS(const CService& service);
180-
181169

182170
extern bool fDiscover;
183171
extern bool fListen;
@@ -1341,7 +1329,6 @@ class CConnman
13411329
uint64_t CalculateKeyedNetGroup(const CAddress& ad) const;
13421330

13431331
CNode* FindNode(const CNetAddr& ip);
1344-
CNode* FindNode(const CSubNet& subNet);
13451332
CNode* FindNode(const std::string& addrName);
13461333
CNode* FindNode(const CService& addr);
13471334

src/net_processing.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -3830,14 +3830,15 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
38303830
continue;
38313831
}
38323832
++num_proc;
3833-
bool fReachable = IsReachable(addr);
3833+
const bool reachable{g_reachable_nets.Contains(addr)};
38343834
if (addr.nTime > current_a_time - 10min && !peer->m_getaddr_sent && vAddr.size() <= 10 && addr.IsRoutable()) {
38353835
// Relay to a limited number of other nodes
3836-
RelayAddress(pfrom.GetId(), addr, fReachable);
3836+
RelayAddress(pfrom.GetId(), addr, reachable);
38373837
}
38383838
// Do not store addresses outside our network
3839-
if (fReachable)
3839+
if (reachable) {
38403840
vAddrOk.push_back(addr);
3841+
}
38413842
}
38423843
peer->m_addr_processed += num_proc;
38433844
peer->m_addr_rate_limited += num_rate_limit;

src/netaddress.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ static const std::array<uint8_t, 6> INTERNAL_IN_IPV6_PREFIX{
8181
0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 // 0xFD + sha256("bitcoin")[0:5].
8282
};
8383

84+
/// All CJDNS addresses start with 0xFC. See
85+
/// https://github.com/cjdelisle/cjdns/blob/master/doc/Whitepaper.md#pulling-it-all-together
86+
static constexpr uint8_t CJDNS_PREFIX{0xFC};
87+
8488
/// Size of IPv4 address (in bytes).
8589
static constexpr size_t ADDR_IPV4_SIZE = 4;
8690

@@ -174,7 +178,7 @@ class CNetAddr
174178
[[nodiscard]] bool IsTor() const { return m_net == NET_ONION; }
175179
[[nodiscard]] bool IsI2P() const { return m_net == NET_I2P; }
176180
[[nodiscard]] bool IsCJDNS() const { return m_net == NET_CJDNS; }
177-
[[nodiscard]] bool HasCJDNSPrefix() const { return m_addr[0] == 0xfc; }
181+
[[nodiscard]] bool HasCJDNSPrefix() const { return m_addr[0] == CJDNS_PREFIX; }
178182
bool IsLocal() const;
179183
bool IsRoutable() const;
180184
bool IsInternal() const;

src/netbase.cpp

+13-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ bool fNameLookup = DEFAULT_NAME_LOOKUP;
3232
std::chrono::milliseconds g_socks5_recv_timeout = 20s;
3333
static std::atomic<bool> interruptSocks5Recv(false);
3434

35+
ReachableNets g_reachable_nets;
36+
3537
std::vector<CNetAddr> WrappedGetAddrInfo(const std::string& name, bool allow_lookup)
3638
{
3739
addrinfo ai_hint{};
@@ -651,9 +653,10 @@ bool LookupSubNet(const std::string& subnet_str, CSubNet& subnet_out)
651653

652654
const size_t slash_pos{subnet_str.find_last_of('/')};
653655
const std::string str_addr{subnet_str.substr(0, slash_pos)};
654-
const std::optional<CNetAddr> addr{LookupHost(str_addr, /*fAllowLookup=*/false)};
656+
std::optional<CNetAddr> addr{LookupHost(str_addr, /*fAllowLookup=*/false)};
655657

656658
if (addr.has_value()) {
659+
addr = static_cast<CNetAddr>(MaybeFlipIPv6toCJDNS(CService{addr.value(), /*port=*/0}));
657660
if (slash_pos != subnet_str.npos) {
658661
const std::string netmask_str{subnet_str.substr(slash_pos + 1)};
659662
uint8_t netmask;
@@ -772,3 +775,12 @@ bool IsBadPort(uint16_t port)
772775
}
773776
return false;
774777
}
778+
779+
CService MaybeFlipIPv6toCJDNS(const CService& service)
780+
{
781+
CService ret{service};
782+
if (ret.IsIPv6() && ret.HasCJDNSPrefix() && g_reachable_nets.Contains(NET_CJDNS)) {
783+
ret.m_net = NET_CJDNS;
784+
}
785+
return ret;
786+
}

0 commit comments

Comments
 (0)