aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGleb Naumenko <naumenko.gs@gmail.com>2020-05-16 21:05:44 -0400
committerGleb Naumenko <naumenko.gs@gmail.com>2020-07-30 14:38:48 +0300
commitacd6135b43941fa51d52f5fcdb2ce944280ad01e (patch)
tree4d49cad92bd7a620e2b48fee2ad5b29201aade63
parent7cc0e8101f01891aa8be093a00d993bb7579c385 (diff)
downloadbitcoin-acd6135b43941fa51d52f5fcdb2ce944280ad01e.tar.xz
Cache responses to addr requests
Prevents a spy from scraping victim's AddrMan by reconnecting and re-requesting addrs.
-rw-r--r--src/net.cpp11
-rw-r--r--src/net.h31
-rw-r--r--src/net_processing.cpp2
3 files changed, 43 insertions, 1 deletions
diff --git a/src/net.cpp b/src/net.cpp
index 171358bb5f..bf29d928a1 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -2539,6 +2539,17 @@ std::vector<CAddress> CConnman::GetAddresses()
return addresses;
}
+std::vector<CAddress> CConnman::GetAddresses(Network requestor_network)
+{
+ const auto current_time = GetTime<std::chrono::microseconds>();
+ if (m_addr_response_caches.find(requestor_network) == m_addr_response_caches.end() ||
+ m_addr_response_caches[requestor_network].m_update_addr_response < current_time) {
+ m_addr_response_caches[requestor_network].m_addrs_response_cache = GetAddresses();
+ m_addr_response_caches[requestor_network].m_update_addr_response = current_time + std::chrono::hours(21) + GetRandMillis(std::chrono::hours(6));
+ }
+ return m_addr_response_caches[requestor_network].m_addrs_response_cache;
+}
+
bool CConnman::AddNode(const std::string& strNode)
{
LOCK(cs_vAddedNodes);
diff --git a/src/net.h b/src/net.h
index 3492a784cc..1b9ed9dff4 100644
--- a/src/net.h
+++ b/src/net.h
@@ -27,6 +27,7 @@
#include <atomic>
#include <cstdint>
#include <deque>
+#include <map>
#include <thread>
#include <memory>
#include <condition_variable>
@@ -254,6 +255,13 @@ public:
void MarkAddressGood(const CAddress& addr);
void AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty = 0);
std::vector<CAddress> GetAddresses();
+ /**
+ * Cache is used to minimize topology leaks, so it should
+ * be used for all non-trusted calls, for example, p2p.
+ * A non-malicious call (from RPC) should
+ * call the function without a parameter to avoid using the cache.
+ */
+ std::vector<CAddress> GetAddresses(Network requestor_network);
// This allows temporarily exceeding m_max_outbound_full_relay, with the goal of finding
// a peer that is better than all our current peers.
@@ -419,6 +427,29 @@ private:
unsigned int nPrevNodeCount{0};
/**
+ * Cache responses to addr requests to minimize privacy leak.
+ * Attack example: scraping addrs in real-time may allow an attacker
+ * to infer new connections of the victim by detecting new records
+ * with fresh timestamps (per self-announcement).
+ */
+ struct CachedAddrResponse {
+ std::vector<CAddress> m_addrs_response_cache;
+ std::chrono::microseconds m_update_addr_response{0};
+ };
+
+ /**
+ * Addr responses stored in different caches
+ * per network prevent cross-network node identification.
+ * If a node for example is multi-homed under Tor and IPv6,
+ * a single cache (or no cache at all) would let an attacker
+ * to easily detect that it is the same node by comparing responses.
+ * The used memory equals to 1000 CAddress records (or around 32 bytes) per
+ * distinct Network (up to 5) we have/had an inbound peer from,
+ * resulting in at most ~160 KB.
+ */
+ std::map<Network, CachedAddrResponse> m_addr_response_caches;
+
+ /**
* Services this instance offers.
*
* This data is replicated in each CNode instance we create during peer
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index a591836f2e..a9f6ed960f 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -3477,7 +3477,7 @@ void ProcessMessage(
pfrom.fSentAddr = true;
pfrom.vAddrToSend.clear();
- std::vector<CAddress> vAddr = connman.GetAddresses();
+ std::vector<CAddress> vAddr = connman.GetAddresses(pfrom.addr.GetNetwork());
FastRandomContext insecure_rand;
for (const CAddress &addr : vAddr) {
pfrom.PushAddress(addr, insecure_rand);