Skip to content

Commit c38544a

Browse files
kwvgUdjinM6
andcommitted
merge bitcoin#27411: Restrict self-advertisements with privacy networks to avoid fingerprinting
The old `GetLocal()` has been moved to `masternode/node.cpp` due to its use in determining a node's external address. We don't want the old variant to be used otherwise so we'll move it out of `net.{cpp,h}` for good measure. Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
1 parent 8b43345 commit c38544a

8 files changed

Lines changed: 168 additions & 22 deletions

File tree

src/masternode/node.cpp

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,42 @@
1515
#include <validation.h>
1616
#include <warnings.h>
1717

18+
namespace {
19+
bool GetLocal(CService& addr, const CNetAddr* paddrPeer)
20+
{
21+
if (!fListen)
22+
return false;
23+
24+
int nBestScore = -1;
25+
{
26+
LOCK(g_maplocalhost_mutex);
27+
int nBestReachability = -1;
28+
for (const auto& entry : mapLocalHost)
29+
{
30+
// For privacy reasons, don't advertise our privacy-network address
31+
// to other networks and don't advertise our other-network address
32+
// to privacy networks.
33+
const Network our_net{entry.first.GetNetwork()};
34+
const Network peers_net{paddrPeer->GetNetwork()};
35+
if (our_net != peers_net &&
36+
(our_net == NET_ONION || our_net == NET_I2P ||
37+
peers_net == NET_ONION || peers_net == NET_I2P)) {
38+
continue;
39+
}
40+
int nScore = entry.second.nScore;
41+
int nReachability = entry.first.GetReachabilityFrom(*paddrPeer);
42+
if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore))
43+
{
44+
addr = CService(entry.first, entry.second.nPort);
45+
nBestReachability = nReachability;
46+
nBestScore = nScore;
47+
}
48+
}
49+
}
50+
return nBestScore >= 0;
51+
}
52+
} // anonymous namespace
53+
1854
CActiveMasternodeManager::CActiveMasternodeManager(const CBLSSecretKey& sk, CConnman& connman, const std::unique_ptr<CDeterministicMNManager>& dmnman) :
1955
m_info(sk, sk.GetPublicKey()),
2056
m_connman{connman},
@@ -204,8 +240,7 @@ bool CActiveMasternodeManager::GetLocalAddress(CService& addrRet)
204240
auto service = m_info.service;
205241
m_connman.ForEachNodeContinueIf(CConnman::AllNodes, [&](CNode* pnode) {
206242
empty = false;
207-
if (pnode->addr.IsIPv4())
208-
fFoundLocal = GetLocal(service, &pnode->addr) && IsValidNetAddr(service);
243+
if (pnode->addr.IsIPv4()) fFoundLocal = GetLocal(service, *pnode) && IsValidNetAddr(service);
209244
return !fFoundLocal;
210245
});
211246
// nothing and no live connections, can't do anything for now

src/net.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ uint16_t GetListenPort()
186186
}
187187

188188
// find 'best' local address for a particular peer
189-
bool GetLocal(CService& addr, const CNetAddr *paddrPeer)
189+
bool GetLocal(CService& addr, const CNode& peer)
190190
{
191191
if (!fListen)
192192
return false;
@@ -197,8 +197,18 @@ bool GetLocal(CService& addr, const CNetAddr *paddrPeer)
197197
LOCK(g_maplocalhost_mutex);
198198
for (const auto& entry : mapLocalHost)
199199
{
200+
// For privacy reasons, don't advertise our privacy-network address
201+
// to other networks and don't advertise our other-network address
202+
// to privacy networks.
203+
const Network our_net{entry.first.GetNetwork()};
204+
const Network peers_net{peer.ConnectedThroughNetwork()};
205+
if (our_net != peers_net &&
206+
(our_net == NET_ONION || our_net == NET_I2P ||
207+
peers_net == NET_ONION || peers_net == NET_I2P)) {
208+
continue;
209+
}
200210
int nScore = entry.second.nScore;
201-
int nReachability = entry.first.GetReachabilityFrom(paddrPeer);
211+
int nReachability = entry.first.GetReachabilityFrom(peer.addr);
202212
if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore))
203213
{
204214
addr = CService(entry.first, entry.second.nPort);
@@ -236,10 +246,10 @@ static std::vector<CAddress> ConvertSeeds(const std::vector<uint8_t> &vSeedsIn)
236246
// Otherwise, return the unroutable 0.0.0.0 but filled in with
237247
// the normal parameters, since the IP may be changed to a useful
238248
// one by discovery.
239-
CService GetLocalAddress(const CNetAddr& addrPeer)
249+
CService GetLocalAddress(const CNode& peer)
240250
{
241251
CService addr;
242-
if (GetLocal(addr, &addrPeer)) {
252+
if (GetLocal(addr, peer)) {
243253
return addr;
244254
}
245255
return CService{CNetAddr(), GetListenPort()};
@@ -262,7 +272,7 @@ bool IsPeerAddrLocalGood(CNode *pnode)
262272

263273
std::optional<CService> GetLocalAddrForPeer(CNode& node)
264274
{
265-
CService addrLocal{GetLocalAddress(node.addr)};
275+
CService addrLocal{GetLocalAddress(node)};
266276
// If discovery is enabled, sometimes give our peer the address it
267277
// tells us that it sees us as in case it has a better idea of our
268278
// address than we do.

src/net.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,8 @@ bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE);
199199
void RemoveLocal(const CService& addr);
200200
bool SeenLocal(const CService& addr);
201201
bool IsLocal(const CService& addr);
202-
bool GetLocal(CService &addr, const CNetAddr *paddrPeer = nullptr);
203-
CService GetLocalAddress(const CNetAddr& addrPeer);
202+
bool GetLocal(CService& addr, const CNode& peer);
203+
CService GetLocalAddress(const CNode& peer);
204204

205205

206206
extern bool fDiscover;

src/net_processing.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3460,7 +3460,7 @@ void PeerManagerImpl::ProcessMessage(
34603460
// indicate to the peer that we will participate in addr relay.
34613461
if (fListen && !m_chainman.ActiveChainstate().IsInitialBlockDownload())
34623462
{
3463-
CAddress addr{GetLocalAddress(pfrom.addr), peer->m_our_services, (uint32_t)GetAdjustedTime()};
3463+
CAddress addr{GetLocalAddress(pfrom), peer->m_our_services, (uint32_t)GetAdjustedTime()};
34643464
FastRandomContext insecure_rand;
34653465
if (addr.IsRoutable())
34663466
{

src/netaddress.cpp

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -742,19 +742,16 @@ uint64_t CNetAddr::GetHash() const
742742

743743
// private extensions to enum Network, only returned by GetExtNetwork,
744744
// and only used in GetReachabilityFrom
745-
static const int NET_UNKNOWN = NET_MAX + 0;
746-
static const int NET_TEREDO = NET_MAX + 1;
747-
int static GetExtNetwork(const CNetAddr *addr)
745+
static const int NET_TEREDO = NET_MAX;
746+
int static GetExtNetwork(const CNetAddr& addr)
748747
{
749-
if (addr == nullptr)
750-
return NET_UNKNOWN;
751-
if (addr->IsRFC4380())
748+
if (addr.IsRFC4380())
752749
return NET_TEREDO;
753-
return addr->GetNetwork();
750+
return addr.GetNetwork();
754751
}
755752

756753
/** Calculates a metric for how reachable (*this) is from a given partner */
757-
int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const
754+
int CNetAddr::GetReachabilityFrom(const CNetAddr& paddrPartner) const
758755
{
759756
enum Reachability {
760757
REACH_UNREACHABLE,
@@ -769,7 +766,7 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const
769766
if (!IsRoutable() || IsInternal())
770767
return REACH_UNREACHABLE;
771768

772-
int ourNet = GetExtNetwork(this);
769+
int ourNet = GetExtNetwork(*this);
773770
int theirNet = GetExtNetwork(paddrPartner);
774771
bool fTunnel = IsRFC3964() || IsRFC6052() || IsRFC6145();
775772

@@ -809,7 +806,6 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const
809806
case NET_IPV6: return REACH_IPV6_WEAK;
810807
case NET_IPV4: return REACH_IPV4;
811808
}
812-
case NET_UNKNOWN:
813809
case NET_UNROUTABLE:
814810
default:
815811
switch(ourNet) {

src/netaddress.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ class CNetAddr
211211
bool HasLinkedIPv4() const;
212212

213213
std::vector<unsigned char> GetAddrBytes() const;
214-
int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const;
214+
int GetReachabilityFrom(const CNetAddr& paddrPartner) const;
215215

216216
explicit CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0);
217217
bool GetIn6Addr(struct in6_addr* pipv6Addr) const;

src/test/fuzz/netaddress.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ FUZZ_TARGET(netaddress)
8383
(void)service.ToStringAddrPort();
8484

8585
const CNetAddr other_net_addr = ConsumeNetAddr(fuzzed_data_provider);
86-
(void)net_addr.GetReachabilityFrom(&other_net_addr);
86+
(void)net_addr.GetReachabilityFrom(other_net_addr);
8787
(void)sub_net.Match(other_net_addr);
8888

8989
const CService other_service{net_addr, fuzzed_data_provider.ConsumeIntegral<uint16_t>()};

src/test/net_tests.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,4 +908,109 @@ BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message)
908908
TestOnlyResetTimeData();
909909
}
910910

911+
912+
BOOST_AUTO_TEST_CASE(advertise_local_address)
913+
{
914+
auto CreatePeer = [](const CAddress& addr) {
915+
return std::make_unique<CNode>(/*id=*/0,
916+
/*sock=*/nullptr,
917+
addr,
918+
/*nKeyedNetGroupIn=*/0,
919+
/*nLocalHostNonceIn=*/0,
920+
CAddress{},
921+
/*pszDest=*/std::string{},
922+
ConnectionType::OUTBOUND_FULL_RELAY,
923+
/*inbound_onion=*/false);
924+
};
925+
SetReachable(NET_CJDNS, true);
926+
927+
CAddress addr_ipv4{Lookup("1.2.3.4", 8333, false).value(), NODE_NONE};
928+
BOOST_REQUIRE(addr_ipv4.IsValid());
929+
BOOST_REQUIRE(addr_ipv4.IsIPv4());
930+
931+
CAddress addr_ipv6{Lookup("1122:3344:5566:7788:9900:aabb:ccdd:eeff", 8333, false).value(), NODE_NONE};
932+
BOOST_REQUIRE(addr_ipv6.IsValid());
933+
BOOST_REQUIRE(addr_ipv6.IsIPv6());
934+
935+
CAddress addr_ipv6_tunnel{Lookup("2002:3344:5566:7788:9900:aabb:ccdd:eeff", 8333, false).value(), NODE_NONE};
936+
BOOST_REQUIRE(addr_ipv6_tunnel.IsValid());
937+
BOOST_REQUIRE(addr_ipv6_tunnel.IsIPv6());
938+
BOOST_REQUIRE(addr_ipv6_tunnel.IsRFC3964());
939+
940+
CAddress addr_teredo{Lookup("2001:0000:5566:7788:9900:aabb:ccdd:eeff", 8333, false).value(), NODE_NONE};
941+
BOOST_REQUIRE(addr_teredo.IsValid());
942+
BOOST_REQUIRE(addr_teredo.IsIPv6());
943+
BOOST_REQUIRE(addr_teredo.IsRFC4380());
944+
945+
CAddress addr_onion;
946+
BOOST_REQUIRE(addr_onion.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion"));
947+
BOOST_REQUIRE(addr_onion.IsValid());
948+
BOOST_REQUIRE(addr_onion.IsTor());
949+
950+
CAddress addr_i2p;
951+
BOOST_REQUIRE(addr_i2p.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p"));
952+
BOOST_REQUIRE(addr_i2p.IsValid());
953+
BOOST_REQUIRE(addr_i2p.IsI2P());
954+
955+
CService service_cjdns{Lookup("fc00:3344:5566:7788:9900:aabb:ccdd:eeff", 8333, false).value(), NODE_NONE};
956+
CAddress addr_cjdns{MaybeFlipIPv6toCJDNS(service_cjdns), NODE_NONE};
957+
BOOST_REQUIRE(addr_cjdns.IsValid());
958+
BOOST_REQUIRE(addr_cjdns.IsCJDNS());
959+
960+
const auto peer_ipv4{CreatePeer(addr_ipv4)};
961+
const auto peer_ipv6{CreatePeer(addr_ipv6)};
962+
const auto peer_ipv6_tunnel{CreatePeer(addr_ipv6_tunnel)};
963+
const auto peer_teredo{CreatePeer(addr_teredo)};
964+
const auto peer_onion{CreatePeer(addr_onion)};
965+
const auto peer_i2p{CreatePeer(addr_i2p)};
966+
const auto peer_cjdns{CreatePeer(addr_cjdns)};
967+
968+
// one local clearnet address - advertise to all but privacy peers
969+
AddLocal(addr_ipv4);
970+
BOOST_CHECK(GetLocalAddress(*peer_ipv4) == addr_ipv4);
971+
BOOST_CHECK(GetLocalAddress(*peer_ipv6) == addr_ipv4);
972+
BOOST_CHECK(GetLocalAddress(*peer_ipv6_tunnel) == addr_ipv4);
973+
BOOST_CHECK(GetLocalAddress(*peer_teredo) == addr_ipv4);
974+
BOOST_CHECK(GetLocalAddress(*peer_cjdns) == addr_ipv4);
975+
BOOST_CHECK(!GetLocalAddress(*peer_onion).IsValid());
976+
BOOST_CHECK(!GetLocalAddress(*peer_i2p).IsValid());
977+
RemoveLocal(addr_ipv4);
978+
979+
// local privacy addresses - don't advertise to clearnet peers
980+
AddLocal(addr_onion);
981+
AddLocal(addr_i2p);
982+
BOOST_CHECK(!GetLocalAddress(*peer_ipv4).IsValid());
983+
BOOST_CHECK(!GetLocalAddress(*peer_ipv6).IsValid());
984+
BOOST_CHECK(!GetLocalAddress(*peer_ipv6_tunnel).IsValid());
985+
BOOST_CHECK(!GetLocalAddress(*peer_teredo).IsValid());
986+
BOOST_CHECK(!GetLocalAddress(*peer_cjdns).IsValid());
987+
BOOST_CHECK(GetLocalAddress(*peer_onion) == addr_onion);
988+
BOOST_CHECK(GetLocalAddress(*peer_i2p) == addr_i2p);
989+
RemoveLocal(addr_onion);
990+
RemoveLocal(addr_i2p);
991+
992+
// local addresses from all networks
993+
AddLocal(addr_ipv4);
994+
AddLocal(addr_ipv6);
995+
AddLocal(addr_ipv6_tunnel);
996+
AddLocal(addr_teredo);
997+
AddLocal(addr_onion);
998+
AddLocal(addr_i2p);
999+
AddLocal(addr_cjdns);
1000+
BOOST_CHECK(GetLocalAddress(*peer_ipv4) == addr_ipv4);
1001+
BOOST_CHECK(GetLocalAddress(*peer_ipv6) == addr_ipv6);
1002+
BOOST_CHECK(GetLocalAddress(*peer_ipv6_tunnel) == addr_ipv6);
1003+
BOOST_CHECK(GetLocalAddress(*peer_teredo) == addr_ipv4);
1004+
BOOST_CHECK(GetLocalAddress(*peer_onion) == addr_onion);
1005+
BOOST_CHECK(GetLocalAddress(*peer_i2p) == addr_i2p);
1006+
BOOST_CHECK(GetLocalAddress(*peer_cjdns) == addr_cjdns);
1007+
RemoveLocal(addr_ipv4);
1008+
RemoveLocal(addr_ipv6);
1009+
RemoveLocal(addr_ipv6_tunnel);
1010+
RemoveLocal(addr_teredo);
1011+
RemoveLocal(addr_onion);
1012+
RemoveLocal(addr_i2p);
1013+
RemoveLocal(addr_cjdns);
1014+
}
1015+
9111016
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)